1
0
Fork 0
mirror of synced 2024-07-01 12:40:34 +12:00

Merge branch 'feat-database-indexing' into feat-custom-id

This commit is contained in:
Damodar Lohani 2021-07-19 12:06:18 +05:45
commit 7c99192ced
33 changed files with 2088 additions and 1052 deletions

View file

@ -212,6 +212,7 @@ RUN chmod +x /usr/local/bin/doctor && \
chmod +x /usr/local/bin/vars && \ chmod +x /usr/local/bin/vars && \
chmod +x /usr/local/bin/worker-audits && \ chmod +x /usr/local/bin/worker-audits && \
chmod +x /usr/local/bin/worker-certificates && \ chmod +x /usr/local/bin/worker-certificates && \
chmod +x /usr/local/bin/worker-database && \
chmod +x /usr/local/bin/worker-deletes && \ chmod +x /usr/local/bin/worker-deletes && \
chmod +x /usr/local/bin/worker-functions && \ chmod +x /usr/local/bin/worker-functions && \
chmod +x /usr/local/bin/worker-mails && \ chmod +x /usr/local/bin/worker-mails && \

View file

@ -19,6 +19,7 @@ $collections = [
'size' => 128, 'size' => 128,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -29,6 +30,7 @@ $collections = [
'size' => 256, 'size' => 256,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -39,6 +41,7 @@ $collections = [
'size' => Database::LENGTH_KEY, 'size' => Database::LENGTH_KEY,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -49,6 +52,7 @@ $collections = [
'size' => Database::LENGTH_KEY, 'size' => Database::LENGTH_KEY,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -59,6 +63,7 @@ $collections = [
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -69,6 +74,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -79,6 +85,7 @@ $collections = [
'size' => 256, 'size' => 256,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -89,6 +96,7 @@ $collections = [
'size' => 256, 'size' => 256,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -99,6 +107,7 @@ $collections = [
'size' => 256, 'size' => 256,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -109,6 +118,7 @@ $collections = [
'size' => 256, 'size' => 256,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -119,6 +129,7 @@ $collections = [
'size' => 256, 'size' => 256,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -129,6 +140,7 @@ $collections = [
'size' => 256, 'size' => 256,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -139,6 +151,7 @@ $collections = [
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => true, 'array' => true,
'filters' => ['json'], 'filters' => ['json'],
], ],
@ -149,6 +162,7 @@ $collections = [
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => true, 'array' => true,
'filters' => ['json'], 'filters' => ['json'],
], ],
@ -159,6 +173,7 @@ $collections = [
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => true, 'array' => true,
'filters' => ['json'], 'filters' => ['json'],
], ],
@ -169,6 +184,7 @@ $collections = [
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => true, 'array' => true,
'filters' => ['json'], 'filters' => ['json'],
], ],
@ -179,6 +195,7 @@ $collections = [
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => true, 'array' => true,
'filters' => ['json'], 'filters' => ['json'],
], ],
@ -206,6 +223,7 @@ $collections = [
'size' => 256, 'size' => 256,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -216,6 +234,7 @@ $collections = [
'size' => 1024, 'size' => 1024,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -226,6 +245,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -236,6 +256,7 @@ $collections = [
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -246,6 +267,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -256,6 +278,7 @@ $collections = [
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => ['json'] 'filters' => ['json']
], ],
@ -266,6 +289,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -276,6 +300,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -286,6 +311,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -296,6 +322,7 @@ $collections = [
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => true, 'array' => true,
'filters' => ['json'], 'filters' => ['json'],
], ],
@ -306,6 +333,7 @@ $collections = [
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => true, 'array' => true,
'filters' => ['json'], 'filters' => ['json'],
], ],
@ -316,6 +344,7 @@ $collections = [
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => true, 'array' => true,
'filters' => ['json'], 'filters' => ['json'],
], ],
@ -343,6 +372,7 @@ $collections = [
'size' => 128, 'size' => 128,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -353,6 +383,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -363,6 +394,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -390,6 +422,7 @@ $collections = [
'size' => Database::LENGTH_KEY, 'size' => Database::LENGTH_KEY,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -400,6 +433,7 @@ $collections = [
'size' => Database::LENGTH_KEY, 'size' => Database::LENGTH_KEY,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -410,6 +444,7 @@ $collections = [
'size' => 128, 'size' => 128,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => true, 'array' => true,
'filters' => [], 'filters' => [],
], ],
@ -420,6 +455,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -430,6 +466,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -440,6 +477,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -450,6 +488,7 @@ $collections = [
'size' => 256, 'size' => 256,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -491,6 +530,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -502,6 +542,7 @@ $collections = [
'size' => Database::LENGTH_KEY, 'size' => Database::LENGTH_KEY,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -512,6 +553,7 @@ $collections = [
'size' => 2048, 'size' => 2048,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -522,6 +564,7 @@ $collections = [
'size' => 2048, 'size' => 2048,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -532,6 +575,7 @@ $collections = [
'size' => 2048, 'size' => 2048,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -542,6 +586,7 @@ $collections = [
'size' => 127, // https://tools.ietf.org/html/rfc4288#section-4.2 'size' => 127, // https://tools.ietf.org/html/rfc4288#section-4.2
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -552,6 +597,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -562,6 +608,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -572,6 +619,7 @@ $collections = [
'size' => 255, 'size' => 255,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -582,6 +630,7 @@ $collections = [
'size' => 2048, 'size' => 2048,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -592,6 +641,7 @@ $collections = [
'size' => 64, 'size' => 64,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -602,6 +652,7 @@ $collections = [
'size' => 64, 'size' => 64,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -612,6 +663,7 @@ $collections = [
'size' => 2048, 'size' => 2048,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -622,6 +674,7 @@ $collections = [
'size' => 2048, 'size' => 2048,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -656,6 +709,7 @@ $collections = [
'size' => 128, 'size' => 128,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => true, 'array' => true,
'filters' => [], 'filters' => [],
], ],
@ -666,6 +720,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -676,6 +731,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -687,6 +743,7 @@ $collections = [
'size' => Database::LENGTH_KEY, 'size' => Database::LENGTH_KEY,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -697,6 +754,7 @@ $collections = [
'size' => 2048, 'size' => 2048,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -707,6 +765,7 @@ $collections = [
'size' => 2048, 'size' => 2048,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -717,6 +776,7 @@ $collections = [
'size' => Database::LENGTH_KEY, 'size' => Database::LENGTH_KEY,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -727,6 +787,7 @@ $collections = [
'size' => 8192, 'size' => 8192,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => ['json', 'encrypt'], 'filters' => ['json', 'encrypt'],
], ],
@ -737,6 +798,7 @@ $collections = [
'size' => 256, 'size' => 256,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => true, 'array' => true,
'filters' => [], 'filters' => [],
], ],
@ -747,6 +809,7 @@ $collections = [
'size' => 128, 'size' => 128,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -757,6 +820,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -767,6 +831,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -777,6 +842,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -804,6 +870,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -814,6 +881,7 @@ $collections = [
'size' => Database::LENGTH_KEY, 'size' => Database::LENGTH_KEY,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -825,6 +893,7 @@ $collections = [
'size' => 2048, 'size' => 2048,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -835,6 +904,7 @@ $collections = [
'size' => 2048, 'size' => 2048,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -845,6 +915,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -872,6 +943,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -882,6 +954,7 @@ $collections = [
'size' => Database::LENGTH_KEY, 'size' => Database::LENGTH_KEY,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -892,6 +965,7 @@ $collections = [
'size' => Database::LENGTH_KEY, 'size' => Database::LENGTH_KEY,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -903,6 +977,7 @@ $collections = [
'size' => 128, 'size' => 128,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -913,6 +988,7 @@ $collections = [
'size' => 128, 'size' => 128,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -923,6 +999,7 @@ $collections = [
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -933,6 +1010,7 @@ $collections = [
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -943,6 +1021,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -953,6 +1032,7 @@ $collections = [
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
], ],
@ -967,6 +1047,91 @@ $collections = [
] ]
], ],
], ],
'certificates' => [
'$collection' => Database::COLLECTIONS,
'$id' => 'certificates',
'name' => 'Certificates',
'attributes' => [
[
'$id' => 'domain',
'type' => Database::VAR_STRING,
'format' => '',
// The maximum total length of a domain name or number is 255 characters.
// https://datatracker.ietf.org/doc/html/rfc2821#section-4.5.3.1
// https://datatracker.ietf.org/doc/html/rfc5321#section-4.5.3.1.2
'size' => 255,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'issueDate',
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'renewDate',
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'attempts',
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'log',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'updated',
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => '_key_domain',
'type' => Database::INDEX_KEY,
'attributes' => ['domain'],
'lengths' => [255],
'orders' => [Database::ORDER_ASC],
],
],
],
]; ];
/* /*
@ -984,6 +1149,7 @@ foreach ($providers as $index => $provider) {
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
]; ];
@ -995,6 +1161,7 @@ foreach ($providers as $index => $provider) {
'size' => 16384, 'size' => 16384,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
]; ];
@ -1008,6 +1175,7 @@ foreach ($auth as $index => $method) {
'size' => 0, 'size' => 0,
'signed' => true, 'signed' => true,
'required' => false, 'required' => false,
'default' => null,
'array' => false, 'array' => false,
'filters' => [], 'filters' => [],
]; ];

View file

@ -82,6 +82,26 @@ return [
'model' => Response::MODEL_COLLECTION, 'model' => Response::MODEL_COLLECTION,
'note' => '', 'note' => '',
], ],
'database.attributes.create' => [
'description' => 'This event triggers when a collection attribute is created.',
'model' => Response::MODEL_ATTRIBUTE,
'note' => '',
],
'database.attributes.delete' => [
'description' => 'This event triggers when a collection attribute is deleted.',
'model' => Response::MODEL_ATTRIBUTE,
'note' => '',
],
'database.indexes.create' => [
'description' => 'This event triggers when a collection index is created.',
'model' => Response::MODEL_INDEX,
'note' => '',
],
'database.indexes.delete' => [
'description' => 'This event triggers when a collection index is deleted.',
'model' => Response::MODEL_INDEX,
'note' => '',
],
'database.documents.create' => [ 'database.documents.create' => [
'description' => 'This event triggers when a database document is created.', 'description' => 'This event triggers when a database document is created.',
'model' => Response::MODEL_DOCUMENT, 'model' => Response::MODEL_DOCUMENT,

View file

@ -32,6 +32,10 @@ $admins = [
'users.write', 'users.write',
'collections.read', 'collections.read',
'collections.write', 'collections.write',
'attributes.read',
'attributes.write',
'indexes.read',
'indexes.write',
'platforms.read', 'platforms.read',
'platforms.write', 'platforms.write',
'keys.read', 'keys.read',

View file

@ -19,6 +19,18 @@ return [ // List of publicly visible scopes
'collections.write' => [ 'collections.write' => [
'description' => 'Access to create, update, and delete your project\'s database collections', 'description' => 'Access to create, update, and delete your project\'s database collections',
], ],
'attributes.read' => [
'description' => 'Access to read your project\'s database collection\'s attributes',
],
'attributes.write' => [
'description' => 'Access to create, update, and delete your project\'s database collection\'s attributes',
],
'indexes.read' => [
'description' => 'Access to read your project\'s database collection\'s indexes',
],
'indexes.write' => [
'description' => 'Access to create, update, and delete your project\'s database collection\'s indexes',
],
'documents.read' => [ 'documents.read' => [
'description' => 'Access to read your project\'s database documents', 'description' => 'Access to read your project\'s database documents',
], ],

View file

@ -858,7 +858,6 @@ App::get('/v1/account/logs')
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForInternal */
$audit = new Audit($dbForInternal); $audit = new Audit($dbForInternal);
$countries = $locale->getText('countries'); $countries = $locale->getText('countries');
$logs = $audit->getLogsByUserAndEvents($user->getId(), [ $logs = $audit->getLogsByUserAndEvents($user->getId(), [

File diff suppressed because it is too large Load diff

View file

@ -1006,6 +1006,7 @@ App::post('/v1/projects/:projectId/tasks')
$task = new Document([ $task = new Document([
'$id' => $dbForConsole->getId(), '$id' => $dbForConsole->getId(),
'projectId' => $project->getId(),
'name' => $name, 'name' => $name,
'status' => $status, 'status' => $status,
'schedule' => $schedule, 'schedule' => $schedule,

View file

@ -224,8 +224,7 @@ App::get('/v1/users/:userId/logs')
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForInternal */
/** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */ /** @var MaxMind\Db\Reader $geodb */
/** @var Utopia\App $app */
$user = $dbForInternal->getDocument('users', $userId); $user = $dbForInternal->getDocument('users', $userId);
if ($user->isEmpty()) { if ($user->isEmpty()) {

View file

@ -16,17 +16,19 @@ use Appwrite\Utopia\Response\Filters\V06;
use Appwrite\Utopia\Response\Filters\V07; use Appwrite\Utopia\Response\Filters\V07;
use Appwrite\Utopia\Response\Filters\V08; use Appwrite\Utopia\Response\Filters\V08;
use Utopia\CLI\Console; use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\Document; use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization as Authorization2; use Utopia\Database\Validator\Authorization as Authorization2;
Config::setParam('domainVerification', false); Config::setParam('domainVerification', false);
Config::setParam('cookieDomain', 'localhost'); Config::setParam('cookieDomain', 'localhost');
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE); Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
App::init(function ($utopia, $request, $response, $console, $project, $consoleDB, $user, $locale, $clients) { App::init(function ($utopia, $request, $response, $console, $project, $dbForConsole, $user, $locale, $clients) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $consoleDB */ /** @var Utopia\Database\Database $dbForConsole */
/** @var Utopia\Database\Document $console */ /** @var Utopia\Database\Document $console */
/** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Document $user */
@ -42,36 +44,29 @@ App::init(function ($utopia, $request, $response, $console, $project, $consoleDB
$domains[$domain->get()] = false; $domains[$domain->get()] = false;
Console::warning($domain->get() . ' is not a publicly accessible domain. Skipping SSL certificate generation.'); Console::warning($domain->get() . ' is not a publicly accessible domain. Skipping SSL certificate generation.');
} else { } else {
Authorization::disable(); Authorization2::disable();
$dbDomain = $consoleDB->getCollectionFirst([
'limit' => 1,
'offset' => 0,
'filters' => [
'$collection=' . Database::SYSTEM_COLLECTION_CERTIFICATES,
'domain=' . $domain->get(),
],
]);
if (empty($dbDomain)) { $certificate = $dbForConsole->findFirst('certificates', [
$dbDomain = [ new Query('domain', QUERY::TYPE_EQUAL, [$domain->get()])
'$collection' => Database::SYSTEM_COLLECTION_CERTIFICATES, ], /*limit*/ 1);
'$permissions' => [
'read' => [], if (empty($certificate)) {
'write' => [], $certificate = new Document([
],
'domain' => $domain->get(), 'domain' => $domain->get(),
]; ]);
$dbDomain = $consoleDB->createDocument($dbDomain); $certificate = $dbForConsole->createDocument('certificates', $certificate);
Authorization::enable(); Authorization2::enable();
Console::info('Issuing a TLS certificate for the master domain (' . $domain->get() . ') in a few seconds...'); // TODO move this to installation script Console::info('Issuing a TLS certificate for the master domain (' . $domain->get() . ') in a few seconds...'); // TODO move this to installation script
Resque::enqueue('v1-certificates', 'CertificatesV1', [ Resque::enqueue('v1-certificates', 'CertificatesV1', [
'document' => $dbDomain, 'document' => $certificate,
'domain' => $domain->get(), 'domain' => $domain->get(),
'validateTarget' => false, 'validateTarget' => false,
'validateCNAME' => false, 'validateCNAME' => false,
]); ]);
} else {
Authorization2::enable(); // ensure authorization is reenabled
} }
$domains[$domain->get()] = true; $domains[$domain->get()] = true;
} }
@ -286,7 +281,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $consoleDB
throw new Exception('Password reset is required', 412); throw new Exception('Password reset is required', 412);
} }
}, ['utopia', 'request', 'response', 'console', 'project', 'consoleDB', 'user', 'locale', 'clients']); }, ['utopia', 'request', 'response', 'console', 'project', 'dbForConsole', 'user', 'locale', 'clients']);
App::options(function ($request, $response) { App::options(function ($request, $response) {
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */

View file

@ -9,7 +9,7 @@ use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\Storage\Device\Local; use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage; use Utopia\Storage\Storage;
App::init(function ($utopia, $request, $response, $project, $user, $register, $events, $audits, $usage, $deletes, $dbForInternal) { App::init(function ($utopia, $request, $response, $project, $user, $register, $events, $audits, $usage, $deletes, $database, $dbForInternal) {
/** @var Utopia\App $utopia */ /** @var Utopia\App $utopia */
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
@ -20,6 +20,7 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $usage */ /** @var Appwrite\Event\Event $usage */
/** @var Appwrite\Event\Event $deletes */ /** @var Appwrite\Event\Event $deletes */
/** @var Appwrite\Event\Event $database */
/** @var Appwrite\Event\Event $functions */ /** @var Appwrite\Event\Event $functions */
/** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Database\Database $dbForInternal */
@ -36,7 +37,6 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
* Abuse Check * Abuse Check
*/ */
$timeLimit = new TimeLimit($route->getLabel('abuse-key', 'url:{url},ip:{ip}'), $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForInternal); $timeLimit = new TimeLimit($route->getLabel('abuse-key', 'url:{url},ip:{ip}'), $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForInternal);
$timeLimit $timeLimit
->setParam('{userId}', $user->getId()) ->setParam('{userId}', $user->getId())
->setParam('{userAgent}', $request->getUserAgent('')) ->setParam('{userAgent}', $request->getUserAgent(''))
@ -110,7 +110,11 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
->setParam('projectId', $project->getId()) ->setParam('projectId', $project->getId())
; ;
}, ['utopia', 'request', 'response', 'project', 'user', 'register', 'events', 'audits', 'usage', 'deletes', 'dbForInternal'], 'api'); $database
->setParam('projectId', $project->getId())
;
}, ['utopia', 'request', 'response', 'project', 'user', 'register', 'events', 'audits', 'usage', 'deletes', 'database', 'dbForInternal'], 'api');
App::init(function ($utopia, $request, $response, $project, $user) { App::init(function ($utopia, $request, $response, $project, $user) {
/** @var Utopia\App $utopia */ /** @var Utopia\App $utopia */
@ -166,7 +170,7 @@ App::init(function ($utopia, $request, $response, $project, $user) {
}, ['utopia', 'request', 'response', 'project', 'user'], 'auth'); }, ['utopia', 'request', 'response', 'project', 'user'], 'auth');
App::shutdown(function ($utopia, $request, $response, $project, $events, $audits, $usage, $deletes, $mode) { App::shutdown(function ($utopia, $request, $response, $project, $events, $audits, $usage, $deletes, $database, $mode) {
/** @var Utopia\App $utopia */ /** @var Utopia\App $utopia */
/** @var Utopia\Swoole\Request $request */ /** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Utopia\Response $response */
@ -175,6 +179,7 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
/** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $usage */ /** @var Appwrite\Event\Event $usage */
/** @var Appwrite\Event\Event $deletes */ /** @var Appwrite\Event\Event $deletes */
/** @var Appwrite\Event\Event $database */
/** @var Appwrite\Event\Event $functions */ /** @var Appwrite\Event\Event $functions */
/** @var bool $mode */ /** @var bool $mode */
@ -204,6 +209,10 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
if (!empty($deletes->getParam('type')) && !empty($deletes->getParam('document'))) { if (!empty($deletes->getParam('type')) && !empty($deletes->getParam('document'))) {
$deletes->trigger(); $deletes->trigger();
} }
if (!empty($database->getParam('type')) && !empty($database->getParam('document'))) {
$database->trigger();
}
$route = $utopia->match($request); $route = $utopia->match($request);
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled' if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled'
@ -218,4 +227,4 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
; ;
} }
}, ['utopia', 'request', 'response', 'project', 'events', 'audits', 'usage', 'deletes', 'mode'], 'api'); }, ['utopia', 'request', 'response', 'project', 'events', 'audits', 'usage', 'deletes', 'database', 'mode'], 'api');

View file

@ -54,14 +54,6 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
$app = new App('UTC'); $app = new App('UTC');
go(function() use ($register, $app) { go(function() use ($register, $app) {
// Only retry connection once before throwing exception
try {
$db = $register->get('dbPool')->get();
} catch (\Exception $exception) {
Console::warning('[Setup] - Database not ready. Waiting for five seconds...');
sleep(5);
}
$db = $register->get('dbPool')->get(); $db = $register->get('dbPool')->get();
$redis = $register->get('redisPool')->get(); $redis = $register->get('redisPool')->get();
@ -77,6 +69,9 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
return $app; return $app;
}); });
// wait for database to be ready
sleep(5);
$dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */ $dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */
if(!$dbForConsole->exists()) { if(!$dbForConsole->exists()) {

View file

@ -72,7 +72,12 @@ const APP_SOCIAL_DISCORD = 'https://appwrite.io/discord';
const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244'; const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244';
const APP_SOCIAL_DEV = 'https://dev.to/appwrite'; const APP_SOCIAL_DEV = 'https://dev.to/appwrite';
const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite'; const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
// Creation Types
const CREATE_TYPE_ATTRIBUTE = 'newAttribute';
const CREATE_TYPE_INDEX = 'newIndex';
// Deletion Types // Deletion Types
const DELETE_TYPE_ATTRIBUTE = 'attribute';
const DELETE_TYPE_INDEX = 'index';
const DELETE_TYPE_DOCUMENT = 'document'; const DELETE_TYPE_DOCUMENT = 'document';
const DELETE_TYPE_EXECUTIONS = 'executions'; const DELETE_TYPE_EXECUTIONS = 'executions';
const DELETE_TYPE_AUDIT = 'audit'; const DELETE_TYPE_AUDIT = 'audit';
@ -386,6 +391,10 @@ App::setResource('deletes', function($register) {
return new Event(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME); return new Event(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME);
}, ['register']); }, ['register']);
App::setResource('database', function($register) {
return new Event(Event::DATABASE_QUEUE_NAME, Event::DATABASE_CLASS_NAME);
}, ['register']);
// Test Mock // Test Mock
App::setResource('clients', function($request, $console, $project) { App::setResource('clients', function($request, $console, $project) {
$console->setAttribute('platforms', [ // Allways allow current host $console->setAttribute('platforms', [ // Allways allow current host

View file

@ -1,14 +1,16 @@
<?php <?php
use Appwrite\Database\Database;
use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
use Appwrite\Database\Adapter\Redis as RedisAdapter;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Network\Validator\CNAME; use Appwrite\Network\Validator\CNAME;
use Appwrite\Resque\Worker; use Appwrite\Resque\Worker;
use Utopia\App; use Utopia\App;
use Utopia\Cache\Adapter\Redis;
use Utopia\Cache\Cache;
use Utopia\CLI\Console; use Utopia\CLI\Console;
use Utopia\Config\Config; use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Validator\Authorization;
use Utopia\Domains\Domain; use Utopia\Domains\Domain;
require_once __DIR__.'/../workers.php'; require_once __DIR__.'/../workers.php';
@ -28,183 +30,178 @@ class CertificatesV1 extends Worker
{ {
global $register; global $register;
$db = $register->get('db'); go(function() use ($register) {
$cache = $register->get('cache'); $db = $register->get('dbPool')->get();
$redis = $register->get('redisPool')->get();
$consoleDB = new Database(); $cache = new Cache(new Redis($redis));
$consoleDB->setAdapter(new RedisAdapter(new MySQLAdapter($db, $cache), $cache)); $dbForConsole = new Database(new MariaDB($db), $cache);
$consoleDB->setNamespace('app_console'); // Main DB $dbForConsole->setNamespace('project_console_internal');
$consoleDB->setMocks(Config::getParam('collections', []));
/** /**
* 1. Get new domain document - DONE * 1. Get new domain document - DONE
* 1.1. Validate domain is valid, public suffix is known and CNAME records are verified - DONE * 1.1. Validate domain is valid, public suffix is known and CNAME records are verified - DONE
* 2. Check if a certificate already exists - DONE * 2. Check if a certificate already exists - DONE
* 3. Check if certificate is about to expire, if not - skip it * 3. Check if certificate is about to expire, if not - skip it
* 3.1. Create / renew certificate * 3.1. Create / renew certificate
* 3.2. Update loadblancer * 3.2. Update loadblancer
* 3.3. Update database (domains, change date, expiry) * 3.3. Update database (domains, change date, expiry)
* 3.4. Set retry on failure * 3.4. Set retry on failure
* 3.5. Schedule to renew certificate in 60 days * 3.5. Schedule to renew certificate in 60 days
*/ */
Authorization::disable(); Authorization::disable();
// Args // Args
$document = $this->args['document']; $document = $this->args['document'];
$domain = $this->args['domain']; $domain = $this->args['domain'];
// Validation Args // Validation Args
$validateTarget = $this->args['validateTarget'] ?? true; $validateTarget = $this->args['validateTarget'] ?? true;
$validateCNAME = $this->args['validateCNAME'] ?? true; $validateCNAME = $this->args['validateCNAME'] ?? true;
// Options
$domain = new Domain((!empty($domain)) ? $domain : '');
$expiry = 60 * 60 * 24 * 30 * 2; // 60 days
$safety = 60 * 60; // 1 hour
$renew = (\time() + $expiry);
if(empty($domain->get())) {
throw new Exception('Missing domain');
}
if(!$domain->isKnown() || $domain->isTest()) {
throw new Exception('Unknown public suffix for domain');
}
if($validateTarget) {
$target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', ''));
// Options if(!$target->isKnown() || $target->isTest()) {
$domain = new Domain((!empty($domain)) ? $domain : ''); throw new Exception('Unreachable CNAME target ('.$target->get().'), please use a domain with a public suffix.');
$expiry = 60 * 60 * 24 * 30 * 2; // 60 days }
$safety = 60 * 60; // 1 hour
$renew = (\time() + $expiry);
if(empty($domain->get())) {
throw new Exception('Missing domain');
}
if(!$domain->isKnown() || $domain->isTest()) {
throw new Exception('Unknown public suffix for domain');
}
if($validateTarget) {
$target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', ''));
if(!$target->isKnown() || $target->isTest()) {
throw new Exception('Unreachable CNAME target ('.$target->get().'), please use a domain with a public suffix.');
} }
}
if($validateCNAME) { if($validateCNAME) {
$validator = new CNAME($target->get()); // Verify Domain with DNS records $validator = new CNAME($target->get()); // Verify Domain with DNS records
if(!$validator->isValid($domain->get())) {
throw new Exception('Failed to verify domain DNS records');
}
}
$certificate = $consoleDB->getCollectionFirst([
'limit' => 1,
'offset' => 0,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_CERTIFICATES,
'domain='.$domain->get(),
],
]);
// $condition = ($certificate
// && $certificate instanceof Document
// && isset($certificate['issueDate'])
// && (($certificate['issueDate'] + ($expiry)) > time())) ? 'true' : 'false';
// throw new Exception('cert issued at'.date('d.m.Y H:i', $certificate['issueDate']).' | renew date is: '.date('d.m.Y H:i', ($certificate['issueDate'] + ($expiry))).' | condition is '.$condition);
$certificate = (!empty($certificate) && $certificate instanceof $certificate) ? $certificate->getArrayCopy() : [];
if(!empty($certificate)
&& isset($certificate['issueDate'])
&& (($certificate['issueDate'] + ($expiry)) > \time())) { // Check last issue time
throw new Exception('Renew isn\'t required');
}
$staging = (App::isProduction()) ? '' : ' --dry-run';
$email = App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS');
if(empty($email)) {
throw new Exception('You must set a valid security email address (_APP_SYSTEM_SECURITY_EMAIL_ADDRESS) to issue an SSL certificate');
}
$stdout = '';
$stderr = '';
$exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}"
." --email ".$email
." -w ".APP_STORAGE_CERTIFICATES
." -d {$domain->get()}", '', $stdout, $stderr);
if($exit !== 0) {
throw new Exception('Failed to issue a certificate with message: '.$stderr);
}
$path = APP_STORAGE_CERTIFICATES.'/'.$domain->get();
if(!\is_readable($path)) {
if (!\mkdir($path, 0755, true)) {
throw new Exception('Failed to create path...');
}
}
if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/cert.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/cert.pem')) { if(!$validator->isValid($domain->get())) {
throw new Exception('Failed to rename certificate cert.pem: '.\json_encode($stdout)); throw new Exception('Failed to verify domain DNS records');
} }
if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/chain.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/chain.pem')) {
throw new Exception('Failed to rename certificate chain.pem: '.\json_encode($stdout));
}
if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/fullchain.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/fullchain.pem')) {
throw new Exception('Failed to rename certificate fullchain.pem: '.\json_encode($stdout));
}
if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/privkey.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/privkey.pem')) {
throw new Exception('Failed to rename certificate privkey.pem: '.\json_encode($stdout));
}
$certificate = \array_merge($certificate, [
'$collection' => Database::SYSTEM_COLLECTION_CERTIFICATES,
'$permissions' => [
'read' => [],
'write' => [],
],
'domain' => $domain->get(),
'issueDate' => \time(),
'renewDate' => $renew,
'attempts' => 0,
'log' => \json_encode($stdout),
]);
$certificate = $consoleDB->createDocument($certificate);
if(!$certificate) {
throw new Exception('Failed saving certificate to DB');
}
if(!empty($document)) {
$document = \array_merge($document, [
'updated' => \time(),
'certificateId' => $certificate->getId(),
]);
$document = $consoleDB->updateDocument($document);
if(!$document) {
throw new Exception('Failed saving domain to DB');
} }
}
$certificate = $dbForConsole->findFirst('certificates', [
new Query('domain', QUERY::TYPE_EQUAL, [$domain->get()])
], /*limit*/ 1);
// $condition = ($certificate
// && $certificate instanceof Document
// && isset($certificate['issueDate'])
// && (($certificate['issueDate'] + ($expiry)) > time())) ? 'true' : 'false';
// throw new Exception('cert issued at'.date('d.m.Y H:i', $certificate['issueDate']).' | renew date is: '.date('d.m.Y H:i', ($certificate['issueDate'] + ($expiry))).' | condition is '.$condition);
$certificate = (!empty($certificate) && $certificate instanceof $certificate) ? $certificate->getArrayCopy() : [];
if(!empty($certificate)
&& isset($certificate['issueDate'])
&& (($certificate['issueDate'] + ($expiry)) > \time())) { // Check last issue time
throw new Exception('Renew isn\'t required');
}
$staging = (App::isProduction()) ? '' : ' --dry-run';
$email = App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS');
if(empty($email)) {
throw new Exception('You must set a valid security email address (_APP_SYSTEM_SECURITY_EMAIL_ADDRESS) to issue an SSL certificate');
}
$stdout = '';
$stderr = '';
$exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}"
." --email ".$email
." -w ".APP_STORAGE_CERTIFICATES
." -d {$domain->get()}", '', $stdout, $stderr);
if($exit !== 0) {
throw new Exception('Failed to issue a certificate with message: '.$stderr);
}
$path = APP_STORAGE_CERTIFICATES.'/'.$domain->get();
if(!\is_readable($path)) {
if (!\mkdir($path, 0755, true)) {
throw new Exception('Failed to create path...');
}
}
if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/cert.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/cert.pem')) {
throw new Exception('Failed to rename certificate cert.pem: '.\json_encode($stdout));
}
if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/chain.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/chain.pem')) {
throw new Exception('Failed to rename certificate chain.pem: '.\json_encode($stdout));
}
if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/fullchain.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/fullchain.pem')) {
throw new Exception('Failed to rename certificate fullchain.pem: '.\json_encode($stdout));
}
if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/privkey.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/privkey.pem')) {
throw new Exception('Failed to rename certificate privkey.pem: '.\json_encode($stdout));
}
$certificate = new Document(\array_merge($certificate, [
'domain' => $domain->get(),
'issueDate' => \time(),
'renewDate' => $renew,
'attempts' => 0,
'log' => \json_encode($stdout),
]));
$certificate = $dbForConsole->createDocument('certificates', $certificate);
if(!$certificate) {
throw new Exception('Failed saving certificate to DB');
}
if(!empty($document)) {
$certificate = new Document(\array_merge($document, [
'updated' => \time(),
'certificateId' => $certificate->getId(),
]));
$config = $certificate = $dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate);
"tls:
certificates: if(!$certificate) {
- certFile: /storage/certificates/{$domain->get()}/fullchain.pem throw new Exception('Failed saving domain to DB');
keyFile: /storage/certificates/{$domain->get()}/privkey.pem"; }
}
$config =
"tls:
certificates:
- certFile: /storage/certificates/{$domain->get()}/fullchain.pem
keyFile: /storage/certificates/{$domain->get()}/privkey.pem";
if(!\file_put_contents(APP_STORAGE_CONFIG.'/'.$domain->get().'.yml', $config)) { if(!\file_put_contents(APP_STORAGE_CONFIG.'/'.$domain->get().'.yml', $config)) {
throw new Exception('Failed to save SSL configuration'); throw new Exception('Failed to save SSL configuration');
} }
ResqueScheduler::enqueueAt($renew + $safety, 'v1-certificates', 'CertificatesV1', [ ResqueScheduler::enqueueAt($renew + $safety, 'v1-certificates', 'CertificatesV1', [
'document' => [], 'document' => [],
'domain' => $domain->get(), 'domain' => $domain->get(),
'validateTarget' => $validateTarget, 'validateTarget' => $validateTarget,
'validateCNAME' => $validateCNAME, 'validateCNAME' => $validateCNAME,
]); // Async task rescheduale ]); // Async task rescheduale
Authorization::reset(); Authorization::reset();
// Return db connections to pool
$register->get('dbPool')->put($db);
$register->get('redisPool')->put($redis);
});
} }
public function shutdown(): void public function shutdown(): void

183
app/workers/database.php Normal file
View file

@ -0,0 +1,183 @@
<?php
use Appwrite\Resque\Worker;
use Utopia\Cache\Cache;
use Utopia\Cache\Adapter\Redis as RedisCache;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Adapter\MariaDB;
require_once __DIR__.'/../init.php';
Console::title('Database V1 Worker');
Console::success(APP_NAME.' database worker v1 has started'."\n");
class DatabaseV1 extends Worker
{
public $args = [];
public function init(): void
{
}
public function run(): void
{
$projectId = $this->args['projectId'] ?? '';
$type = $this->args['type'] ?? '';
switch (strval($type)) {
case CREATE_TYPE_ATTRIBUTE:
$attribute = $this->args['document'] ?? '';
$attribute = new Document($attribute);
$this->createAttribute($attribute, $projectId);
break;
case DELETE_TYPE_ATTRIBUTE:
$attribute = $this->args['document'] ?? '';
$attribute = new Document($attribute);
$this->deleteAttribute($attribute, $projectId);
break;
case CREATE_TYPE_INDEX:
$index = $this->args['document'] ?? '';
$index = new Document($index);
$this->createIndex($index, $projectId);
break;
case DELETE_TYPE_INDEX:
$index = $this->args['document'] ?? '';
$index = new Document($index);
$this->deleteIndex($index, $projectId);
break;
default:
Console::error('No database operation for type: '.$type);
break;
}
}
public function shutdown(): void
{
}
/**
* @param Document $attribute
* @param string $projectId
*/
protected function createAttribute($attribute, $projectId): void
{
$dbForExternal = $this->getExternalDB($projectId);
$collectionId = $attribute->getCollection();
$id = $attribute->getAttribute('$id', '');
$type = $attribute->getAttribute('type', '');
$size = $attribute->getAttribute('size', 0);
$required = $attribute->getAttribute('required', false);
$default = $attribute->getAttribute('default', null);
$signed = $attribute->getAttribute('signed', true);
$array = $attribute->getAttribute('array', false);
$filters = $attribute->getAttribute('filters', []);
$success = $dbForExternal->createAttribute($collectionId, $id, $type, $size, $required, $default, $signed, $array, $filters);
if ($success) {
$removed = $dbForExternal->removeAttributeInQueue($collectionId, $id);
}
}
/**
* @param Document $attribute
* @param string $projectId
*/
protected function deleteAttribute($attribute, $projectId): void
{
$dbForExternal = $this->getExternalDB($projectId);
$collectionId = $attribute->getCollection();
$id = $attribute->getAttribute('$id');
$success = $dbForExternal->deleteAttribute($collectionId, $id);
}
/**
* @param Document $index
* @param string $projectId
*/
protected function createIndex($index, $projectId): void
{
$dbForExternal = $this->getExternalDB($projectId);
$collectionId = $index->getCollection();
$id = $index->getAttribute('$id', '');
$type = $index->getAttribute('type', '');
$attributes = $index->getAttribute('attributes', []);
$lengths = $index->getAttribute('lengths', []);
$orders = $index->getAttribute('orders', []);
$success = $dbForExternal->createIndex($collectionId, $id, $type, $attributes, $lengths, $orders);
if ($success) {
$dbForExternal->removeIndexInQueue($collectionId, $id);
}
}
/**
* @param Document $index
* @param string $projectId
*/
protected function deleteIndex($index, $projectId): void
{
$dbForExternal = $this->getExternalDB($projectId);
$collectionId = $index->getCollection();
$id = $index->getAttribute('$id');
$success = $dbForExternal->deleteIndex($collectionId, $id);
}
/**
* @param string $projectId
*
* @return Database
*/
protected function getInternalDB($projectId): Database
{
global $register;
$dbForInternal = null;
go(function() use ($register, $projectId, &$dbForInternal) {
$db = $register->get('dbPool')->get();
$redis = $register->get('redisPool')->get();
$cache = new Cache(new RedisCache($redis));
$dbForInternal = new Database(new MariaDB($db), $cache);
$dbForInternal->setNamespace('project_'.$projectId.'_internal'); // Main DB
});
return $dbForInternal;
}
/**
* @param string $projectId
*
* @return Database
*/
protected function getExternalDB($projectId): Database
{
global $register;
/** @var Database $dbForExternal */
$dbForExternal = null;
go(function() use ($register, $projectId, &$dbForExternal) {
$db = $register->get('dbPool')->get();
$redis = $register->get('redisPool')->get();
$cache = new Cache(new RedisCache($redis));
$dbForExternal = new Database(new MariaDB($db), $cache);
$dbForExternal->setNamespace('project_'.$projectId.'_external'); // Main DB
});
return $dbForExternal;
}
}

View file

@ -1,12 +1,10 @@
<?php <?php
use Appwrite\Database\Database; use Utopia\Database\Database;
use Utopia\Database\Database as Database2; use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\Cache\Adapter\Redis as RedisCache;
use Appwrite\Database\Adapter\MySQL as MySQLAdapter; use Utopia\Database\Validator\Authorization;
use Appwrite\Database\Adapter\Redis as RedisAdapter;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Resque\Worker; use Appwrite\Resque\Worker;
use Utopia\Storage\Device\Local; use Utopia\Storage\Device\Local;
use Utopia\Abuse\Abuse; use Utopia\Abuse\Abuse;
@ -43,19 +41,17 @@ class DeletesV1 extends Worker
$document = new Document($document); $document = new Document($document);
switch ($document->getCollection()) { switch ($document->getCollection()) {
case Database::SYSTEM_COLLECTION_PROJECTS: // TODO@kodumbeats define these as constants somewhere
case 'projects':
$this->deleteProject($document); $this->deleteProject($document);
break; break;
case Database::SYSTEM_COLLECTION_FUNCTIONS: case 'functions':
$this->deleteFunction($document, $projectId); $this->deleteFunction($document, $projectId);
break; break;
case Database::SYSTEM_COLLECTION_USERS: case 'users':
$this->deleteUser($document, $projectId); $this->deleteUser($document, $projectId);
break; break;
case Database::SYSTEM_COLLECTION_COLLECTIONS: case 'teams':
$this->deleteDocuments($document, $projectId);
break;
case Database::SYSTEM_COLLECTION_TEAMS:
$this->deleteMemberships($document, $projectId); $this->deleteMemberships($document, $projectId);
break; break;
default: default:
@ -91,97 +87,91 @@ class DeletesV1 extends Worker
{ {
} }
protected function deleteDocuments(Document $document, $projectId) /**
{ * @param Document $document teams document
$collectionId = $document->getId(); * @param string $projectId
*/
// Delete Documents in the deleted collection
$this->deleteByGroup([
'$collection='.$collectionId
], $this->getProjectDB($projectId));
}
protected function deleteMemberships(Document $document, $projectId) { protected function deleteMemberships(Document $document, $projectId) {
$teamId = $document->getAttribute('teamId', '');
// Delete Memberships // Delete Memberships
$this->deleteByGroup([ $this->deleteByGroup('memberships', [
'$collection='.Database::SYSTEM_COLLECTION_MEMBERSHIPS, new Query('teamId', Query::TYPE_EQUAL, [$teamId])
'teamId='.$document->getId(), ], $this->getInternalDB($projectId));
], $this->getProjectDB($projectId));
} }
/**
* @param Document $document project document
*/
protected function deleteProject(Document $document) protected function deleteProject(Document $document)
{ {
$projectId = $document->getId();
// Delete all DBs // Delete all DBs
$this->getConsoleDB()->deleteNamespace($document->getId()); $this->getExternalDB($projectId)->delete();
$this->getInternalDB($projectId)->delete();
// Delete all storage directories
$uploads = new Local(APP_STORAGE_UPLOADS.'/app-'.$document->getId()); $uploads = new Local(APP_STORAGE_UPLOADS.'/app-'.$document->getId());
$cache = new Local(APP_STORAGE_CACHE.'/app-'.$document->getId()); $cache = new Local(APP_STORAGE_CACHE.'/app-'.$document->getId());
// Delete all storage directories
$uploads->delete($uploads->getRoot(), true); $uploads->delete($uploads->getRoot(), true);
$cache->delete($cache->getRoot(), true); $cache->delete($cache->getRoot(), true);
} }
/**
* @param Document $document user document
* @param string $projectId
*/
protected function deleteUser(Document $document, $projectId) protected function deleteUser(Document $document, $projectId)
{ {
$tokens = $document->getAttribute('tokens', []); $userId = $document->getId();
foreach ($tokens as $token) {
if (!$this->getProjectDB($projectId)->deleteDocument($token->getId())) {
throw new Exception('Failed to remove token from DB');
}
}
$sessions = $document->getAttribute('sessions', []);
foreach ($sessions as $session) {
if (!$this->getProjectDB($projectId)->deleteDocument($session->getId())) {
throw new Exception('Failed to remove session from DB');
}
}
// Tokens and Sessions removed with user document
// Delete Memberships and decrement team membership counts // Delete Memberships and decrement team membership counts
$this->deleteByGroup([ $this->deleteByGroup('memberships', [
'$collection='.Database::SYSTEM_COLLECTION_MEMBERSHIPS, new Query('userId', Query::TYPE_EQUAL, [$userId])
'userId='.$document->getId(), ], $this->getInternalDB($projectId), function(Document $document) use ($projectId, $userId) {
], $this->getProjectDB($projectId), function(Document $document) use ($projectId) {
if ($document->getAttribute('confirm')) { // Count only confirmed members if ($document->getAttribute('confirm')) { // Count only confirmed members
$teamId = $document->getAttribute('teamId'); $teamId = $document->getAttribute('teamId');
$team = $this->getProjectDB($projectId)->getDocument($teamId); $team = $this->getInternalDB($projectId)->getDocument('teams', $teamId);
if(!$team->isEmpty()) { if(!$team->isEmpty()) {
$team = $this->getProjectDB($projectId)->updateDocument(\array_merge($team->getArrayCopy(), [ $team = $this->getInternalDB($projectId)->updateDocument('teams', $teamId, new Document(\array_merge($team->getArrayCopy(), [
'sum' => \max($team->getAttribute('sum', 0) - 1, 0), // Ensure that sum >= 0 'sum' => \max($team->getAttribute('sum', 0) - 1, 0), // Ensure that sum >= 0
])); ])));
} }
} }
}); });
} }
/**
* @param int $timestamp
*/
protected function deleteExecutionLogs($timestamp) protected function deleteExecutionLogs($timestamp)
{ {
$this->deleteForProjectIds(function($projectId) use ($timestamp) { $this->deleteForProjectIds(function($projectId) use ($timestamp) {
if (!($projectDB = $this->getProjectDB($projectId))) { if (!($dbForInternal = $this->getInternalDB($projectId))) {
throw new Exception('Failed to get projectDB for project '.$projectId); throw new Exception('Failed to get projectDB for project '.$projectId);
} }
// Delete Executions // Delete Executions
$this->deleteByGroup([ $this->deleteByGroup('executions', [
'$collection='.Database::SYSTEM_COLLECTION_EXECUTIONS, new Query('dateCreated', Query::TYPE_LESSER, [$timestamp])
'dateCreated<'.$timestamp ], $dbForInternal);
], $projectDB);
}); });
} }
/**
* @param int $timestamp
*/
protected function deleteAbuseLogs($timestamp) protected function deleteAbuseLogs($timestamp)
{ {
global $register;
if($timestamp == 0) { if($timestamp == 0) {
throw new Exception('Failed to delete audit logs. No timestamp provided'); throw new Exception('Failed to delete audit logs. No timestamp provided');
} }
$this->deleteForProjectIds(function($projectId) use ($timestamp){ $this->deleteForProjectIds(function($projectId) use ($timestamp){
$timeLimit = new TimeLimit("", 0, 1, $this->getInternalDB($projectId)); $timeLimit = new TimeLimit("", 0, 1, $this->getInternalDB($projectId));
$abuse = new Abuse($timeLimit); $abuse = new Abuse($timeLimit);
$status = $abuse->cleanup($timestamp); $status = $abuse->cleanup($timestamp);
@ -191,13 +181,15 @@ class DeletesV1 extends Worker
}); });
} }
/**
* @param int $timestamp
*/
protected function deleteAuditLogs($timestamp) protected function deleteAuditLogs($timestamp)
{ {
global $register;
if($timestamp == 0) { if($timestamp == 0) {
throw new Exception('Failed to delete audit logs. No timestamp provided'); throw new Exception('Failed to delete audit logs. No timestamp provided');
} }
$this->deleteForProjectIds(function($projectId) use ($register, $timestamp){ $this->deleteForProjectIds(function($projectId) use ($timestamp){
$audit = new Audit($this->getInternalDB($projectId)); $audit = new Audit($this->getInternalDB($projectId));
$status = $audit->cleanup($timestamp); $status = $audit->cleanup($timestamp);
if (!$status) { if (!$status) {
@ -206,16 +198,19 @@ class DeletesV1 extends Worker
}); });
} }
/**
* @param Document $document function document
* @param string $projectId
*/
protected function deleteFunction(Document $document, $projectId) protected function deleteFunction(Document $document, $projectId)
{ {
$projectDB = $this->getProjectDB($projectId); $dbForInternal = $this->getInternalDB($projectId);
$device = new Local(APP_STORAGE_FUNCTIONS.'/app-'.$projectId); $device = new Local(APP_STORAGE_FUNCTIONS.'/app-'.$projectId);
// Delete Tags // Delete Tags
$this->deleteByGroup([ $this->deleteByGroup('tags', [
'$collection='.Database::SYSTEM_COLLECTION_TAGS, new Query('functionId', Query::TYPE_EQUAL, [$document->getId()])
'functionId='.$document->getId(), ], $dbForInternal, function(Document $document) use ($device) {
], $projectDB, function(Document $document) use ($device) {
if ($device->delete($document->getAttribute('path', ''))) { if ($device->delete($document->getAttribute('path', ''))) {
Console::success('Delete code tag: '.$document->getAttribute('path', '')); Console::success('Delete code tag: '.$document->getAttribute('path', ''));
@ -226,17 +221,25 @@ class DeletesV1 extends Worker
}); });
// Delete Executions // Delete Executions
$this->deleteByGroup([ $this->deleteByGroup('executions', [
'$collection='.Database::SYSTEM_COLLECTION_EXECUTIONS, new Query('functionId', Query::TYPE_EQUAL, [$document->getId()])
'functionId='.$document->getId(), ], $dbForInternal);
], $projectDB);
} }
/**
* @param Document $document to be deleted
* @param Database $database to delete it from
* @param callable $callback to perform after document is deleted
*
* @return bool
*/
protected function deleteById(Document $document, Database $database, callable $callback = null): bool protected function deleteById(Document $document, Database $database, callable $callback = null): bool
{ {
Authorization::disable(); Authorization::disable();
if($database->deleteDocument($document->getId())) { // TODO@kodumbeats is it better to pass objects or ID strings?
if($database->deleteDocument($document->getCollection(), $document->getId())) {
Console::success('Deleted document "'.$document->getId().'" successfully'); Console::success('Deleted document "'.$document->getId().'" successfully');
if(is_callable($callback)) { if(is_callable($callback)) {
@ -253,6 +256,9 @@ class DeletesV1 extends Worker
Authorization::reset(); Authorization::reset();
} }
/**
* @param callable $callback
*/
protected function deleteForProjectIds(callable $callback) protected function deleteForProjectIds(callable $callback)
{ {
$count = 0; $count = 0;
@ -262,19 +268,12 @@ class DeletesV1 extends Worker
$sum = $limit; $sum = $limit;
$executionStart = \microtime(true); $executionStart = \microtime(true);
while($sum === $limit) { while($sum === $limit) {
$chunk++; $chunk++;
Authorization::disable(); Authorization::disable();
$projects = $this->getConsoleDB()->getCollection([ $projects = $this->getConsoleDB()->find('projects', [], $limit);
'limit' => $limit,
'orderType' => 'ASC',
'orderCast' => 'string',
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_PROJECTS,
],
]);
Authorization::reset(); Authorization::reset();
$projectIds = array_map (function ($project) { $projectIds = array_map (function ($project) {
@ -294,7 +293,13 @@ class DeletesV1 extends Worker
Console::info("Found {$count} projects " . ($executionEnd - $executionStart) . " seconds"); Console::info("Found {$count} projects " . ($executionEnd - $executionStart) . " seconds");
} }
protected function deleteByGroup(array $filters, Database $database, callable $callback = null) /**
* @param string $collection collectionID
* @param Query[] $queries
* @param Database $database
* @param callable $callback
*/
protected function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null)
{ {
$count = 0; $count = 0;
$chunk = 0; $chunk = 0;
@ -303,19 +308,13 @@ class DeletesV1 extends Worker
$sum = $limit; $sum = $limit;
$executionStart = \microtime(true); $executionStart = \microtime(true);
while($sum === $limit) { while($sum === $limit) {
$chunk++; $chunk++;
Authorization::disable(); Authorization::disable();
$results = $database->getCollection([ $results = $database->find($collection, $queries, $limit, 0);
'limit' => $limit,
'orderField' => '$id',
'orderType' => 'ASC',
'orderCast' => 'string',
'filters' => $filters,
]);
Authorization::reset(); Authorization::reset();
@ -334,6 +333,10 @@ class DeletesV1 extends Worker
Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds");
} }
/**
* @param Document $document certificates document
* @return Database
*/
protected function deleteCertificates(Document $document) protected function deleteCertificates(Document $document)
{ {
$domain = $document->getAttribute('domain'); $domain = $document->getAttribute('domain');
@ -348,56 +351,48 @@ class DeletesV1 extends Worker
Console::info("No certificate files found for {$domain}"); Console::info("No certificate files found for {$domain}");
} }
} }
/**
* @param string $projectId
* @return Database
*/
protected function getInternalDB($projectId): Database
{
global $register;
$cache = new Cache(new RedisCache($register->get('cache')));
$dbForInternal = new Database(new MariaDB($register->get('db')), $cache);
$dbForInternal->setNamespace('project_'.$projectId.'_internal'); // Main DB
return $dbForInternal;
}
/** /**
* @return Database; * @param string $projectId
* @return Database
*/
protected function getExternalDB($projectId): Database
{
global $register;
$cache = new Cache(new RedisCache($register->get('cache')));
$dbForExternal = new Database(new MariaDB($register->get('db')), $cache);
$dbForExternal->setNamespace('project_'.$projectId.'_external'); // Main DB
return $dbForExternal;
}
/**
* @return Database
*/ */
protected function getConsoleDB(): Database protected function getConsoleDB(): Database
{ {
global $register; global $register;
$db = $register->get('db');
$cache = $register->get('cache');
if($this->consoleDB === null) {
$this->consoleDB = new Database();
$this->consoleDB->setAdapter(new RedisAdapter(new MySQLAdapter($db, $cache), $cache));;
$this->consoleDB->setNamespace('app_console'); // Main DB
$this->consoleDB->setMocks(Config::getParam('collections', []));
}
return $this->consoleDB;
}
/**
* @return Database;
*/
protected function getProjectDB($projectId): Database
{
global $register;
$db = $register->get('db');
$cache = $register->get('cache');
$projectDB = new Database();
$projectDB->setAdapter(new RedisAdapter(new MySQLAdapter($db, $cache), $cache));
$projectDB->setNamespace('app_'.$projectId); // Main DB
$projectDB->setMocks(Config::getParam('collections', []));
return $projectDB;
}
/**
* @return Database2
*/
protected function getInternalDB($projectId): Database2
{
global $register;
$cache = new Cache(new RedisCache($register->get('cache'))); $cache = new Cache(new RedisCache($register->get('cache')));
$dbForInternal = new Database2(new MariaDB($register->get('db')), $cache); $dbForConsole = new Database(new MariaDB($register->get('db')), $cache);
$dbForInternal->setNamespace('project_'.$projectId.'_internal'); // Main DB $dbForConsole->setNamespace('project_console_internal'); // Main DB
return $dbForInternal; return $dbForConsole;
} }
} }

View file

@ -465,10 +465,6 @@ class FunctionsV1 extends Worker
Authorization::reset(); Authorization::reset();
if (false === $function) {
throw new Exception('Failed saving execution to DB', 500);
}
$executionModel = new Execution(); $executionModel = new Execution();
$executionUpdate = new Event('v1-webhooks', 'WebhooksV1'); $executionUpdate = new Event('v1-webhooks', 'WebhooksV1');

View file

@ -1,14 +1,14 @@
<?php <?php
use Appwrite\Database\Database;
use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
use Appwrite\Database\Adapter\Redis as RedisAdapter;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Resque\Worker; use Appwrite\Resque\Worker;
use Cron\CronExpression; use Cron\CronExpression;
use Utopia\App; use Utopia\App;
use Utopia\Cache\Adapter\Redis;
use Utopia\Cache\Cache;
use Utopia\CLI\Console; use Utopia\CLI\Console;
use Utopia\Config\Config; use Utopia\Database\Database;
use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Validator\Authorization;
require_once __DIR__.'/../workers.php'; require_once __DIR__.'/../workers.php';
@ -33,10 +33,20 @@ class TasksV1 extends Worker
$db = $register->get('db'); $db = $register->get('db');
$cache = $register->get('cache'); $cache = $register->get('cache');
$consoleDB = new Database(); $projectId = $this->args['projectId'] ?? null;
$consoleDB->setAdapter(new RedisAdapter(new MySQLAdapter($db, $cache), $cache)); $taskId = $this->args['$id'] ?? null;
$consoleDB->setNamespace('app_console'); // Main DB $updated = $this->args['updated'] ?? null;
$consoleDB->setMocks(Config::getParam('collections', [])); $next = $this->args['next'] ?? null;
$delay = \time() - $next;
$errors = [];
$timeout = 60 * 5; // 5 minutes
$errorLimit = 5;
$logLimit = 5;
$alert = '';
$cache = new Cache(new Redis($cache));
$dbForConsole = new Database(new MariaDB($db), $cache);
$dbForConsole->setNamespace('project_console_internal');
/* /*
* 1. Get Original Task * 1. Get Original Task
@ -51,30 +61,25 @@ class TasksV1 extends Worker
* If error count bigger than allowed change status to pause * If error count bigger than allowed change status to pause
*/ */
$taskId = $this->args['$id'] ?? null;
$updated = $this->args['updated'] ?? null;
$next = $this->args['next'] ?? null;
$delay = \time() - $next;
$errors = [];
$timeout = 60 * 5; // 5 minutes
$errorLimit = 5;
$logLimit = 5;
$alert = '';
if (empty($taskId)) { if (empty($taskId)) {
throw new Exception('Missing task $id'); throw new Exception('Missing task $id');
} }
Authorization::disable(); Authorization::disable();
$task = $consoleDB->getDocument($taskId); $project = $dbForConsole->getDocument('projects', $projectId);
Authorization::reset(); Authorization::reset();
if (\is_null($task->getId()) || Database::SYSTEM_COLLECTION_TASKS !== $task->getCollection()) { // Find the task in the $project->getAttribute('tasks') array
$taskIndex = array_search($taskId, array_column($project->getAttributes()['tasks'], '$id'));
if ($taskIndex === false) {
throw new Exception('Task Not Found'); throw new Exception('Task Not Found');
} }
$task = $project->getAttribute('tasks')[$taskIndex];
if ($task->getAttribute('updated') !== $updated) { // Task have already been rescheduled by owner if ($task->getAttribute('updated') !== $updated) { // Task have already been rescheduled by owner
return; return;
} }
@ -193,9 +198,11 @@ class TasksV1 extends Worker
->setAttribute('delay', $delay) ->setAttribute('delay', $delay)
; ;
Authorization::disable(); $project->findAndReplace('$id', $task->getId(), $task);
if (false === $consoleDB->updateDocument($task->getArrayCopy())) { Authorization::disable();
if (false === $dbForConsole->updateDocument('projects', $project->getId(), $project)) {
throw new Exception('Failed saving tasks to DB'); throw new Exception('Failed saving tasks to DB');
} }

10
bin/worker-database Normal file
View file

@ -0,0 +1,10 @@
#!/bin/sh
if [ -z "$_APP_REDIS_USER" ] && [ -z "$_APP_REDIS_PASS" ]
then
REDIS_BACKEND="${_APP_REDIS_HOST}:${_APP_REDIS_PORT}"
else
REDIS_BACKEND="redis://${_APP_REDIS_USER}:${_APP_REDIS_PASS}@${_APP_REDIS_HOST}:${_APP_REDIS_PORT}"
fi
QUEUE='v1-database' APP_INCLUDE='/usr/src/code/app/workers/database.php' php /usr/src/code/vendor/bin/resque -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php

16
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "68ed82b309f6a9dd878ef031424ac00a", "content-hash": "ca8cf356c884e8d332b04bebf9d3b11b",
"packages": [ "packages": [
{ {
"name": "adhocore/jwt", "name": "adhocore/jwt",
@ -115,16 +115,16 @@
}, },
{ {
"name": "appwrite/php-runtimes", "name": "appwrite/php-runtimes",
"version": "0.3.0", "version": "0.4.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/appwrite/php-runtimes.git", "url": "https://github.com/appwrite/php-runtimes.git",
"reference": "39be003cdff22c8447de151921001eb5d3bf2319" "reference": "cc7090a67d8824c779190b38873f0f8154f906b2"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/appwrite/php-runtimes/zipball/39be003cdff22c8447de151921001eb5d3bf2319", "url": "https://api.github.com/repos/appwrite/php-runtimes/zipball/cc7090a67d8824c779190b38873f0f8154f906b2",
"reference": "39be003cdff22c8447de151921001eb5d3bf2319", "reference": "cc7090a67d8824c779190b38873f0f8154f906b2",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -144,7 +144,7 @@
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"BSD-3" "BSD-3-Clause"
], ],
"authors": [ "authors": [
{ {
@ -164,9 +164,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/appwrite/php-runtimes/issues", "issues": "https://github.com/appwrite/php-runtimes/issues",
"source": "https://github.com/appwrite/php-runtimes/tree/0.3.0" "source": "https://github.com/appwrite/php-runtimes/tree/0.4.0"
}, },
"time": "2021-06-15T07:52:43+00:00" "time": "2021-06-23T07:17:12+00:00"
}, },
{ {
"name": "chillerlan/php-qrcode", "name": "chillerlan/php-qrcode",

View file

@ -240,6 +240,31 @@ services:
- _APP_DB_USER - _APP_DB_USER
- _APP_DB_PASS - _APP_DB_PASS
appwrite-worker-database:
entrypoint: worker-database
container_name: appwrite-worker-database
build:
context: .
networks:
- appwrite
volumes:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
environment:
- _APP_ENV
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
appwrite-worker-certificates: appwrite-worker-certificates:
entrypoint: worker-certificates entrypoint: worker-certificates
container_name: appwrite-worker-certificates container_name: appwrite-worker-certificates

View file

@ -7,6 +7,9 @@ use Resque;
class Event class Event
{ {
const DATABASE_QUEUE_NAME= 'v1-database';
const DATABASE_CLASS_NAME = 'DatabaseV1';
const DELETE_QUEUE_NAME = 'v1-deletes'; const DELETE_QUEUE_NAME = 'v1-deletes';
const DELETE_CLASS_NAME = 'DeletesV1'; const DELETE_CLASS_NAME = 'DeletesV1';

View file

@ -10,6 +10,7 @@ use Appwrite\Utopia\Response\Filter;
use Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response\Model\None; use Appwrite\Utopia\Response\Model\None;
use Appwrite\Utopia\Response\Model\Any; use Appwrite\Utopia\Response\Model\Any;
use Appwrite\Utopia\Response\Model\Attribute;
use Appwrite\Utopia\Response\Model\BaseList; use Appwrite\Utopia\Response\Model\BaseList;
use Appwrite\Utopia\Response\Model\Collection; use Appwrite\Utopia\Response\Model\Collection;
use Appwrite\Utopia\Response\Model\Continent; use Appwrite\Utopia\Response\Model\Continent;
@ -22,6 +23,7 @@ use Appwrite\Utopia\Response\Model\ErrorDev;
use Appwrite\Utopia\Response\Model\Execution; use Appwrite\Utopia\Response\Model\Execution;
use Appwrite\Utopia\Response\Model\File; use Appwrite\Utopia\Response\Model\File;
use Appwrite\Utopia\Response\Model\Func; use Appwrite\Utopia\Response\Model\Func;
use Appwrite\Utopia\Response\Model\Index;
use Appwrite\Utopia\Response\Model\JWT; use Appwrite\Utopia\Response\Model\JWT;
use Appwrite\Utopia\Response\Model\Key; use Appwrite\Utopia\Response\Model\Key;
use Appwrite\Utopia\Response\Model\Language; use Appwrite\Utopia\Response\Model\Language;
@ -63,6 +65,10 @@ class Response extends SwooleResponse
// Database // Database
const MODEL_COLLECTION = 'collection'; const MODEL_COLLECTION = 'collection';
const MODEL_COLLECTION_LIST = 'collectionList'; const MODEL_COLLECTION_LIST = 'collectionList';
const MODEL_ATTRIBUTE = 'attribute';
const MODEL_ATTRIBUTE_LIST = 'attributeList';
const MODEL_INDEX = 'index';
const MODEL_INDEX_LIST = 'indexList';
const MODEL_RULE = 'rule'; const MODEL_RULE = 'rule';
const MODEL_DOCUMENT = 'document'; const MODEL_DOCUMENT = 'document';
const MODEL_DOCUMENT_LIST = 'documentList'; const MODEL_DOCUMENT_LIST = 'documentList';
@ -150,6 +156,8 @@ class Response extends SwooleResponse
->setModel(new ErrorDev()) ->setModel(new ErrorDev())
// Lists // Lists
->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION)) ->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION))
->setModel(new BaseList('Attributes List', self::MODEL_ATTRIBUTE_LIST, 'attributes', self::MODEL_ATTRIBUTE))
->setModel(new BaseList('Indexes List', self::MODEL_INDEX_LIST, 'indexes', self::MODEL_INDEX))
->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_DOCUMENT)) ->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_DOCUMENT))
->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER)) ->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER))
->setModel(new BaseList('Sessions List', self::MODEL_SESSION_LIST, 'sessions', self::MODEL_SESSION)) ->setModel(new BaseList('Sessions List', self::MODEL_SESSION_LIST, 'sessions', self::MODEL_SESSION))
@ -174,6 +182,8 @@ class Response extends SwooleResponse
// Entities // Entities
->setModel(new Permissions()) ->setModel(new Permissions())
->setModel(new Collection()) ->setModel(new Collection())
->setModel(new Attribute())
->setModel(new Index())
->setModel(new ModelDocument()) ->setModel(new ModelDocument())
->setModel(new Rule()) ->setModel(new Rule())
->setModel(new Log()) ->setModel(new Log())

View file

@ -0,0 +1,87 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Attribute extends Model
{
public function __construct()
{
$this
->addRule('$collection', [
'type' => self::TYPE_STRING,
'description' => 'Collection ID.',
'default' => '',
'example' => '5e5ea5c16d55',
])
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'Attribute ID.',
'default' => '',
'example' => '60ccf71b98a2d',
])
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Attribute type.',
'default' => '',
'example' => 'string',
])
->addRule('size', [
'type' => self::TYPE_STRING,
'description' => 'Attribute size.',
'default' => 0,
'example' => 128,
])
->addRule('required', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Is attribute required?',
'default' => false,
'example' => true,
])
->addRule('signed', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Is attribute signed?',
'default' => true,
'example' => true,
'required' => false,
])
->addRule('array', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Is attribute an array?',
'default' => false,
'example' => false,
'required' => false
])
->addRule('filters', [
'type' => self::TYPE_STRING,
'description' => 'Attribute filters.',
'default' => [],
'example' => [],
'array' => true,
'required' => false,
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Attribute';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_ATTRIBUTE;
}
}

View file

@ -4,6 +4,7 @@ namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response; use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response\Model;
use stdClass;
class Collection extends Model class Collection extends Model
{ {
@ -16,36 +17,53 @@ class Collection extends Model
'default' => '', 'default' => '',
'example' => '5e5ea5c16897e', 'example' => '5e5ea5c16897e',
]) ])
->addRule('$permissions', [ ->addRule('$read', [
'type' => Response::MODEL_PERMISSIONS, 'type' => self::TYPE_STRING,
'description' => 'Collection permissions.', 'description' => 'Collection read permissions.',
'default' => new \stdClass, 'default' => '',
'example' => new \stdClass, 'example' => 'role:all',
'array' => false, 'array' => true
])
->addRule('$write', [
'type' => self::TYPE_STRING,
'description' => 'Collection write permissions.',
'default' => '',
'example' => 'user:608f9da25e7e1',
'array' => true
]) ])
->addRule('name', [ ->addRule('name', [
'type' => self::TYPE_STRING, 'type' => self::TYPE_STRING,
'description' => 'Collection name.', 'description' => 'Collection name.',
'default' => '', 'default' => '',
'example' => 'Movies', 'example' => '',
]) ])
->addRule('dateCreated', [ ->addRule('attributes', [
'type' => self::TYPE_INTEGER, 'type' => Response::MODEL_ATTRIBUTE,
'description' => 'Collection creation date in Unix timestamp.', 'description' => 'Collection attributes.',
'default' => 0,
'example' => 1592981250,
])
->addRule('dateUpdated', [
'type' => self::TYPE_INTEGER,
'description' => 'Collection creation date in Unix timestamp.',
'default' => 0,
'example' => 1592981550,
])
->addRule('rules', [
'type' => Response::MODEL_RULE,
'description' => 'Collection rules.',
'default' => [], 'default' => [],
'array' => true, 'example' => new stdClass,
'array' => true
])
->addRule('indexes', [
'type' => Response::MODEL_INDEX,
'description' => 'Collection indexes.',
'default' => [],
'example' => new stdClass,
'array' => true
])
->addRule('attributesInQueue', [
'type' => Response::MODEL_ATTRIBUTE,
'description' => 'Collection attributes in creation queue.',
'default' => [],
'example' => new stdClass,
'array' => true
])
->addRule('indexesInQueue', [
'type' => Response::MODEL_INDEX,
'description' => 'Collection indexes in creation queue.',
'default' => [],
'example' => new stdClass,
'array' => true
]) ])
; ;
} }
@ -69,4 +87,4 @@ class Collection extends Model
{ {
return Response::MODEL_COLLECTION; return Response::MODEL_COLLECTION;
} }
} }

View file

@ -41,12 +41,20 @@ class Document extends Any
'default' => '', 'default' => '',
'example' => '5e5ea5c15117e', 'example' => '5e5ea5c15117e',
]) ])
->addRule('$permissions', [ ->addRule('$read', [
'type' => Response::MODEL_PERMISSIONS, 'type' => self::TYPE_STRING,
'description' => 'Document permissions.', 'description' => 'Document read permissions.',
'default' => new \stdClass, 'default' => '',
'example' => new \stdClass, 'example' => 'role:all',
'array' => false, 'array' => true,
]); ])
->addRule('$write', [
'type' => self::TYPE_STRING,
'description' => 'Document write permissions.',
'default' => '',
'example' => 'user:608f9da25e7e1',
'array' => true,
])
;
} }
} }

View file

@ -0,0 +1,76 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Index extends Model
{
public function __construct()
{
$this
->addRule('$collection', [
'type' => self::TYPE_STRING,
'description' => 'Collection ID.',
'default' => '',
'example' => '5e5ea5c16d55',
])
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'Index ID.',
'default' => '',
'example' => '',
])
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Index type.',
'default' => '',
'example' => '',
])
->addRule('attributes', [
'type' => self::TYPE_STRING,
'description' => 'Index attributes.',
'default' => [],
'example' => [],
'array' => true,
])
->addRule('lengths', [
'type' => self::TYPE_STRING,
'description' => 'Index lengths.',
'default' => [],
'example' => [],
'array' => true,
'required' => false,
])
->addRule('orders', [
'type' => self::TYPE_STRING,
'description' => 'Index orders.',
'default' => [],
'example' => [],
'array' => true,
'required' => false,
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'Index';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_INDEX;
}
}

View file

@ -21,6 +21,12 @@ class Task extends Model
'default' => '', 'default' => '',
'example' => '5e5ea5c16897e', 'example' => '5e5ea5c16897e',
]) ])
->addRule('projectId', [
'type' => self::TYPE_STRING,
'description' => 'Project ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('name', [ ->addRule('name', [
'type' => self::TYPE_STRING, 'type' => self::TYPE_STRING,
'description' => 'Task name.', 'description' => 'Task name.',

View file

@ -69,6 +69,10 @@ trait ProjectCustom
'teams.write', 'teams.write',
'collections.read', 'collections.read',
'collections.write', 'collections.write',
'attributes.read',
'attributes.write',
'indexes.read',
'indexes.write',
'documents.read', 'documents.read',
'documents.write', 'documents.write',
'files.read', 'files.read',
@ -110,6 +114,10 @@ trait ProjectCustom
'database.collections.create', 'database.collections.create',
'database.collections.update', 'database.collections.update',
'database.collections.delete', 'database.collections.delete',
'database.attributes.create',
'database.attributes.delete',
'database.indexes.create',
'database.indexes.delete',
'database.documents.create', 'database.documents.create',
'database.documents.update', 'database.documents.update',
'database.documents.delete', 'database.documents.delete',

View file

@ -11,42 +11,6 @@ trait DatabaseBase
/** /**
* Test for SUCCESS * Test for SUCCESS
*/ */
$actors = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'name' => 'Actors',
'read' => ['role:all'],
'write' => ['role:member', 'role:admin'],
'rules' => [
[
'label' => 'First Name',
'key' => 'firstName',
'type' => 'text',
'default' => '',
'required' => true,
'array' => false
],
[
'label' => 'Last Name',
'key' => 'lastName',
'type' => 'text',
'default' => '',
'required' => true,
'array' => false
],
],
]);
$this->assertEquals($actors['headers']['status-code'], 201);
$this->assertEquals($actors['body']['name'], 'Actors');
$this->assertIsArray($actors['body']['$permissions']);
$this->assertIsArray($actors['body']['$permissions']['read']);
$this->assertIsArray($actors['body']['$permissions']['write']);
$this->assertCount(1, $actors['body']['$permissions']['read']);
$this->assertCount(2, $actors['body']['$permissions']['write']);
$movies = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([ $movies = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
@ -54,50 +18,142 @@ trait DatabaseBase
]), [ ]), [
'name' => 'Movies', 'name' => 'Movies',
'read' => ['role:all'], 'read' => ['role:all'],
'write' => ['role:member', 'role:admin'], 'write' => ['role:all'],
'rules' => [
[
'label' => 'Name',
'key' => 'name',
'type' => 'text',
'default' => '',
'required' => true,
'array' => false
],
[
'label' => 'Release Year',
'key' => 'releaseYear',
'type' => 'numeric',
'default' => 0,
'required' => false,
'array' => false
],
[
'label' => 'Actors',
'key' => 'actors',
'type' => 'document',
'default' => [],
'required' => false,
'array' => true,
'list' => [$actors['body']['$id']],
],
],
]); ]);
$this->assertEquals($movies['headers']['status-code'], 201); $this->assertEquals($movies['headers']['status-code'], 201);
$this->assertEquals($movies['body']['name'], 'Movies'); $this->assertEquals($movies['body']['name'], 'Movies');
$this->assertIsArray($movies['body']['$permissions']);
$this->assertIsArray($movies['body']['$permissions']['read']);
$this->assertIsArray($movies['body']['$permissions']['write']);
$this->assertCount(1, $movies['body']['$permissions']['read']);
$this->assertCount(2, $movies['body']['$permissions']['write']);
return array_merge(['moviesId' => $movies['body']['$id'], 'actorsId' => $actors['body']['$id']]); return ['moviesId' => $movies['body']['$id']];
} }
/** /**
* @depends testCreateCollection * @depends testCreateCollection
*/ */
public function testCreateAttributes(array $data): array
{
$title = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['moviesId'] . '/attributes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'id' => 'title',
'type' => 'string',
'size' => 256,
'required' => true,
]);
$releaseYear = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['moviesId'] . '/attributes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'id' => 'releaseYear',
'type' => 'integer',
'size' => 0,
'required' => true,
]);
$actors = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['moviesId'] . '/attributes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'id' => 'actors',
'type' => 'string',
'size' => 256,
'required' => false,
'default' => null,
'array' => true,
]);
$this->assertEquals($title['headers']['status-code'], 201);
$this->assertEquals($title['body']['$collection'], $data['moviesId']);
$this->assertEquals($title['body']['$id'], 'title');
$this->assertEquals($title['body']['type'], 'string');
$this->assertEquals($title['body']['size'], 256);
$this->assertEquals($title['body']['required'], true);
$this->assertEquals($releaseYear['headers']['status-code'], 201);
$this->assertEquals($releaseYear['body']['$collection'], $data['moviesId']);
$this->assertEquals($releaseYear['body']['$id'], 'releaseYear');
$this->assertEquals($releaseYear['body']['type'], 'integer');
$this->assertEquals($releaseYear['body']['size'], 0);
$this->assertEquals($releaseYear['body']['required'], true);
$this->assertEquals($actors['headers']['status-code'], 201);
$this->assertEquals($actors['body']['$collection'], $data['moviesId']);
$this->assertEquals($actors['body']['$id'], 'actors');
$this->assertEquals($actors['body']['type'], 'string');
$this->assertEquals($actors['body']['size'], 256);
$this->assertEquals($actors['body']['required'], false);
$this->assertEquals($actors['body']['array'], true);
// wait for database worker to create attributes
sleep(10);
$movies = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), []);
$this->assertEquals($movies['body']['$id'], $title['body']['$collection']);
$this->assertEquals($movies['body']['$id'], $releaseYear['body']['$collection']);
$this->assertEquals($movies['body']['$id'], $actors['body']['$collection']);
$this->assertIsArray($movies['body']['attributesInQueue']);
$this->assertCount(0, $movies['body']['attributesInQueue']);
$this->assertIsArray($movies['body']['attributes']);
$this->assertCount(3, $movies['body']['attributes']);
$this->assertEquals($movies['body']['attributes'][0]['$id'], $title['body']['$id']);
$this->assertEquals($movies['body']['attributes'][1]['$id'], $releaseYear['body']['$id']);
$this->assertEquals($movies['body']['attributes'][2]['$id'], $actors['body']['$id']);
return $data;
}
/**
* @depends testCreateAttributes
*/
public function testCreateIndexes(array $data): array
{
$titleIndex = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['moviesId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'id' => 'titleIndex',
'type' => 'fulltext',
'attributes' => ['title'],
]);
$this->assertEquals($titleIndex['headers']['status-code'], 201);
$this->assertEquals($titleIndex['body']['$collection'], $data['moviesId']);
$this->assertEquals($titleIndex['body']['$id'], 'titleIndex');
$this->assertEquals($titleIndex['body']['type'], 'fulltext');
$this->assertCount(1, $titleIndex['body']['attributes']);
$this->assertEquals($titleIndex['body']['attributes'][0], 'title');
// wait for database worker to create index
sleep(5);
$movies = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), []);
$this->assertEquals($movies['body']['$id'], $titleIndex['body']['$collection']);
$this->assertIsArray($movies['body']['indexes']);
$this->assertCount(1, $movies['body']['indexes']);
$this->assertEquals($movies['body']['indexes'][0]['$id'], $titleIndex['body']['$id']);
return $data;
}
/**
* @depends testCreateIndexes
*/
public function testCreateDocument(array $data):array public function testCreateDocument(array $data):array
{ {
$document1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([ $document1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
@ -105,21 +161,11 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'data' => [ 'data' => [
'name' => 'Captain America', 'title' => 'Captain America',
'releaseYear' => 1944, 'releaseYear' => 1944,
'actors' => [ 'actors' => [
[ 'Chris Evans',
'$collection' => $data['actorsId'], 'Samuel Jackson',
'$permissions' => ['read' => [], 'write' => []],
'firstName' => 'Chris',
'lastName' => 'Evans',
],
[
'$collection' => $data['actorsId'],
'$permissions' => ['read' => [], 'write' => []],
'firstName' => 'Samuel',
'lastName' => 'Jackson',
],
] ]
], ],
'read' => ['user:'.$this->getUser()['$id']], 'read' => ['user:'.$this->getUser()['$id']],
@ -131,27 +177,12 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'data' => [ 'data' => [
'name' => 'Spider-Man: Far From Home', 'title' => 'Spider-Man: Far From Home',
'releaseYear' => 2019, 'releaseYear' => 2019,
'actors' => [ 'actors' => [
[ 'Tom Holland',
'$collection' => $data['actorsId'], 'Zendaya Maree Stoermer',
'$permissions' => ['read' => [], 'write' => []], 'Samuel Jackson',
'firstName' => 'Tom',
'lastName' => 'Holland',
],
[
'$collection' => $data['actorsId'],
'$permissions' => ['read' => [], 'write' => []],
'firstName' => 'Zendaya',
'lastName' => 'Maree Stoermer',
],
[
'$collection' => $data['actorsId'],
'$permissions' => ['read' => [], 'write' => []],
'firstName' => 'Samuel',
'lastName' => 'Jackson',
],
] ]
], ],
'read' => ['user:'.$this->getUser()['$id']], 'read' => ['user:'.$this->getUser()['$id']],
@ -163,21 +194,11 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'data' => [ 'data' => [
'name' => 'Spider-Man: Homecoming', 'title' => 'Spider-Man: Homecoming',
'releaseYear' => 2017, 'releaseYear' => 2017,
'actors' => [ 'actors' => [
[ 'Tom Holland',
'$collection' => $data['actorsId'], 'Zendaya Maree Stoermer',
'$permissions' => ['read' => [], 'write' => []],
'firstName' => 'Tom',
'lastName' => 'Holland',
],
[
'$collection' => $data['actorsId'],
'$permissions' => ['read' => [], 'write' => []],
'firstName' => 'Zendaya',
'lastName' => 'Maree Stoermer',
],
], ],
], ],
'read' => ['user:'.$this->getUser()['$id']], 'read' => ['user:'.$this->getUser()['$id']],
@ -197,46 +218,40 @@ trait DatabaseBase
$this->assertEquals($document1['headers']['status-code'], 201); $this->assertEquals($document1['headers']['status-code'], 201);
$this->assertEquals($document1['body']['$collection'], $data['moviesId']); $this->assertEquals($document1['body']['$collection'], $data['moviesId']);
$this->assertEquals($document1['body']['name'], 'Captain America'); $this->assertEquals($document1['body']['title'], 'Captain America');
$this->assertEquals($document1['body']['releaseYear'], 1944); $this->assertEquals($document1['body']['releaseYear'], 1944);
$this->assertIsArray($document1['body']['$permissions']); $this->assertIsArray($document1['body']['$read']);
$this->assertIsArray($document1['body']['$permissions']['read']); $this->assertIsArray($document1['body']['$write']);
$this->assertIsArray($document1['body']['$permissions']['write']); $this->assertCount(1, $document1['body']['$read']);
$this->assertCount(1, $document1['body']['$permissions']['read']); $this->assertCount(1, $document1['body']['$write']);
$this->assertCount(1, $document1['body']['$permissions']['write']);
$this->assertCount(2, $document1['body']['actors']); $this->assertCount(2, $document1['body']['actors']);
$this->assertEquals($document1['body']['actors'][0], 'Chris Evans');
$this->assertEquals($document1['body']['actors'][1], 'Samuel Jackson');
$this->assertEquals($document2['headers']['status-code'], 201); $this->assertEquals($document2['headers']['status-code'], 201);
$this->assertEquals($document2['body']['$collection'], $data['moviesId']); $this->assertEquals($document2['body']['$collection'], $data['moviesId']);
$this->assertEquals($document2['body']['name'], 'Spider-Man: Far From Home'); $this->assertEquals($document2['body']['title'], 'Spider-Man: Far From Home');
$this->assertEquals($document2['body']['releaseYear'], 2019); $this->assertEquals($document2['body']['releaseYear'], 2019);
$this->assertIsArray($document2['body']['$permissions']); $this->assertIsArray($document2['body']['$read']);
$this->assertIsArray($document2['body']['$permissions']['read']); $this->assertIsArray($document2['body']['$write']);
$this->assertIsArray($document2['body']['$permissions']['write']); $this->assertCount(1, $document2['body']['$read']);
$this->assertCount(1, $document2['body']['$permissions']['read']); $this->assertCount(1, $document2['body']['$write']);
$this->assertCount(1, $document2['body']['$permissions']['write']);
$this->assertCount(3, $document2['body']['actors']); $this->assertCount(3, $document2['body']['actors']);
$this->assertEquals($document2['body']['actors'][0]['firstName'], 'Tom'); $this->assertEquals($document2['body']['actors'][0], 'Tom Holland');
$this->assertEquals($document2['body']['actors'][0]['lastName'], 'Holland'); $this->assertEquals($document2['body']['actors'][1], 'Zendaya Maree Stoermer');
$this->assertEquals($document2['body']['actors'][1]['firstName'], 'Zendaya'); $this->assertEquals($document2['body']['actors'][2], 'Samuel Jackson');
$this->assertEquals($document2['body']['actors'][1]['lastName'], 'Maree Stoermer');
$this->assertEquals($document2['body']['actors'][2]['firstName'], 'Samuel');
$this->assertEquals($document2['body']['actors'][2]['lastName'], 'Jackson');
$this->assertEquals($document3['headers']['status-code'], 201); $this->assertEquals($document3['headers']['status-code'], 201);
$this->assertEquals($document3['body']['$collection'], $data['moviesId']); $this->assertEquals($document3['body']['$collection'], $data['moviesId']);
$this->assertEquals($document3['body']['name'], 'Spider-Man: Homecoming'); $this->assertEquals($document3['body']['title'], 'Spider-Man: Homecoming');
$this->assertEquals($document3['body']['releaseYear'], 2017); $this->assertEquals($document3['body']['releaseYear'], 2017);
$this->assertIsArray($document3['body']['$permissions']); $this->assertIsArray($document3['body']['$read']);
$this->assertIsArray($document3['body']['$permissions']['read']); $this->assertIsArray($document3['body']['$write']);
$this->assertIsArray($document3['body']['$permissions']['write']); $this->assertCount(1, $document3['body']['$read']);
$this->assertCount(1, $document3['body']['$permissions']['read']); $this->assertCount(1, $document3['body']['$write']);
$this->assertCount(1, $document3['body']['$permissions']['write']);
$this->assertCount(2, $document3['body']['actors']); $this->assertCount(2, $document3['body']['actors']);
$this->assertEquals($document3['body']['actors'][0]['firstName'], 'Tom'); $this->assertEquals($document2['body']['actors'][0], 'Tom Holland');
$this->assertEquals($document3['body']['actors'][0]['lastName'], 'Holland'); $this->assertEquals($document2['body']['actors'][1], 'Zendaya Maree Stoermer');
$this->assertEquals($document3['body']['actors'][1]['firstName'], 'Zendaya');
$this->assertEquals($document3['body']['actors'][1]['lastName'], 'Maree Stoermer');
$this->assertEquals($document4['headers']['status-code'], 400); $this->assertEquals($document4['headers']['status-code'], 400);
@ -252,9 +267,8 @@ trait DatabaseBase
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'orderField' => 'releaseYear', 'orderAttributes' => ['releaseYear'],
'orderType' => 'ASC', 'orderTypes' => ['ASC'],
'orderCast' => 'int',
]); ]);
$this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']); $this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']);
@ -266,9 +280,8 @@ trait DatabaseBase
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'orderField' => 'releaseYear', 'orderAttributes' => ['releaseYear'],
'orderType' => 'DESC', 'orderTypes' => ['DESC'],
'orderCast' => 'int',
]); ]);
$this->assertEquals(1944, $documents['body']['documents'][2]['releaseYear']); $this->assertEquals(1944, $documents['body']['documents'][2]['releaseYear']);
@ -289,9 +302,8 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'limit' => 1, 'limit' => 1,
'orderField' => 'releaseYear', 'orderAttributes' => ['releaseYear'],
'orderType' => 'ASC', 'orderTypes' => ['ASC'],
'orderCast' => 'int',
]); ]);
$this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']); $this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']);
@ -303,9 +315,8 @@ trait DatabaseBase
], $this->getHeaders()), [ ], $this->getHeaders()), [
'limit' => 2, 'limit' => 2,
'offset' => 1, 'offset' => 1,
'orderField' => 'releaseYear', 'orderAttributes' => ['releaseYear'],
'orderType' => 'ASC', 'orderTypes' => ['ASC'],
'orderCast' => 'int',
]); ]);
$this->assertEquals(2017, $documents['body']['documents'][0]['releaseYear']); $this->assertEquals(2017, $documents['body']['documents'][0]['releaseYear']);
@ -324,7 +335,7 @@ trait DatabaseBase
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'search' => 'Captain America', 'queries' => ['title.search("Captain America")'],
]); ]);
$this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']); $this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']);
@ -334,7 +345,7 @@ trait DatabaseBase
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'search' => 'Homecoming', 'queries' => ['title.search("Homecoming")'],
]); ]);
$this->assertEquals(2017, $documents['body']['documents'][0]['releaseYear']); $this->assertEquals(2017, $documents['body']['documents'][0]['releaseYear']);
@ -344,7 +355,7 @@ trait DatabaseBase
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'search' => 'spider', 'queries' => ['title.search("spider")'],
]); ]);
$this->assertEquals(2019, $documents['body']['documents'][0]['releaseYear']); $this->assertEquals(2019, $documents['body']['documents'][0]['releaseYear']);
@ -353,52 +364,53 @@ trait DatabaseBase
return []; return [];
} }
// TODO@kodumbeats test for empty searches and misformatted queries
/** /**
* @depends testCreateDocument * @depends testCreateDocument
*/ */
public function testListDocumentsFilters(array $data):array // public function testListDocumentsFilters(array $data):array
{ // {
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([ // $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json', // 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], // 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ // ], $this->getHeaders()), [
'filters' => [ // 'filters' => [
'actors.firstName=Tom' // 'actors.firstName=Tom'
], // ],
]); // ]);
$this->assertCount(2, $documents['body']['documents']); // $this->assertCount(2, $documents['body']['documents']);
$this->assertEquals('Spider-Man: Far From Home', $documents['body']['documents'][0]['name']); // $this->assertEquals('Spider-Man: Far From Home', $documents['body']['documents'][0]['name']);
$this->assertEquals('Spider-Man: Homecoming', $documents['body']['documents'][1]['name']); // $this->assertEquals('Spider-Man: Homecoming', $documents['body']['documents'][1]['name']);
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([ // $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json', // 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], // 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ // ], $this->getHeaders()), [
'filters' => [ // 'filters' => [
'releaseYear=1944' // 'releaseYear=1944'
], // ],
]); // ]);
$this->assertCount(1, $documents['body']['documents']); // $this->assertCount(1, $documents['body']['documents']);
$this->assertEquals('Captain America', $documents['body']['documents'][0]['name']); // $this->assertEquals('Captain America', $documents['body']['documents'][0]['name']);
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([ // $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json', // 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], // 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ // ], $this->getHeaders()), [
'filters' => [ // 'filters' => [
'releaseYear!=1944' // 'releaseYear!=1944'
], // ],
]); // ]);
$this->assertCount(2, $documents['body']['documents']); // $this->assertCount(2, $documents['body']['documents']);
$this->assertEquals('Spider-Man: Far From Home', $documents['body']['documents'][0]['name']); // $this->assertEquals('Spider-Man: Far From Home', $documents['body']['documents'][0]['name']);
$this->assertEquals('Spider-Man: Homecoming', $documents['body']['documents'][1]['name']); // $this->assertEquals('Spider-Man: Homecoming', $documents['body']['documents'][1]['name']);
return []; // return [];
} // }
/** /**
* @depends testCreateDocument * @depends testCreateDocument
@ -410,35 +422,34 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'data' => [ 'data' => [
'name' => 'Thor: Ragnaroc', 'title' => 'Thor: Ragnaroc',
'releaseYear' => 2017, 'releaseYear' => 2017,
'actors' => [],
], ],
'read' => ['user:'.$this->getUser()['$id'], 'testx'], 'read' => ['user:'.$this->getUser()['$id'], 'user:testx'],
'write' => ['user:'.$this->getUser()['$id'], 'testy'], 'write' => ['user:'.$this->getUser()['$id'], 'user:testy'],
]); ]);
$id = $document['body']['$id']; $id = $document['body']['$id'];
$collection = $document['body']['$collection']; $collection = $document['body']['$collection'];
$this->assertEquals($document['headers']['status-code'], 201); $this->assertEquals($document['headers']['status-code'], 201);
$this->assertEquals($document['body']['name'], 'Thor: Ragnaroc'); $this->assertEquals($document['body']['title'], 'Thor: Ragnaroc');
$this->assertEquals($document['body']['releaseYear'], 2017); $this->assertEquals($document['body']['releaseYear'], 2017);
$this->assertEquals($document['body']['$permissions']['read'][1], 'testx'); $this->assertEquals($document['body']['$read'][1], 'user:testx');
$this->assertEquals($document['body']['$permissions']['write'][1], 'testy'); $this->assertEquals($document['body']['$write'][1], 'user:testy');
$document = $this->client->call(Client::METHOD_PATCH, '/database/collections/' . $collection . '/documents/' . $id, array_merge([ $document = $this->client->call(Client::METHOD_PATCH, '/database/collections/' . $collection . '/documents/' . $id, array_merge([
'content-type' => 'application/json', 'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'data' => [ 'data' => [
'name' => 'Thor: Ragnarok' 'title' => 'Thor: Ragnarok',
], ],
'read' => ['user:'.$this->getUser()['$id']],
'write' => ['user:'.$this->getUser()['$id']],
]); ]);
$this->assertEquals($document['headers']['status-code'], 200); $this->assertEquals($document['headers']['status-code'], 200);
$this->assertEquals($document['body']['name'], 'Thor: Ragnarok'); $this->assertEquals($document['body']['title'], 'Thor: Ragnarok');
$this->assertEquals($document['body']['releaseYear'], 2017); $this->assertEquals($document['body']['releaseYear'], 2017);
$document = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collection . '/documents/' . $id, array_merge([ $document = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collection . '/documents/' . $id, array_merge([
@ -450,7 +461,7 @@ trait DatabaseBase
$collection = $document['body']['$collection']; $collection = $document['body']['$collection'];
$this->assertEquals($document['headers']['status-code'], 200); $this->assertEquals($document['headers']['status-code'], 200);
$this->assertEquals($document['body']['name'], 'Thor: Ragnarok'); $this->assertEquals($document['body']['title'], 'Thor: Ragnarok');
$this->assertEquals($document['body']['releaseYear'], 2017); $this->assertEquals($document['body']['releaseYear'], 2017);
return []; return [];
@ -466,8 +477,9 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'data' => [ 'data' => [
'name' => 'Thor: Ragnarok', 'title' => 'Thor: Ragnarok',
'releaseYear' => 2017, 'releaseYear' => 2017,
'actors' => [],
], ],
'read' => ['user:'.$this->getUser()['$id']], 'read' => ['user:'.$this->getUser()['$id']],
'write' => ['user:'.$this->getUser()['$id']], 'write' => ['user:'.$this->getUser()['$id']],
@ -512,7 +524,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'data' => [ 'data' => [
'name' => 'Captain America', 'title' => 'Captain America',
'releaseYear' => 1944, 'releaseYear' => 1944,
'actors' => [], 'actors' => [],
], ],
@ -522,24 +534,23 @@ trait DatabaseBase
$this->assertEquals($document['headers']['status-code'], 201); $this->assertEquals($document['headers']['status-code'], 201);
$this->assertEquals($document['body']['$collection'], $data['moviesId']); $this->assertEquals($document['body']['$collection'], $data['moviesId']);
$this->assertEquals($document['body']['name'], 'Captain America'); $this->assertEquals($document['body']['title'], 'Captain America');
$this->assertEquals($document['body']['releaseYear'], 1944); $this->assertEquals($document['body']['releaseYear'], 1944);
$this->assertIsArray($document['body']['$permissions']); $this->assertIsArray($document['body']['$read']);
$this->assertIsArray($document['body']['$permissions']['read']); $this->assertIsArray($document['body']['$write']);
$this->assertIsArray($document['body']['$permissions']['write']);
if($this->getSide() == 'client') { if($this->getSide() == 'client') {
$this->assertCount(1, $document['body']['$permissions']['read']); $this->assertCount(1, $document['body']['$read']);
$this->assertCount(1, $document['body']['$permissions']['write']); $this->assertCount(1, $document['body']['$write']);
$this->assertEquals(['user:'.$this->getUser()['$id']], $document['body']['$permissions']['read']); $this->assertEquals(['user:'.$this->getUser()['$id']], $document['body']['$read']);
$this->assertEquals(['user:'.$this->getUser()['$id']], $document['body']['$permissions']['write']); $this->assertEquals(['user:'.$this->getUser()['$id']], $document['body']['$write']);
} }
if($this->getSide() == 'server') { if($this->getSide() == 'server') {
$this->assertCount(0, $document['body']['$permissions']['read']); $this->assertCount(0, $document['body']['$read']);
$this->assertCount(0, $document['body']['$permissions']['write']); $this->assertCount(0, $document['body']['$write']);
$this->assertEquals([], $document['body']['$permissions']['read']); $this->assertEquals([], $document['body']['$read']);
$this->assertEquals([], $document['body']['$permissions']['write']); $this->assertEquals([], $document['body']['$write']);
} }
// Updated and Inherit Permissions // Updated and Inherit Permissions
@ -549,7 +560,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'data' => [ 'data' => [
'name' => 'Captain America 2', 'title' => 'Captain America 2',
'releaseYear' => 1945, 'releaseYear' => 1945,
'actors' => [], 'actors' => [],
], ],
@ -557,21 +568,21 @@ trait DatabaseBase
]); ]);
$this->assertEquals($document['headers']['status-code'], 200); $this->assertEquals($document['headers']['status-code'], 200);
$this->assertEquals($document['body']['name'], 'Captain America 2'); $this->assertEquals($document['body']['title'], 'Captain America 2');
$this->assertEquals($document['body']['releaseYear'], 1945); $this->assertEquals($document['body']['releaseYear'], 1945);
if($this->getSide() == 'client') { if($this->getSide() == 'client') {
$this->assertCount(1, $document['body']['$permissions']['read']); $this->assertCount(1, $document['body']['$read']);
$this->assertCount(1, $document['body']['$permissions']['write']); $this->assertCount(1, $document['body']['$write']);
$this->assertEquals(['role:all'], $document['body']['$permissions']['read']); $this->assertEquals(['role:all'], $document['body']['$read']);
$this->assertEquals(['user:'.$this->getUser()['$id']], $document['body']['$permissions']['write']); $this->assertEquals(['user:'.$this->getUser()['$id']], $document['body']['$write']);
} }
if($this->getSide() == 'server') { if($this->getSide() == 'server') {
$this->assertCount(1, $document['body']['$permissions']['read']); $this->assertCount(1, $document['body']['$read']);
$this->assertCount(0, $document['body']['$permissions']['write']); $this->assertCount(0, $document['body']['$write']);
$this->assertEquals(['role:all'], $document['body']['$permissions']['read']); $this->assertEquals(['role:all'], $document['body']['$read']);
$this->assertEquals([], $document['body']['$permissions']['write']); $this->assertEquals([], $document['body']['$write']);
} }
$document = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents/' . $id, array_merge([ $document = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents/' . $id, array_merge([
@ -580,21 +591,21 @@ trait DatabaseBase
], $this->getHeaders())); ], $this->getHeaders()));
$this->assertEquals($document['headers']['status-code'], 200); $this->assertEquals($document['headers']['status-code'], 200);
$this->assertEquals($document['body']['name'], 'Captain America 2'); $this->assertEquals($document['body']['title'], 'Captain America 2');
$this->assertEquals($document['body']['releaseYear'], 1945); $this->assertEquals($document['body']['releaseYear'], 1945);
if($this->getSide() == 'client') { if($this->getSide() == 'client') {
$this->assertCount(1, $document['body']['$permissions']['read']); $this->assertCount(1, $document['body']['$read']);
$this->assertCount(1, $document['body']['$permissions']['write']); $this->assertCount(1, $document['body']['$write']);
$this->assertEquals(['role:all'], $document['body']['$permissions']['read']); $this->assertEquals(['role:all'], $document['body']['$read']);
$this->assertEquals(['user:'.$this->getUser()['$id']], $document['body']['$permissions']['write']); $this->assertEquals(['user:'.$this->getUser()['$id']], $document['body']['$write']);
} }
if($this->getSide() == 'server') { if($this->getSide() == 'server') {
$this->assertCount(1, $document['body']['$permissions']['read']); $this->assertCount(1, $document['body']['$read']);
$this->assertCount(0, $document['body']['$permissions']['write']); $this->assertCount(0, $document['body']['$write']);
$this->assertEquals(['role:all'], $document['body']['$permissions']['read']); $this->assertEquals(['role:all'], $document['body']['$read']);
$this->assertEquals([], $document['body']['$permissions']['write']); $this->assertEquals([], $document['body']['$write']);
} }
// Reset Permissions // Reset Permissions
@ -604,7 +615,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'data' => [ 'data' => [
'name' => 'Captain America 3', 'title' => 'Captain America 3',
'releaseYear' => 1946, 'releaseYear' => 1946,
'actors' => [], 'actors' => [],
], ],
@ -618,12 +629,12 @@ trait DatabaseBase
if($this->getSide() == 'server') { if($this->getSide() == 'server') {
$this->assertEquals($document['headers']['status-code'], 200); $this->assertEquals($document['headers']['status-code'], 200);
$this->assertEquals($document['body']['name'], 'Captain America 3'); $this->assertEquals($document['body']['title'], 'Captain America 3');
$this->assertEquals($document['body']['releaseYear'], 1946); $this->assertEquals($document['body']['releaseYear'], 1946);
$this->assertCount(0, $document['body']['$permissions']['read']); $this->assertCount(0, $document['body']['$read']);
$this->assertCount(0, $document['body']['$permissions']['write']); $this->assertCount(0, $document['body']['$write']);
$this->assertEquals([], $document['body']['$permissions']['read']); $this->assertEquals([], $document['body']['$read']);
$this->assertEquals([], $document['body']['$permissions']['write']); $this->assertEquals([], $document['body']['$write']);
} }
return $data; return $data;

View file

@ -27,34 +27,49 @@ class DatabaseCustomServerTest extends Scope
]), [ ]), [
'name' => 'Actors', 'name' => 'Actors',
'read' => ['role:all'], 'read' => ['role:all'],
'write' => ['role:member', 'role:admin'], 'write' => ['role:all'],
'rules' => [
[
'label' => 'First Name',
'key' => 'firstName',
'type' => 'text',
'default' => '',
'required' => true,
'array' => false
],
[
'label' => 'Last Name',
'key' => 'lastName',
'type' => 'text',
'default' => '',
'required' => true,
'array' => false
],
],
]); ]);
$this->assertEquals($actors['headers']['status-code'], 201); $this->assertEquals($actors['headers']['status-code'], 201);
$this->assertEquals($actors['body']['name'], 'Actors'); $this->assertEquals($actors['body']['name'], 'Actors');
$this->assertIsArray($actors['body']['$permissions']);
$this->assertIsArray($actors['body']['$permissions']['read']); $firstName = $this->client->call(Client::METHOD_POST, '/database/collections/' . $actors['body']['$id'] . '/attributes', array_merge([
$this->assertIsArray($actors['body']['$permissions']['write']); 'content-type' => 'application/json',
$this->assertCount(1, $actors['body']['$permissions']['read']); 'x-appwrite-project' => $this->getProject()['$id'],
$this->assertCount(2, $actors['body']['$permissions']['write']); 'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'id' => 'firstName',
'type' => 'string',
'size' => 256,
'required' => true,
]);
$lastName = $this->client->call(Client::METHOD_POST, '/database/collections/' . $actors['body']['$id'] . '/attributes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'id' => 'lastName',
'type' => 'string',
'size' => 256,
'required' => true,
]);
// wait for database worker to finish creating attributes
sleep(5);
$collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $actors['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), []);
$this->assertEquals($collection['body']['$id'], $firstName['body']['$collection']);
$this->assertEquals($collection['body']['$id'], $lastName['body']['$collection']);
$this->assertIsArray($collection['body']['attributes']);
$this->assertCount(2, $collection['body']['attributes']);
$this->assertEquals($collection['body']['attributes'][0]['$id'], $firstName['body']['$id']);
$this->assertEquals($collection['body']['attributes'][1]['$id'], $lastName['body']['$id']);
// Add Documents to the collection // Add Documents to the collection
$document1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $actors['body']['$id'] . '/documents', array_merge([ $document1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $actors['body']['$id'] . '/documents', array_merge([
@ -80,24 +95,22 @@ class DatabaseCustomServerTest extends Scope
'read' => ['user:'.$this->getUser()['$id']], 'read' => ['user:'.$this->getUser()['$id']],
'write' => ['user:'.$this->getUser()['$id']], 'write' => ['user:'.$this->getUser()['$id']],
]); ]);
$this->assertEquals($document1['headers']['status-code'], 201); $this->assertEquals($document1['headers']['status-code'], 201);
$this->assertEquals($document1['body']['$collection'], $actors['body']['$id']); $this->assertEquals($document1['body']['$collection'], $actors['body']['$id']);
$this->assertIsArray($document1['body']['$permissions']); $this->assertIsArray($document1['body']['$read']);
$this->assertIsArray($document1['body']['$permissions']['read']); $this->assertIsArray($document1['body']['$write']);
$this->assertIsArray($document1['body']['$permissions']['write']); $this->assertCount(1, $document1['body']['$read']);
$this->assertCount(1, $document1['body']['$permissions']['read']); $this->assertCount(1, $document1['body']['$write']);
$this->assertCount(1, $document1['body']['$permissions']['write']);
$this->assertEquals($document1['body']['firstName'], 'Tom'); $this->assertEquals($document1['body']['firstName'], 'Tom');
$this->assertEquals($document1['body']['lastName'], 'Holland'); $this->assertEquals($document1['body']['lastName'], 'Holland');
$this->assertEquals($document2['headers']['status-code'], 201); $this->assertEquals($document2['headers']['status-code'], 201);
$this->assertEquals($document2['body']['$collection'], $actors['body']['$id']); $this->assertEquals($document2['body']['$collection'], $actors['body']['$id']);
$this->assertIsArray($document2['body']['$permissions']); $this->assertIsArray($document2['body']['$read']);
$this->assertIsArray($document2['body']['$permissions']['read']); $this->assertIsArray($document2['body']['$write']);
$this->assertIsArray($document2['body']['$permissions']['write']); $this->assertCount(1, $document2['body']['$read']);
$this->assertCount(1, $document2['body']['$permissions']['read']); $this->assertCount(1, $document2['body']['$write']);
$this->assertCount(1, $document2['body']['$permissions']['write']);
$this->assertEquals($document2['body']['firstName'], 'Samuel'); $this->assertEquals($document2['body']['firstName'], 'Samuel');
$this->assertEquals($document2['body']['lastName'], 'Jackson'); $this->assertEquals($document2['body']['lastName'], 'Jackson');

View file

@ -20,24 +20,6 @@ trait WebhooksBase
'name' => 'Actors', 'name' => 'Actors',
'read' => ['role:all'], 'read' => ['role:all'],
'write' => ['role:all'], 'write' => ['role:all'],
'rules' => [
[
'label' => 'First Name',
'key' => 'firstName',
'type' => 'text',
'default' => '',
'required' => true,
'array' => false
],
[
'label' => 'Last Name',
'key' => 'lastName',
'type' => 'text',
'default' => '',
'required' => true,
'array' => false
],
],
]); ]);
$this->assertEquals($actors['headers']['status-code'], 201); $this->assertEquals($actors['headers']['status-code'], 201);
@ -55,12 +37,10 @@ trait WebhooksBase
$this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true); $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true);
$this->assertNotEmpty($webhook['data']['$id']); $this->assertNotEmpty($webhook['data']['$id']);
$this->assertEquals($webhook['data']['name'], 'Actors'); $this->assertEquals($webhook['data']['name'], 'Actors');
$this->assertIsArray($webhook['data']['$permissions']);
$this->assertIsArray($webhook['data']['$read']); $this->assertIsArray($webhook['data']['$read']);
$this->assertIsArray($webhook['data']['$write']); $this->assertIsArray($webhook['data']['$write']);
$this->assertCount(1, $webhook['data']['$read']); $this->assertCount(1, $webhook['data']['$read']);
$this->assertCount(1, $webhook['data']['$write']); $this->assertCount(1, $webhook['data']['$write']);
$this->assertCount(2, $webhook['data']['rules']);
return array_merge(['actorsId' => $actors['body']['$id']]); return array_merge(['actorsId' => $actors['body']['$id']]);
} }
@ -68,6 +48,60 @@ trait WebhooksBase
/** /**
* @depends testCreateCollection * @depends testCreateCollection
*/ */
public function testCreateAttributes(array $data): array
{
$firstName = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/attributes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'id' => 'firstName',
'type' => 'string',
'size' => 256,
'required' => true,
]);
$lastName = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/attributes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'id' => 'lastName',
'type' => 'string',
'size' => 256,
'required' => true,
]);
$this->assertEquals($firstName['headers']['status-code'], 201);
$this->assertEquals($firstName['body']['$collection'], $data['actorsId']);
$this->assertEquals($firstName['body']['$id'], 'firstName');
$this->assertEquals($lastName['headers']['status-code'], 201);
$this->assertEquals($lastName['body']['$collection'], $data['actorsId']);
$this->assertEquals($lastName['body']['$id'], 'lastName');
// wait for database worker to kick in
sleep(10);
$webhook = $this->getLastRequest();
$this->assertEquals($webhook['method'], 'POST');
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Event'], 'database.attributes.create');
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], 'not-yet-implemented');
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
$this->assertNotEmpty($webhook['data']['$id']);
$this->assertEquals($webhook['data']['$id'], 'lastName');
// TODO@kodumbeats test webhook for removing attribute
return $data;
}
/**
* @depends testCreateAttributes
*/
public function testCreateDocument(array $data): array public function testCreateDocument(array $data): array
{ {
$document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/documents', array_merge([ $document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/documents', array_merge([

View file

@ -15,7 +15,7 @@ class WebhooksCustomServerTest extends Scope
use SideServer; use SideServer;
/** /**
* @depends testCreateCollection * @depends testCreateAttributes
*/ */
public function testUpdateCollection($data): array public function testUpdateCollection($data): array
{ {
@ -28,26 +28,6 @@ class WebhooksCustomServerTest extends Scope
'x-appwrite-key' => $this->getProject()['apiKey'] 'x-appwrite-key' => $this->getProject()['apiKey']
]), [ ]), [
'name' => 'Actors1', 'name' => 'Actors1',
'read' => ['role:all'],
'write' => ['role:all'],
'rules' => [
[
'label' => 'First Name',
'key' => 'firstName',
'type' => 'text',
'default' => '',
'required' => true,
'array' => false
],
[
'label' => 'Last Name',
'key' => 'lastName',
'type' => 'text',
'default' => '',
'required' => true,
'array' => false
],
],
]); ]);
$this->assertEquals($actors['headers']['status-code'], 200); $this->assertEquals($actors['headers']['status-code'], 200);
@ -65,16 +45,73 @@ class WebhooksCustomServerTest extends Scope
$this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true); $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true);
$this->assertNotEmpty($webhook['data']['$id']); $this->assertNotEmpty($webhook['data']['$id']);
$this->assertEquals($webhook['data']['name'], 'Actors1'); $this->assertEquals($webhook['data']['name'], 'Actors1');
$this->assertIsArray($webhook['data']['$permissions']); $this->assertIsArray($webhook['data']['$read']);
$this->assertIsArray($webhook['data']['$permissions']['read']); $this->assertIsArray($webhook['data']['$write']);
$this->assertIsArray($webhook['data']['$permissions']['write']); $this->assertCount(1, $webhook['data']['$read']);
$this->assertCount(1, $webhook['data']['$permissions']['read']); $this->assertCount(1, $webhook['data']['$write']);
$this->assertCount(1, $webhook['data']['$permissions']['write']);
$this->assertCount(2, $webhook['data']['rules']);
return array_merge(['actorsId' => $actors['body']['$id']]); return array_merge(['actorsId' => $actors['body']['$id']]);
} }
/**
* @depends testCreateAttributes
*/
public function testCreateDeleteIndexes($data): array
{
$index = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/indexes', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'id' => 'fullname',
'type' => 'text',
'attributes' => ['lastName', 'firstName'],
'orders' => ['ASC', 'ASC'],
]);
$this->assertEquals($index['headers']['status-code'], 201);
$this->assertEquals($index['body']['$collection'], $data['actorsId']);
$this->assertEquals($index['body']['$id'], 'fullname');
// wait for database worker to create index
sleep(5);
$webhook = $this->getLastRequest();
$this->assertEquals($webhook['method'], 'POST');
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Event'], 'database.indexes.create');
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], 'not-yet-implemented');
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
$this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true);
// TODO@kodumbeats test for indexes.delete
// Remove index
// $index = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['actorsId'] . '/indexes/' . $index['body']['$id'], array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// 'x-appwrite-key' => $this->getProject()['apiKey']
// ]));
// // wait for database worker to remove index
// sleep(5);
// $webhook = $this->getLastRequest();
// // $this->assertEquals($webhook['method'], 'DELETE');
// $this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
// $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
// $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Event'], 'database.indexes.delete');
// $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], 'not-yet-implemented');
// $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
// $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
// $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true);
return $data;
}
public function testDeleteCollection(): array public function testDeleteCollection(): array
{ {
/** /**
@ -88,24 +125,6 @@ class WebhooksCustomServerTest extends Scope
'name' => 'Demo', 'name' => 'Demo',
'read' => ['role:all'], 'read' => ['role:all'],
'write' => ['role:all'], 'write' => ['role:all'],
'rules' => [
[
'label' => 'First Name',
'key' => 'firstName',
'type' => 'text',
'default' => '',
'required' => true,
'array' => false
],
[
'label' => 'Last Name',
'key' => 'lastName',
'type' => 'text',
'default' => '',
'required' => true,
'array' => false
],
],
]); ]);
$this->assertEquals($actors['headers']['status-code'], 201); $this->assertEquals($actors['headers']['status-code'], 201);
@ -131,12 +150,10 @@ class WebhooksCustomServerTest extends Scope
$this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true); $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true);
$this->assertNotEmpty($webhook['data']['$id']); $this->assertNotEmpty($webhook['data']['$id']);
$this->assertEquals($webhook['data']['name'], 'Demo'); $this->assertEquals($webhook['data']['name'], 'Demo');
$this->assertIsArray($webhook['data']['$permissions']); $this->assertIsArray($webhook['data']['$read']);
$this->assertIsArray($webhook['data']['$permissions']['read']); $this->assertIsArray($webhook['data']['$write']);
$this->assertIsArray($webhook['data']['$permissions']['write']); $this->assertCount(1, $webhook['data']['$read']);
$this->assertCount(1, $webhook['data']['$permissions']['read']); $this->assertCount(1, $webhook['data']['$write']);
$this->assertCount(1, $webhook['data']['$permissions']['write']);
$this->assertCount(2, $webhook['data']['rules']);
return []; return [];
} }
@ -306,8 +323,8 @@ class WebhooksCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [ ], $this->getHeaders()), [
'name' => 'Test', 'name' => 'Test',
'env' => 'php-8.0', 'execute' => ['role:all'],
'runtime' => ['role:all'], 'runtime' => 'php-8.0',
'timeout' => 10, 'timeout' => 10,
]); ]);