From 3172f3073c27050d2226df6c7feabaf9cf212567 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 13 Aug 2021 16:57:08 -0400 Subject: [PATCH 001/130] Split attribute model into primitive types --- .../Utopia/Response/Model/Attribute.php | 21 -------- .../Utopia/Response/Model/AttributeFloat.php | 51 +++++++++++++++++++ .../Response/Model/AttributeInteger.php | 51 +++++++++++++++++++ .../Utopia/Response/Model/AttributeString.php | 41 +++++++++++++++ 4 files changed, 143 insertions(+), 21 deletions(-) create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeFloat.php create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeInteger.php create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeString.php diff --git a/src/Appwrite/Utopia/Response/Model/Attribute.php b/src/Appwrite/Utopia/Response/Model/Attribute.php index b8763be6f..c25b21da5 100644 --- a/src/Appwrite/Utopia/Response/Model/Attribute.php +++ b/src/Appwrite/Utopia/Response/Model/Attribute.php @@ -28,25 +28,12 @@ class Attribute extends Model '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?', @@ -54,14 +41,6 @@ class Attribute extends Model 'example' => false, 'required' => false ]) - ->addRule('filters', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute filters.', - 'default' => [], - 'example' => [], - 'array' => true, - 'required' => false, - ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php new file mode 100644 index 000000000..bf9ca0f10 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php @@ -0,0 +1,51 @@ +addRule('min', [ + 'type' => self::TYPE_FLOAT, + 'description' => 'Minimum value to enforce on new documents.', + 'default' => null, + 'example' => 0.5, + 'array' => false, + 'required' => false, + ]) + ->addRule('max', [ + 'type' => self::TYPE_FLOAT, + 'description' => 'Minimum value to enforce on new documents.', + 'default' => null, + 'example' => 2.5, + 'array' => false, + 'required' => false, + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'Float'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType():string + { + return Response::MODEL_FLOAT; + } +} \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php new file mode 100644 index 000000000..5a5ec896c --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php @@ -0,0 +1,51 @@ +addRule('min', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Minimum value to enforce on new documents.', + 'default' => null, + 'example' => 0, + 'array' => false, + 'required' => false, + ]) + ->addRule('max', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Minimum value to enforce on new documents.', + 'default' => null, + 'example' => 10, + 'array' => false, + 'required' => false, + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'Integer'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType():string + { + return Response::MODEL_INTEGER; + } +} \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/AttributeString.php b/src/Appwrite/Utopia/Response/Model/AttributeString.php new file mode 100644 index 000000000..c12de4395 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeString.php @@ -0,0 +1,41 @@ +addRule('size', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute size.', + 'default' => 0, + 'example' => 128, + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'String'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType():string + { + return Response::MODEL_ATTRIBUTE_STRING; + } +} \ No newline at end of file From 20f5f03173ae67a05bc96bebdcabbc4ea888b038 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 13 Aug 2021 16:58:36 -0400 Subject: [PATCH 002/130] Create formatted string attribute models --- src/Appwrite/Utopia/Response/Model/Email.php | 43 ++++++++++++++++++++ src/Appwrite/Utopia/Response/Model/IP.php | 43 ++++++++++++++++++++ src/Appwrite/Utopia/Response/Model/URL.php | 43 ++++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 src/Appwrite/Utopia/Response/Model/Email.php create mode 100644 src/Appwrite/Utopia/Response/Model/IP.php create mode 100644 src/Appwrite/Utopia/Response/Model/URL.php diff --git a/src/Appwrite/Utopia/Response/Model/Email.php b/src/Appwrite/Utopia/Response/Model/Email.php new file mode 100644 index 000000000..e876019f8 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/Email.php @@ -0,0 +1,43 @@ +addRule('format', [ + 'type' => self::TYPE_STRING, + 'description' => 'String format.', + 'default' => 'email', + 'example' => 'email', + 'array' => false, + 'required' => true, + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'Email'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType():string + { + return Response::MODEL_EMAIL; + } +} \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/IP.php b/src/Appwrite/Utopia/Response/Model/IP.php new file mode 100644 index 000000000..e81805550 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/IP.php @@ -0,0 +1,43 @@ +addRule('format', [ + 'type' => self::TYPE_STRING, + 'description' => 'String format.', + 'default' => 'ip', + 'example' => 'ip', + 'array' => false, + 'required' => true, + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'IP'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType():string + { + return Response::MODEL_IP; + } +} \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/URL.php b/src/Appwrite/Utopia/Response/Model/URL.php new file mode 100644 index 000000000..37340ab61 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/URL.php @@ -0,0 +1,43 @@ +addRule('format', [ + 'type' => self::TYPE_STRING, + 'description' => 'String format.', + 'default' => 'url', + 'example' => 'url', + 'array' => false, + 'required' => true, + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'URL'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType():string + { + return Response::MODEL_URL; + } +} \ No newline at end of file From 9c3f1988bbcbf8c46783e77c64fd2efa03387c6c Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 13 Aug 2021 16:58:54 -0400 Subject: [PATCH 003/130] Instantiate new attribute models, constants, and lists --- src/Appwrite/Utopia/Response.php | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 3cd25b603..b31297262 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -11,6 +11,9 @@ use Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response\Model\None; use Appwrite\Utopia\Response\Model\Any; use Appwrite\Utopia\Response\Model\Attribute; +use Appwrite\Utopia\Response\Model\AttributeString; +use Appwrite\Utopia\Response\Model\AttributeInteger; +use Appwrite\Utopia\Response\Model\AttributeFloat; use Appwrite\Utopia\Response\Model\BaseList; use Appwrite\Utopia\Response\Model\Collection; use Appwrite\Utopia\Response\Model\Continent; @@ -18,12 +21,14 @@ use Appwrite\Utopia\Response\Model\Country; use Appwrite\Utopia\Response\Model\Currency; use Appwrite\Utopia\Response\Model\Document as ModelDocument; use Appwrite\Utopia\Response\Model\Domain; +use Appwrite\Utopia\Response\Model\Email; use Appwrite\Utopia\Response\Model\Error; use Appwrite\Utopia\Response\Model\ErrorDev; use Appwrite\Utopia\Response\Model\Execution; use Appwrite\Utopia\Response\Model\File; use Appwrite\Utopia\Response\Model\Func; use Appwrite\Utopia\Response\Model\Index; +use Appwrite\Utopia\Response\Model\IP; use Appwrite\Utopia\Response\Model\JWT; use Appwrite\Utopia\Response\Model\Key; use Appwrite\Utopia\Response\Model\Language; @@ -40,6 +45,7 @@ use Appwrite\Utopia\Response\Model\Project; use Appwrite\Utopia\Response\Model\Rule; use Appwrite\Utopia\Response\Model\Tag; use Appwrite\Utopia\Response\Model\Token; +use Appwrite\Utopia\Response\Model\URL; use Appwrite\Utopia\Response\Model\Webhook; use Appwrite\Utopia\Response\Model\Preferences; use Appwrite\Utopia\Response\Model\Mock; // Keep last @@ -62,13 +68,27 @@ class Response extends SwooleResponse // Database const MODEL_COLLECTION = 'collection'; 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_DOCUMENT = 'document'; const MODEL_DOCUMENT_LIST = 'documentList'; + // Database Attributes + const MODEL_ATTRIBUTE = 'attribute'; + const MODEL_ATTRIBUTE_LIST = 'attributeList'; + const MODEL_ATTRIBUTE_STRING = 'attributeString'; + const MODEL_ATTRIBUTE_STRING_LIST = 'attributeStringList'; + const MODEL_INTEGER= 'integer'; + const MODEL_INTEGER_LIST= 'integerList'; + const MODEL_FLOAT= 'float'; + const MODEL_FLOAT_LIST= 'floatList'; + const MODEL_EMAIL= 'email'; + const MODEL_EMAIL_LIST= 'emailList'; + const MODEL_IP= 'ip'; + const MODEL_IP_LIST= 'ipList'; + const MODEL_URL= 'url'; + const MODEL_URL_LIST= 'urlList'; + // Users const MODEL_USER = 'user'; const MODEL_USER_LIST = 'userList'; @@ -179,6 +199,12 @@ class Response extends SwooleResponse // Entities ->setModel(new Collection()) ->setModel(new Attribute()) + ->setModel(new AttributeString()) + ->setModel(new AttributeInteger()) + ->setModel(new AttributeFloat()) + ->setModel(new Email()) + ->setModel(new IP()) + ->setModel(new URL()) ->setModel(new Index()) ->setModel(new ModelDocument()) ->setModel(new Log()) From a7c4edba9533d2ece4611d694ac1c247846281ad Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sat, 14 Aug 2021 21:56:28 +0300 Subject: [PATCH 004/130] Updated collection structures to support fulltext search --- app/config/collections2.php | 106 ++++++++++++++++++++++++++---- app/controllers/api/account.php | 36 +++++----- app/controllers/api/database.php | 6 +- app/controllers/api/functions.php | 21 +++++- app/controllers/api/projects.php | 23 ++++--- app/controllers/api/storage.php | 12 +++- app/controllers/api/teams.php | 13 +++- app/controllers/api/users.php | 17 +++-- 8 files changed, 181 insertions(+), 53 deletions(-) diff --git a/app/config/collections2.php b/app/config/collections2.php index ae9a4feeb..d75775232 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -221,13 +221,24 @@ $collections = [ 'array' => true, 'filters' => ['json'], ], + [ + '$id' => 'search', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], ], 'indexes' => [ [ - '$id' => '_fulltext_name', + '$id' => '_key_search', 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['name'], - 'lengths' => [1024], + 'attributes' => ['search'], + 'lengths' => [2048], 'orders' => [Database::ORDER_ASC], ], ], @@ -370,6 +381,17 @@ $collections = [ 'array' => true, 'filters' => ['json'], ], + [ + '$id' => 'search', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], ], 'indexes' => [ [ @@ -379,6 +401,13 @@ $collections = [ 'lengths' => [1024], 'orders' => [Database::ORDER_ASC], ], + [ + '$id' => '_key_search', + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [2048], + 'orders' => [Database::ORDER_ASC], + ], ], ], @@ -668,13 +697,24 @@ $collections = [ 'array' => false, 'filters' => [], ], + [ + '$id' => 'search', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], ], 'indexes' => [ [ - '$id' => '_fulltext_name', + '$id' => '_key_search', 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['name'], - 'lengths' => [1024], + 'attributes' => ['search'], + 'lengths' => [2048], 'orders' => [Database::ORDER_ASC], ], ], @@ -948,6 +988,17 @@ $collections = [ 'array' => false, 'filters' => [], ], + [ + '$id' => 'search', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], ], 'indexes' => [ [ @@ -958,10 +1009,10 @@ $collections = [ 'orders' => [Database::ORDER_ASC], ], [ - '$id' => '_fulltext_name', + '$id' => '_key_search', 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['name'], - 'lengths' => [1024], + 'attributes' => ['search'], + 'lengths' => [2048], 'orders' => [Database::ORDER_ASC], ], ], @@ -1116,13 +1167,24 @@ $collections = [ 'array' => false, 'filters' => [], ], + [ + '$id' => 'search', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], ], 'indexes' => [ [ - '$id' => '_fulltext_name', + '$id' => '_key_search', 'type' => Database::INDEX_FULLTEXT, - 'attributes' => ['name'], - 'lengths' => [1024], + 'attributes' => ['search'], + 'lengths' => [2048], 'orders' => [Database::ORDER_ASC], ], ], @@ -1189,6 +1251,17 @@ $collections = [ 'array' => false, 'filters' => [], ], + [ + '$id' => 'search', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], ], 'indexes' => [ [ @@ -1198,6 +1271,13 @@ $collections = [ 'lengths' => [Database::LENGTH_KEY], 'orders' => [Database::ORDER_ASC], ], + [ + '$id' => '_key_search', + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [2048], + 'orders' => [Database::ORDER_ASC], + ], ], ], @@ -1405,4 +1485,4 @@ $collections = [ ], ]; -return $collections; +return $collections; \ No newline at end of file diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index ef0880511..11649966d 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -103,6 +103,7 @@ App::post('/v1/account') 'sessions' => [], 'tokens' => [], 'memberships' => [], + 'search' => implode(' ', [$userId, $email, $name]), ])); } catch (Duplicate $th) { throw new Exception('Account already exists', 409); @@ -195,8 +196,8 @@ App::post('/v1/account/sessions') Authorization::setRole('user:' . $profile->getId()); $session = $dbForInternal->createDocument('sessions', $session - ->setAttribute('$read', ['user:' . $profile->getId()]) - ->setAttribute('$write', ['user:' . $profile->getId()]) + ->setAttribute('$read', ['user:' . $profile->getId()]) + ->setAttribute('$write', ['user:' . $profile->getId()]) ); $profile->setAttribute('sessions', $session, Document::SET_TYPE_APPEND); @@ -481,6 +482,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'sessions' => [], 'tokens' => [], 'memberships' => [], + 'search' => implode(' ', [$userId, $email, $name]), ])); } catch (Duplicate $th) { throw new Exception('Account already exists', 409); @@ -530,8 +532,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') Authorization::setRole('user:' . $user->getId()); $session = $dbForInternal->createDocument('sessions', $session - ->setAttribute('$read', ['user:' . $user->getId()]) - ->setAttribute('$write', ['user:' . $user->getId()]) + ->setAttribute('$read', ['user:' . $user->getId()]) + ->setAttribute('$write', ['user:' . $user->getId()]) ); $user = $dbForInternal->updateDocument('users', $user->getId(), $user); @@ -644,6 +646,7 @@ App::post('/v1/account/sessions/anonymous') 'sessions' => [], 'tokens' => [], 'memberships' => [], + 'search' => $userId, ])); Authorization::reset(); @@ -978,7 +981,10 @@ App::patch('/v1/account/name') /** @var Utopia\Database\Database $dbForInternal */ /** @var Appwrite\Event\Event $audits */ - $user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('name', $name)); + $user = $dbForInternal->updateDocument('users', $user->getId(), $user + ->setAttribute('name', $name) + ->setAttribute('search', implode(' ', [$user->getId(), $name, $user->getAttribute('email')])) + ); $audits ->setParam('userId', $user->getId()) @@ -1073,9 +1079,10 @@ App::patch('/v1/account/email') } $user = $dbForInternal->updateDocument('users', $user->getId(), $user - ->setAttribute('password', $isAnonymousUser ? Auth::passwordHash($password) : $user->getAttribute('password', '')) - ->setAttribute('email', $email) - ->setAttribute('emailVerification', false) // After this user needs to confirm mail again + ->setAttribute('password', $isAnonymousUser ? Auth::passwordHash($password) : $user->getAttribute('password', '')) + ->setAttribute('email', $email) + ->setAttribute('emailVerification', false) // After this user needs to confirm mail again + ->setAttribute('search', implode(' ', [$user->getId(), $user->getAttribute('name'), $user->getAttribute('email')])) ); $audits @@ -1489,10 +1496,9 @@ App::put('/v1/account/recovery') ); /** - * We act like we're updating and validating - * the recovery token but actually we don't need it anymore. - */ - + * We act like we're updating and validating + * the recovery token but actually we don't need it anymore. + */ foreach ($tokens as $key => $token) { if ($recovery === $token->getId()) { $recovery = $token; @@ -1650,9 +1656,9 @@ App::put('/v1/account/verification') $profile = $dbForInternal->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true)); /** - * We act like we're updating and validating - * the verification token but actually we don't need it anymore. - */ + * We act like we're updating and validating + * the verification token but actually we don't need it anymore. + */ foreach ($tokens as $key => $token) { if ($token->getId() === $verification) { $verification = $token; diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 1ebb857da..05f74c94f 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -189,7 +189,11 @@ App::get('/v1/database/collections') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForExternal */ - $queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : []; + $queries = []; + + if (!empty($search)) { + $queries[] = new Query('name', Query::TYPE_SEARCH, [$search]); + } $response->dynamic(new Document([ 'collections' => $dbForExternal->find(Database::COLLECTIONS, $queries, $limit, $offset, ['_id'], [$orderType]), diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index a0bcb535b..9994c9eaa 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -53,8 +53,9 @@ App::post('/v1/functions') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ + $functionId = ($functionId == 'unique()') ? $dbForInternal->getId() : $functionId; $function = $dbForInternal->createDocument('functions', new Document([ - '$id' => $functionId == 'unique()' ? $dbForInternal->getId() : $functionId, + '$id' => $functionId, 'execute' => $execute, 'dateCreated' => time(), 'dateUpdated' => time(), @@ -68,6 +69,7 @@ App::post('/v1/functions') 'schedulePrevious' => 0, 'scheduleNext' => 0, 'timeout' => $timeout, + 'search' => implode(' ', [$functionId, $name, $runtime]), ])); $response->setStatusCode(Response::STATUS_CODE_CREATED); @@ -95,7 +97,11 @@ App::get('/v1/functions') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : []; + $queries = []; + + if (!empty($search)) { + $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); + } $response->dynamic(new Document([ 'functions' => $dbForInternal->find('functions', $queries, $limit, $offset, ['_id'], [$orderType]), @@ -296,6 +302,7 @@ App::put('/v1/functions/:functionId') 'schedule' => $schedule, 'scheduleNext' => (int)$next, 'timeout' => $timeout, + 'search' => implode(' ', [$functionId, $name, $function->getAttribute('runtime')]), ]))); if ($next && $schedule !== $original) { @@ -472,8 +479,9 @@ App::post('/v1/functions/:functionId/tags') throw new Exception('Failed moving file', 500); } + $tagId = $dbForInternal->getId(); $tag = $dbForInternal->createDocument('tags', new Document([ - '$id' => $dbForInternal->getId(), + '$id' => $tagId, '$read' => [], '$write' => [], 'functionId' => $function->getId(), @@ -481,6 +489,7 @@ App::post('/v1/functions/:functionId/tags') 'command' => $command, 'path' => $path, 'size' => $size, + 'search' => implode(' ', [$tagId, $command]), ])); $usage @@ -519,6 +528,12 @@ App::get('/v1/functions/:functionId/tags') throw new Exception('Function not found', 404); } + $queries = []; + + if (!empty($search)) { + $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); + } + $queries[] = new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]); $results = $dbForInternal->find('tags', $queries, $limit, $offset, ['_id'], [$orderType]); diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 493eb65dc..dfce1525d 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -5,9 +5,7 @@ use Appwrite\Database\Validator\CustomId; use Appwrite\Network\Validator\CNAME; use Appwrite\Network\Validator\Domain as DomainValidator; use Appwrite\Network\Validator\URL; -use Appwrite\Task\Validator\Cron; use Appwrite\Utopia\Response; -use Cron\CronExpression; use Utopia\App; use Utopia\Config\Config; use Utopia\Database\Document; @@ -78,8 +76,9 @@ App::post('/v1/projects') $auths[$method['key'] ?? ''] = true; } + $projectId = ($projectId == 'unique()') ? $dbForConsole->getId() : $projectId; $project = $dbForConsole->createDocument('projects', new Document([ - '$id' => $projectId == 'unique()' ? $dbForConsole->getId() : $projectId, + '$id' => $projectId, '$collection' => 'projects', '$read' => ['team:' . $teamId], '$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'], @@ -95,12 +94,13 @@ App::post('/v1/projects') 'legalCity' => $legalCity, 'legalAddress' => $legalAddress, 'legalTaxId' => $legalTaxId, + 'auths' => $auths, 'services' => new stdClass(), 'platforms' => [], 'webhooks' => [], 'keys' => [], 'domains' => [], - 'auths' => $auths, + 'search' => implode(' ', [$projectId, $name]), ])); $collections = Config::getParam('collections2', []); /** @var array $collections */ @@ -171,7 +171,11 @@ App::get('/v1/projects') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForConsole */ - $queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : []; + $queries = []; + + if (!empty($search)) { + $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); + } $results = $dbForConsole->find('projects', $queries, $limit, $offset, ['_id'], [$orderType]); $sum = $dbForConsole->count('projects', $queries, APP_LIMIT_COUNT); @@ -445,6 +449,7 @@ App::patch('/v1/projects/:projectId') ->setAttribute('legalCity', $legalCity) ->setAttribute('legalAddress', $legalAddress) ->setAttribute('legalTaxId', $legalTaxId) + ->setAttribute('search', implode(' ', [$projectId, $name])) ); $response->dynamic($project, Response::MODEL_PROJECT); @@ -546,7 +551,7 @@ App::patch('/v1/projects/:projectId/auth/limit') $auths['limit'] = $limit; $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('auths', $auths) + ->setAttribute('auths', $auths) ); $response->dynamic($project, Response::MODEL_PROJECT); @@ -869,7 +874,7 @@ App::post('/v1/projects/:projectId/keys') ]); $project = $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('keys', $key, Document::SET_TYPE_APPEND) + ->setAttribute('keys', $key, Document::SET_TYPE_APPEND) ); $response->setStatusCode(Response::STATUS_CODE_CREATED); @@ -1053,7 +1058,7 @@ App::post('/v1/projects/:projectId/platforms') ]); $project = $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('platforms', $platform, Document::SET_TYPE_APPEND) + ->setAttribute('platforms', $platform, Document::SET_TYPE_APPEND) ); $response->setStatusCode(Response::STATUS_CODE_CREATED); @@ -1262,7 +1267,7 @@ App::post('/v1/projects/:projectId/domains') ]); $project = $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('domains', $domain, Document::SET_TYPE_APPEND) + ->setAttribute('domains', $domain, Document::SET_TYPE_APPEND) ); $response->setStatusCode(Response::STATUS_CODE_CREATED); diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 4f579a1b3..50e1f4d43 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -123,13 +123,14 @@ App::post('/v1/storage/files') $sizeActual = $device->getFileSize($path); + $fileId = ($fileId == 'unique()') ? $dbForInternal->getId() : $fileId; $file = $dbForInternal->createDocument('files', new Document([ - '$id' => $fileId == 'unique()' ? $dbForInternal->getId() : $fileId, + '$id' => $fileId, '$read' => (is_null($read) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $read ?? [], // By default set read permissions for user '$write' => (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? [], // By default set write permissions for user 'dateCreated' => \time(), 'bucketId' => '', - 'name' => $file['name'], + 'name' => $file['name'] ?? '', 'path' => $path, 'signature' => $device->getFileHash($path), 'mimeType' => $mimeType, @@ -141,6 +142,7 @@ App::post('/v1/storage/files') 'openSSLCipher' => OpenSSL::CIPHER_AES_128_GCM, 'openSSLTag' => \bin2hex($tag), 'openSSLIV' => \bin2hex($iv), + 'search' => implode(' ', [$fileId, $file['name'] ?? '',]), ])); $audits @@ -178,7 +180,11 @@ App::get('/v1/storage/files') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, $search)] : []; + $queries = []; + + if (!empty($search)) { + $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); + } $response->dynamic(new Document([ 'files' => $dbForInternal->find('files', $queries, $limit, $offset, ['_id'], [$orderType]), diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 01ce3c82f..5e9deedc6 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -57,6 +57,7 @@ App::post('/v1/teams') 'name' => $name, 'sum' => ($isPrivilegedUser || $isAppUser) ? 0 : 1, 'dateCreated' => \time(), + 'search' => implode(' ', [$teamId, $name]), ])); Authorization::reset(); @@ -106,7 +107,11 @@ App::get('/v1/teams') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : []; + $queries = []; + + if (!empty($search)) { + $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); + } $results = $dbForInternal->find('teams', $queries, $limit, $offset, ['_id'], [$orderType]); $sum = $dbForInternal->count('teams', $queries, APP_LIMIT_COUNT); @@ -170,7 +175,10 @@ App::put('/v1/teams/:teamId') throw new Exception('Team not found', 404); } - $team = $dbForInternal->updateDocument('teams', $team->getId(), $team->setAttribute('name', $name)); + $team = $dbForInternal->updateDocument('teams', $team->getId(),$team + ->setAttribute('name', $name) + ->setAttribute('search', implode(' ', [$teamId, $name])) + ); $response->dynamic($team, Response::MODEL_TEAM); }); @@ -314,6 +322,7 @@ App::post('/v1/teams/:teamId/memberships') 'sessions' => [], 'tokens' => [], 'memberships' => [], + 'search' => implode(' ', [$userId, $email, $name]), ])); } catch (Duplicate $th) { throw new Exception('Account already exists', 409); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index d57be233e..c2c7a9bb3 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -17,6 +17,7 @@ use Utopia\Database\Exception\Duplicate; use Utopia\Database\Validator\UID; use DeviceDetector\DeviceDetector; use Appwrite\Database\Validator\CustomId; +use Utopia\Database\Query; App::post('/v1/users') ->desc('Create User') @@ -60,6 +61,7 @@ App::post('/v1/users') 'sessions' => [], 'tokens' => [], 'memberships' => [], + 'search' => implode(' ', [$userId, $email, $name]), ])); } catch (Duplicate $th) { throw new Exception('Account already exists', 409); @@ -90,8 +92,14 @@ App::get('/v1/users') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $results = $dbForInternal->find('users', [], $limit, $offset, ['_id'], [$orderType]); - $sum = $dbForInternal->count('users', [], APP_LIMIT_COUNT); + $queries = []; + + if (!empty($search)) { + $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); + } + + $results = $dbForInternal->find('users', $queries, $limit, $offset, ['_id'], [$orderType]); + $sum = $dbForInternal->count('users', $queries, APP_LIMIT_COUNT); $response->dynamic(new Document([ 'users' => $results, @@ -519,11 +527,6 @@ App::delete('/v1/users/:userId') if (!$dbForInternal->deleteDocument('users', $userId)) { throw new Exception('Failed to remove user from DB', 500); } - - // $dbForInternal->createDocument('users', new Document([ - // '$id' => $userId, - // '$read' => ['role:all'], - // ])); $deletes ->setParam('type', DELETE_TYPE_DOCUMENT) From 6948a75133b136cbd07733b0aa3bd361431ac727 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 16 Aug 2021 15:54:25 -0400 Subject: [PATCH 005/130] Prefix custom string models with Attribute --- src/Appwrite/Utopia/Response.php | 18 +++++++++--------- .../Model/{Email.php => AttributeEmail.php} | 6 +++--- .../Response/Model/{IP.php => AttributeIP.php} | 6 +++--- .../Model/{URL.php => AttributeURL.php} | 6 +++--- 4 files changed, 18 insertions(+), 18 deletions(-) rename src/Appwrite/Utopia/Response/Model/{Email.php => AttributeEmail.php} (85%) rename src/Appwrite/Utopia/Response/Model/{IP.php => AttributeIP.php} (86%) rename src/Appwrite/Utopia/Response/Model/{URL.php => AttributeURL.php} (85%) diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index b31297262..297b9f530 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -14,6 +14,9 @@ use Appwrite\Utopia\Response\Model\Attribute; use Appwrite\Utopia\Response\Model\AttributeString; use Appwrite\Utopia\Response\Model\AttributeInteger; use Appwrite\Utopia\Response\Model\AttributeFloat; +use Appwrite\Utopia\Response\Model\AttributeEmail; +use Appwrite\Utopia\Response\Model\AttributeIP; +use Appwrite\Utopia\Response\Model\AttributeURL; use Appwrite\Utopia\Response\Model\BaseList; use Appwrite\Utopia\Response\Model\Collection; use Appwrite\Utopia\Response\Model\Continent; @@ -21,14 +24,12 @@ use Appwrite\Utopia\Response\Model\Country; use Appwrite\Utopia\Response\Model\Currency; use Appwrite\Utopia\Response\Model\Document as ModelDocument; use Appwrite\Utopia\Response\Model\Domain; -use Appwrite\Utopia\Response\Model\Email; use Appwrite\Utopia\Response\Model\Error; use Appwrite\Utopia\Response\Model\ErrorDev; use Appwrite\Utopia\Response\Model\Execution; use Appwrite\Utopia\Response\Model\File; use Appwrite\Utopia\Response\Model\Func; use Appwrite\Utopia\Response\Model\Index; -use Appwrite\Utopia\Response\Model\IP; use Appwrite\Utopia\Response\Model\JWT; use Appwrite\Utopia\Response\Model\Key; use Appwrite\Utopia\Response\Model\Language; @@ -45,7 +46,6 @@ use Appwrite\Utopia\Response\Model\Project; use Appwrite\Utopia\Response\Model\Rule; use Appwrite\Utopia\Response\Model\Tag; use Appwrite\Utopia\Response\Model\Token; -use Appwrite\Utopia\Response\Model\URL; use Appwrite\Utopia\Response\Model\Webhook; use Appwrite\Utopia\Response\Model\Preferences; use Appwrite\Utopia\Response\Model\Mock; // Keep last @@ -82,11 +82,11 @@ class Response extends SwooleResponse const MODEL_INTEGER_LIST= 'integerList'; const MODEL_FLOAT= 'float'; const MODEL_FLOAT_LIST= 'floatList'; - const MODEL_EMAIL= 'email'; + const MODEL_ATTRIBUTE_EMAIL= 'email'; const MODEL_EMAIL_LIST= 'emailList'; - const MODEL_IP= 'ip'; + const MODEL_ATTRIBUTE_IP= 'ip'; const MODEL_IP_LIST= 'ipList'; - const MODEL_URL= 'url'; + const MODEL_ATTRIBUTE_URL= 'url'; const MODEL_URL_LIST= 'urlList'; // Users @@ -202,9 +202,9 @@ class Response extends SwooleResponse ->setModel(new AttributeString()) ->setModel(new AttributeInteger()) ->setModel(new AttributeFloat()) - ->setModel(new Email()) - ->setModel(new IP()) - ->setModel(new URL()) + ->setModel(new AttributeEmail()) + ->setModel(new AttributeIP()) + ->setModel(new AttributeURL()) ->setModel(new Index()) ->setModel(new ModelDocument()) ->setModel(new Log()) diff --git a/src/Appwrite/Utopia/Response/Model/Email.php b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php similarity index 85% rename from src/Appwrite/Utopia/Response/Model/Email.php rename to src/Appwrite/Utopia/Response/Model/AttributeEmail.php index e876019f8..b0ea0d730 100644 --- a/src/Appwrite/Utopia/Response/Model/Email.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php @@ -5,7 +5,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model\AttributeString; -class Email extends AttributeString +class AttributeEmail extends AttributeString { public function __construct() { @@ -28,7 +28,7 @@ class Email extends AttributeString */ public function getName():string { - return 'Email'; + return 'AttributeEmail'; } /** @@ -38,6 +38,6 @@ class Email extends AttributeString */ public function getType():string { - return Response::MODEL_EMAIL; + return Response::MODEL_ATTRIBUTE_EMAIL; } } \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/IP.php b/src/Appwrite/Utopia/Response/Model/AttributeIP.php similarity index 86% rename from src/Appwrite/Utopia/Response/Model/IP.php rename to src/Appwrite/Utopia/Response/Model/AttributeIP.php index e81805550..19147ce00 100644 --- a/src/Appwrite/Utopia/Response/Model/IP.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeIP.php @@ -5,7 +5,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model\AttributeString; -class IP extends AttributeString +class AttributeIP extends AttributeString { public function __construct() { @@ -28,7 +28,7 @@ class IP extends AttributeString */ public function getName():string { - return 'IP'; + return 'AttributeIP'; } /** @@ -38,6 +38,6 @@ class IP extends AttributeString */ public function getType():string { - return Response::MODEL_IP; + return Response::MODEL_ATTRIBUTE_IP; } } \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/URL.php b/src/Appwrite/Utopia/Response/Model/AttributeURL.php similarity index 85% rename from src/Appwrite/Utopia/Response/Model/URL.php rename to src/Appwrite/Utopia/Response/Model/AttributeURL.php index 37340ab61..d310af131 100644 --- a/src/Appwrite/Utopia/Response/Model/URL.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeURL.php @@ -5,7 +5,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model\AttributeString; -class URL extends AttributeString +class AttributeURL extends AttributeString { public function __construct() { @@ -28,7 +28,7 @@ class URL extends AttributeString */ public function getName():string { - return 'URL'; + return 'AttributeURL'; } /** @@ -38,6 +38,6 @@ class URL extends AttributeString */ public function getType():string { - return Response::MODEL_URL; + return Response::MODEL_ATTRIBUTE_URL; } } \ No newline at end of file From 78d013c10958de7c9ac1ede4b909f3f286ca9772 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 16 Aug 2021 15:59:58 -0400 Subject: [PATCH 006/130] Prefix all attribute models with Attribute --- src/Appwrite/Utopia/Response.php | 16 +++++----------- .../Utopia/Response/Model/AttributeFloat.php | 4 ++-- .../Utopia/Response/Model/AttributeInteger.php | 4 ++-- .../Utopia/Response/Model/AttributeString.php | 2 +- 4 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 297b9f530..06c9375d0 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -77,17 +77,11 @@ class Response extends SwooleResponse const MODEL_ATTRIBUTE = 'attribute'; const MODEL_ATTRIBUTE_LIST = 'attributeList'; const MODEL_ATTRIBUTE_STRING = 'attributeString'; - const MODEL_ATTRIBUTE_STRING_LIST = 'attributeStringList'; - const MODEL_INTEGER= 'integer'; - const MODEL_INTEGER_LIST= 'integerList'; - const MODEL_FLOAT= 'float'; - const MODEL_FLOAT_LIST= 'floatList'; - const MODEL_ATTRIBUTE_EMAIL= 'email'; - const MODEL_EMAIL_LIST= 'emailList'; - const MODEL_ATTRIBUTE_IP= 'ip'; - const MODEL_IP_LIST= 'ipList'; - const MODEL_ATTRIBUTE_URL= 'url'; - const MODEL_URL_LIST= 'urlList'; + const MODEL_ATTRIBUTE_INTEGER= 'attributeInteger'; + const MODEL_ATTRIBUTE_FLOAT= 'attributeFloat'; + const MODEL_ATTRIBUTE_EMAIL= 'attributeEmail'; + const MODEL_ATTRIBUTE_IP= 'attributeIp'; + const MODEL_ATTRIBUTE_URL= 'attributeUrl'; // Users const MODEL_USER = 'user'; diff --git a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php index bf9ca0f10..3320582b5 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php @@ -36,7 +36,7 @@ class AttributeFloat extends Attribute */ public function getName():string { - return 'Float'; + return 'AttributeFloat'; } /** @@ -46,6 +46,6 @@ class AttributeFloat extends Attribute */ public function getType():string { - return Response::MODEL_FLOAT; + return Response::MODEL_ATTRIBUTE_FLOAT; } } \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php index 5a5ec896c..ee65d3a39 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php @@ -36,7 +36,7 @@ class AttributeInteger extends Attribute */ public function getName():string { - return 'Integer'; + return 'AttributeInteger'; } /** @@ -46,6 +46,6 @@ class AttributeInteger extends Attribute */ public function getType():string { - return Response::MODEL_INTEGER; + return Response::MODEL_ATTRIBUTE_INTEGER; } } \ No newline at end of file diff --git a/src/Appwrite/Utopia/Response/Model/AttributeString.php b/src/Appwrite/Utopia/Response/Model/AttributeString.php index c12de4395..e7eb558b6 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeString.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeString.php @@ -26,7 +26,7 @@ class AttributeString extends Attribute */ public function getName():string { - return 'String'; + return 'AttributeString'; } /** From d2ad1a899e06440de991c4adb572a6aa6843d3d0 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 16 Aug 2021 16:01:20 -0400 Subject: [PATCH 007/130] Add response model for boolean attribute --- src/Appwrite/Utopia/Response.php | 1 + .../Response/Model/AttributeBoolean.php | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeBoolean.php diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 06c9375d0..3a890f9a5 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -79,6 +79,7 @@ class Response extends SwooleResponse const MODEL_ATTRIBUTE_STRING = 'attributeString'; const MODEL_ATTRIBUTE_INTEGER= 'attributeInteger'; const MODEL_ATTRIBUTE_FLOAT= 'attributeFloat'; + const MODEL_ATTRIBUTE_BOOLEAN= 'attributeBoolean'; const MODEL_ATTRIBUTE_EMAIL= 'attributeEmail'; const MODEL_ATTRIBUTE_IP= 'attributeIp'; const MODEL_ATTRIBUTE_URL= 'attributeUrl'; diff --git a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php new file mode 100644 index 000000000..70c089e51 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php @@ -0,0 +1,35 @@ + Date: Mon, 16 Aug 2021 16:06:27 -0400 Subject: [PATCH 008/130] Add default param to attribute response models --- src/Appwrite/Utopia/Response/Model/AttributeBoolean.php | 8 ++++++++ src/Appwrite/Utopia/Response/Model/AttributeEmail.php | 8 ++++++++ src/Appwrite/Utopia/Response/Model/AttributeFloat.php | 8 ++++++++ src/Appwrite/Utopia/Response/Model/AttributeIP.php | 8 ++++++++ src/Appwrite/Utopia/Response/Model/AttributeInteger.php | 8 ++++++++ src/Appwrite/Utopia/Response/Model/AttributeString.php | 8 ++++++++ src/Appwrite/Utopia/Response/Model/AttributeURL.php | 8 ++++++++ 7 files changed, 56 insertions(+) diff --git a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php index 70c089e51..93979288e 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php @@ -10,6 +10,14 @@ class AttributeBoolean extends Model public function __construct() { $this + ->addRule('default', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'example' => false, + 'array' => false, + 'required' => false, + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php index b0ea0d730..11d068e8f 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php @@ -18,6 +18,14 @@ class AttributeEmail extends AttributeString 'array' => false, 'required' => true, ]) + ->addRule('default', [ + 'type' => self::TYPE_STRING, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'example' => 'default@example.com', + 'array' => false, + 'required' => false, + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php index 3320582b5..66259cfdb 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php @@ -26,6 +26,14 @@ class AttributeFloat extends Attribute 'array' => false, 'required' => false, ]) + ->addRule('default', [ + 'type' => self::TYPE_FLOAT, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'example' => 2.5, + 'array' => false, + 'required' => false, + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeIP.php b/src/Appwrite/Utopia/Response/Model/AttributeIP.php index 19147ce00..a380e6e4d 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeIP.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeIP.php @@ -18,6 +18,14 @@ class AttributeIP extends AttributeString 'array' => false, 'required' => true, ]) + ->addRule('default', [ + 'type' => self::TYPE_STRING, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'example' => '192.0.2.0', + 'array' => false, + 'required' => false, + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php index ee65d3a39..3092a7145 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php @@ -26,6 +26,14 @@ class AttributeInteger extends Attribute 'array' => false, 'required' => false, ]) + ->addRule('default', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'example' => 10, + 'array' => false, + 'required' => false, + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeString.php b/src/Appwrite/Utopia/Response/Model/AttributeString.php index e7eb558b6..29a86a4b2 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeString.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeString.php @@ -16,6 +16,14 @@ class AttributeString extends Attribute 'default' => 0, 'example' => 128, ]) + ->addRule('default', [ + 'type' => self::TYPE_STRING, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'example' => 'default', + 'array' => false, + 'required' => false, + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeURL.php b/src/Appwrite/Utopia/Response/Model/AttributeURL.php index d310af131..52a549e2a 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeURL.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeURL.php @@ -18,6 +18,14 @@ class AttributeURL extends AttributeString 'array' => false, 'required' => true, ]) + ->addRule('default', [ + 'type' => self::TYPE_STRING, + 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', + 'default' => null, + 'example' => 'http://example.com', + 'array' => false, + 'required' => false, + ]) ; } From 328358654a8691fec9e36c66dfc5306ffebebddc Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 16 Aug 2021 16:08:18 -0400 Subject: [PATCH 009/130] Instantiate boolean attribute response model --- src/Appwrite/Utopia/Response.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 3a890f9a5..53d574f47 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -14,6 +14,7 @@ use Appwrite\Utopia\Response\Model\Attribute; use Appwrite\Utopia\Response\Model\AttributeString; use Appwrite\Utopia\Response\Model\AttributeInteger; use Appwrite\Utopia\Response\Model\AttributeFloat; +use Appwrite\Utopia\Response\Model\AttributeBoolean; use Appwrite\Utopia\Response\Model\AttributeEmail; use Appwrite\Utopia\Response\Model\AttributeIP; use Appwrite\Utopia\Response\Model\AttributeURL; @@ -197,6 +198,7 @@ class Response extends SwooleResponse ->setModel(new AttributeString()) ->setModel(new AttributeInteger()) ->setModel(new AttributeFloat()) + ->setModel(new AttributeBoolean()) ->setModel(new AttributeEmail()) ->setModel(new AttributeIP()) ->setModel(new AttributeURL()) From 4c794623139c7cbbb4ab09a8756f5481ae853a56 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 16 Aug 2021 18:05:52 -0400 Subject: [PATCH 010/130] Create constants for attribute formats --- app/init.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/app/init.php b/app/init.php index 46dafebac..f80606f4c 100644 --- a/app/init.php +++ b/app/init.php @@ -62,6 +62,11 @@ const APP_LIMIT_COUNT = 5000; const APP_LIMIT_USERS = 10000; const APP_CACHE_BUSTER = 151; const APP_VERSION_STABLE = '0.10.0'; +const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; +const APP_DATABASE_ATTRIBUTE_IP = 'ip'; +const APP_DATABASE_ATTRIBUTE_URL = 'url'; +const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange'; +const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange'; const APP_STORAGE_UPLOADS = '/storage/uploads'; const APP_STORAGE_FUNCTIONS = '/storage/functions'; const APP_STORAGE_CACHE = '/storage/cache'; @@ -196,19 +201,19 @@ Database2::addFilter('encrypt', } ); -Structure::addFormat('email', function() { +Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function() { return new Email(); }, Database2::VAR_STRING); -Structure::addFormat('ip', function() { +Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function() { return new IP(); }, Database2::VAR_STRING); -Structure::addFormat('url', function() { +Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function() { return new URL(); }, Database2::VAR_STRING); -Structure::addFormat('int-range', function($attribute) { +Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function($attribute) { // Format encoded as json string containing name and relevant options // E.g. Range: $format = json_encode(['name'=>$name, 'min'=>$min, 'max'=>$max]); $format = json_decode($attribute['format'], true); @@ -218,7 +223,7 @@ Structure::addFormat('int-range', function($attribute) { return new Range($min, $max, $type); }, Database2::VAR_INTEGER); -Structure::addFormat('float-range', function($attribute) { +Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function($attribute) { // Format encoded as json string containing name and relevant options // E.g. Range: $format = json_encode(['name'=>$name, 'min'=>$min, 'max'=>$max]); $format = json_decode($attribute['format'], true); From 10e42976522cfe721d6511a8c40e88b9bd1c398c Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 16 Aug 2021 18:59:33 -0400 Subject: [PATCH 011/130] Update attribute response models --- src/Appwrite/Utopia/Response/Model.php | 2 +- .../Utopia/Response/Model/Attribute.php | 2 +- .../Response/Model/AttributeBoolean.php | 2 +- .../Utopia/Response/Model/AttributeEmail.php | 8 +++---- .../Utopia/Response/Model/AttributeFloat.php | 22 +++++++---------- .../Utopia/Response/Model/AttributeIP.php | 8 +++---- .../Response/Model/AttributeInteger.php | 24 ++++++++----------- .../Utopia/Response/Model/AttributeString.php | 2 +- .../Utopia/Response/Model/AttributeURL.php | 6 ++--- 9 files changed, 34 insertions(+), 42 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Model.php b/src/Appwrite/Utopia/Response/Model.php index 7c6aafdbd..73b660963 100644 --- a/src/Appwrite/Utopia/Response/Model.php +++ b/src/Appwrite/Utopia/Response/Model.php @@ -35,7 +35,7 @@ abstract class Model /** * Filter Document Structure * - * @return string + * @return Document */ public function filter(Document $document): Document { diff --git a/src/Appwrite/Utopia/Response/Model/Attribute.php b/src/Appwrite/Utopia/Response/Model/Attribute.php index c25b21da5..33df056a2 100644 --- a/src/Appwrite/Utopia/Response/Model/Attribute.php +++ b/src/Appwrite/Utopia/Response/Model/Attribute.php @@ -39,7 +39,7 @@ class Attribute extends Model 'description' => 'Is attribute an array?', 'default' => false, 'example' => false, - 'required' => false + 'require' => false ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php index 93979288e..17c2e8960 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php @@ -16,7 +16,7 @@ class AttributeBoolean extends Model 'default' => null, 'example' => false, 'array' => false, - 'required' => false, + 'require' => false, ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php index 11d068e8f..af090b77f 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php @@ -13,10 +13,10 @@ class AttributeEmail extends AttributeString ->addRule('format', [ 'type' => self::TYPE_STRING, 'description' => 'String format.', - 'default' => 'email', - 'example' => 'email', + 'default' => \json_encode(['name'=>APP_DATABASE_ATTRIBUTE_EMAIL]), + 'example' => \json_encode(['name'=>APP_DATABASE_ATTRIBUTE_EMAIL]), 'array' => false, - 'required' => true, + 'require' => true, ]) ->addRule('default', [ 'type' => self::TYPE_STRING, @@ -24,7 +24,7 @@ class AttributeEmail extends AttributeString 'default' => null, 'example' => 'default@example.com', 'array' => false, - 'required' => false, + 'require' => false, ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php index 66259cfdb..ec90cc7ed 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php @@ -10,21 +10,17 @@ class AttributeFloat extends Attribute public function __construct() { $this - ->addRule('min', [ + ->addRule('format', [ 'type' => self::TYPE_FLOAT, - 'description' => 'Minimum value to enforce on new documents.', + 'description' => 'Float format.', 'default' => null, - 'example' => 0.5, + 'example' => \json_encode([ + 'name' => APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, + 'min' => 1.5, + 'max' => 2.5, + ]), 'array' => false, - 'required' => false, - ]) - ->addRule('max', [ - 'type' => self::TYPE_FLOAT, - 'description' => 'Minimum value to enforce on new documents.', - 'default' => null, - 'example' => 2.5, - 'array' => false, - 'required' => false, + 'require' => false, ]) ->addRule('default', [ 'type' => self::TYPE_FLOAT, @@ -32,7 +28,7 @@ class AttributeFloat extends Attribute 'default' => null, 'example' => 2.5, 'array' => false, - 'required' => false, + 'require' => false, ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeIP.php b/src/Appwrite/Utopia/Response/Model/AttributeIP.php index a380e6e4d..ef6f5b4db 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeIP.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeIP.php @@ -13,10 +13,10 @@ class AttributeIP extends AttributeString ->addRule('format', [ 'type' => self::TYPE_STRING, 'description' => 'String format.', - 'default' => 'ip', - 'example' => 'ip', + 'default' => \json_encode(['name'=>APP_DATABASE_ATTRIBUTE_IP]), + 'example' => \json_encode(['name'=>APP_DATABASE_ATTRIBUTE_IP]), 'array' => false, - 'required' => true, + 'require' => true, ]) ->addRule('default', [ 'type' => self::TYPE_STRING, @@ -24,7 +24,7 @@ class AttributeIP extends AttributeString 'default' => null, 'example' => '192.0.2.0', 'array' => false, - 'required' => false, + 'require' => false, ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php index 3092a7145..161a9cb9b 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php @@ -10,21 +10,17 @@ class AttributeInteger extends Attribute public function __construct() { $this - ->addRule('min', [ - 'type' => self::TYPE_INTEGER, - 'description' => 'Minimum value to enforce on new documents.', + ->addRule('format', [ + 'type' => self::TYPE_STRING, + 'description' => 'Integer format.', 'default' => null, - 'example' => 0, + 'example' => \json_encode([ + 'name' => APP_DATABASE_ATTRIBUTE_INT_RANGE, + 'min' => 0, + 'max' => 10, + ]), 'array' => false, - 'required' => false, - ]) - ->addRule('max', [ - 'type' => self::TYPE_INTEGER, - 'description' => 'Minimum value to enforce on new documents.', - 'default' => null, - 'example' => 10, - 'array' => false, - 'required' => false, + 'require' => false, ]) ->addRule('default', [ 'type' => self::TYPE_INTEGER, @@ -32,7 +28,7 @@ class AttributeInteger extends Attribute 'default' => null, 'example' => 10, 'array' => false, - 'required' => false, + 'require' => false, ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeString.php b/src/Appwrite/Utopia/Response/Model/AttributeString.php index 29a86a4b2..9dd4b1829 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeString.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeString.php @@ -22,7 +22,7 @@ class AttributeString extends Attribute 'default' => null, 'example' => 'default', 'array' => false, - 'required' => false, + 'require' => false, ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeURL.php b/src/Appwrite/Utopia/Response/Model/AttributeURL.php index 52a549e2a..20e7f09fa 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeURL.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeURL.php @@ -13,8 +13,8 @@ class AttributeURL extends AttributeString ->addRule('format', [ 'type' => self::TYPE_STRING, 'description' => 'String format.', - 'default' => 'url', - 'example' => 'url', + 'default' => \json_encode(['name'=>APP_DATABASE_ATTRIBUTE_URL]), + 'example' => \json_encode(['name'=>APP_DATABASE_ATTRIBUTE_URL]), 'array' => false, 'required' => true, ]) @@ -24,7 +24,7 @@ class AttributeURL extends AttributeString 'default' => null, 'example' => 'http://example.com', 'array' => false, - 'required' => false, + 'require' => false, ]) ; } From 8f24d121f5752abebf4e5a097a0be19dc7385495 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 16 Aug 2021 19:21:00 -0400 Subject: [PATCH 012/130] Validate attributes and responses --- app/controllers/api/database.php | 186 ++++++++++++++++++++----------- 1 file changed, 120 insertions(+), 66 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 75eab3714..f3e766341 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -1,6 +1,5 @@ getCollection(); $attributeId = $attribute->getId(); $type = $attribute->getAttribute('type', ''); $size = $attribute->getAttribute('size', 0); $required = $attribute->getAttribute('required', true); $default = $attribute->getAttribute('default', null); - $min = $attribute->getAttribute('min', null); - $max = $attribute->getAttribute('max', null); $signed = $attribute->getAttribute('signed', true); // integers are signed by default $array = $attribute->getAttribute('array', false); $format = $attribute->getAttribute('format', null); @@ -51,15 +58,6 @@ $attributesCallback = function ($attribute, $response, $dbForExternal, $database throw new Exception('Collection not found', 404); } - // TODO@kodumbeats how to depend on $size for Text validator length - // Ensure attribute default is within required size - if ($size > 0 && !\is_null($default)) { - $validator = new Text($size); - if (!$validator->isValid($default)) { - throw new Exception('Length of default attribute exceeds attribute size', 400); - } - } - if (!\is_null($format)) { $name = \json_decode($format, true)['name']; if (!Structure::hasFormat($name, $type)) { @@ -67,23 +65,6 @@ $attributesCallback = function ($attribute, $response, $dbForExternal, $database } } - if (!is_null($min) || !is_null($max)) { // Add range validator if either $min or $max is provided - switch ($type) { - case Database::VAR_INTEGER: - $min = (is_null($min)) ? -INF : \intval($min); - $max = (is_null($max)) ? INF : \intval($max); - $format = 'int-range'; - break; - case Database::VAR_FLOAT: - $min = (is_null($min)) ? -INF : \floatval($min); - $max = (is_null($max)) ? INF : \floatval($max); - $format = 'float-range'; - break; - default: - throw new Exception("Format range not available for {$type} attributes.", 400); - } - } - $success = $dbForExternal->addAttributeInQueue($collectionId, $attributeId, $type, $size, $required, $default, $signed, $array, $format, $filters); // Database->addAttributeInQueue() does not return a document @@ -97,8 +78,6 @@ $attributesCallback = function ($attribute, $response, $dbForExternal, $database 'size' => $size, 'required' => $required, 'default' => $default, - 'min' => $min, - 'max' => $max, 'signed' => $signed, 'array' => $array, 'format' => $format, @@ -117,7 +96,8 @@ $attributesCallback = function ($attribute, $response, $dbForExternal, $database ; $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic($attribute, Response::MODEL_ATTRIBUTE); + + return $attribute; }; App::post('/v1/database/collections') @@ -344,13 +324,19 @@ App::post('/v1/database/collections/:collectionId/attributes/string') ->inject('dbForExternal') ->inject('database') ->inject('audits') - ->action(function ($collectionId, $attributeId, $size, $required, $default, $array, $response, $dbForExternal, $database, $audits) use ($attributesCallback) { + ->action(function ($collectionId, $attributeId, $size, $required, $default, $array, $response, $dbForExternal, $database, $audits) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForExternal*/ /** @var Appwrite\Event\Event $database */ /** @var Appwrite\Event\Event $audits */ - return $attributesCallback(new Document([ + // Ensure attribute default is within required size + $validator = new Text($size); + if (!is_null($default) && !$validator->isValid($default)) { + throw new Exception($validator->getDescription(), 400); + } + + $attribute = attributesCallback(new Document([ '$collection' => $collectionId, '$id' => $attributeId, 'type' => Database::VAR_STRING, @@ -359,6 +345,8 @@ App::post('/v1/database/collections/:collectionId/attributes/string') 'default' => $default, 'array' => $array, ]), $response, $dbForExternal, $database, $audits); + + $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_STRING); }); App::post('/v1/database/collections/:collectionId/attributes/email') @@ -382,13 +370,19 @@ App::post('/v1/database/collections/:collectionId/attributes/email') ->inject('dbForExternal') ->inject('database') ->inject('audits') - ->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForExternal, $database, $audits) use ($attributesCallback) { + ->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForExternal, $database, $audits) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForExternal*/ /** @var Appwrite\Event\Event $database */ /** @var Appwrite\Event\Event $audits */ - return $attributesCallback(new Document([ + // Ensure attribute default is valid email + $validator = new Email(); + if (!is_null($default) && !$validator->isValid($default)) { + throw new Exception($validator->getDescription(), 400); + } + + $attribute = attributesCallback(new Document([ '$collection' => $collectionId, '$id' => $attributeId, 'type' => Database::VAR_STRING, @@ -396,8 +390,10 @@ App::post('/v1/database/collections/:collectionId/attributes/email') 'required' => $required, 'default' => $default, 'array' => $array, - 'format' => \json_encode(['name'=>'email']), + 'format' => \json_encode(['name'=> APP_DATABASE_ATTRIBUTE_EMAIL]), ]), $response, $dbForExternal, $database, $audits); + + $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_EMAIL); }); App::post('/v1/database/collections/:collectionId/attributes/ip') @@ -421,13 +417,19 @@ App::post('/v1/database/collections/:collectionId/attributes/ip') ->inject('dbForExternal') ->inject('database') ->inject('audits') - ->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForExternal, $database, $audits) use ($attributesCallback) { + ->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForExternal, $database, $audits) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForExternal*/ /** @var Appwrite\Event\Event $database */ /** @var Appwrite\Event\Event $audits */ - return $attributesCallback(new Document([ + // Ensure attribute default is valid IP address + $validator = new IP(); + if (!is_null($default) && !$validator->isValid($default)) { + throw new Exception($validator->getDescription(), 400); + } + + $attribute = attributesCallback(new Document([ '$collection' => $collectionId, '$id' => $attributeId, 'type' => Database::VAR_STRING, @@ -435,8 +437,10 @@ App::post('/v1/database/collections/:collectionId/attributes/ip') 'required' => $required, 'default' => $default, 'array' => $array, - 'format' => \json_encode(['name'=>'ip']), + 'format' => \json_encode(['name'=> APP_DATABASE_ATTRIBUTE_IP]), ]), $response, $dbForExternal, $database, $audits); + + $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_IP); }); App::post('/v1/database/collections/:collectionId/attributes/url') @@ -461,13 +465,19 @@ App::post('/v1/database/collections/:collectionId/attributes/url') ->inject('dbForExternal') ->inject('database') ->inject('audits') - ->action(function ($collectionId, $attributeId, $size, $required, $default, $array, $response, $dbForExternal, $database, $audits) use ($attributesCallback) { + ->action(function ($collectionId, $attributeId, $size, $required, $default, $array, $response, $dbForExternal, $database, $audits) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForExternal*/ /** @var Appwrite\Event\Event $database */ /** @var Appwrite\Event\Event $audits */ - return $attributesCallback(new Document([ + // Ensure attribute default is valid URL + $validator = new URL(); + if (!is_null($default) && !$validator->isValid($default)) { + throw new Exception($validator->getDescription(), 400); + } + + $attribute = attributesCallback(new Document([ '$collection' => $collectionId, '$id' => $attributeId, 'type' => Database::VAR_STRING, @@ -475,8 +485,10 @@ App::post('/v1/database/collections/:collectionId/attributes/url') 'required' => $required, 'default' => $default, 'array' => $array, - 'format' => \json_encode(['name'=>'url']), + 'format' => \json_encode(['name'=> APP_DATABASE_ATTRIBUTE_URL]), ]), $response, $dbForExternal, $database, $audits); + + $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_URL); }); App::post('/v1/database/collections/:collectionId/attributes/integer') @@ -502,13 +514,23 @@ App::post('/v1/database/collections/:collectionId/attributes/integer') ->inject('dbForExternal') ->inject('database') ->inject('audits') - ->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForExternal, $database, $audits) use ($attributesCallback) { + ->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForExternal, $database, $audits) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForExternal*/ /** @var Appwrite\Event\Event $database */ /** @var Appwrite\Event\Event $audits */ - return $attributesCallback(new Document([ + // Ensure attribute default is within range + $format = (\is_null($min) || \is_null($max)); // whether to apply range format + $min = \is_null($min) ? -INF : \intval($min); + $max = \is_null($max) ? INF : \intval($max); + $validator = new Range($min, $max, Database::VAR_INTEGER); + + if (!is_null($default) && !$validator->isValid($default)) { + throw new Exception($validator->getDescription(), 400); + } + + $attribute = attributesCallback(new Document([ '$collection' => $collectionId, '$id' => $attributeId, 'type' => Database::VAR_INTEGER, @@ -516,12 +538,14 @@ App::post('/v1/database/collections/:collectionId/attributes/integer') 'required' => $required, 'default' => $default, 'array' => $array, - 'format' => \json_encode([ - 'name'=>'int-range', + 'format' => ($format) ? \json_encode([ + 'name'=> APP_DATABASE_ATTRIBUTE_INT_RANGE, 'min' => $min, 'max' => $max, - ]), + ]) : null, ]), $response, $dbForExternal, $database, $audits); + + $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_INTEGER); }); App::post('/v1/database/collections/:collectionId/attributes/float') @@ -547,13 +571,23 @@ App::post('/v1/database/collections/:collectionId/attributes/float') ->inject('dbForExternal') ->inject('database') ->inject('audits') - ->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForExternal, $database, $audits) use ($attributesCallback) { + ->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForExternal, $database, $audits) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForExternal*/ /** @var Appwrite\Event\Event $database */ /** @var Appwrite\Event\Event $audits */ - return $attributesCallback(new Document([ + // Ensure attribute default is within range + $format = (\is_null($min) || \is_null($max)); // whether to apply range format + $min = \is_null($min) ? -INF : \floatval($min); + $max = \is_null($max) ? INF : \floatval($max); + $validator = new Range($min, $max, Database::VAR_FLOAT); + + if (!is_null($default) && !$validator->isValid($default)) { + throw new Exception($validator->getDescription(), 400); + } + + $attribute = attributesCallback(new Document([ '$collection' => $collectionId, '$id' => $attributeId, 'type' => Database::VAR_FLOAT, @@ -561,12 +595,14 @@ App::post('/v1/database/collections/:collectionId/attributes/float') 'size' => 0, 'default' => $default, 'array' => $array, - 'format' => \json_encode([ - 'name'=>'float-range', + 'format' => ($format) ? \json_encode([ + 'name'=> APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, 'min' => $min, 'max' => $max, - ]), + ]) : null, ]), $response, $dbForExternal, $database, $audits); + + $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_FLOAT); }); App::post('/v1/database/collections/:collectionId/attributes/boolean') @@ -590,13 +626,13 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean') ->inject('dbForExternal') ->inject('database') ->inject('audits') - ->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForExternal, $database, $audits) use ($attributesCallback) { + ->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForExternal, $database, $audits) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForExternal*/ /** @var Appwrite\Event\Event $database */ /** @var Appwrite\Event\Event $audits */ - return $attributesCallback(new Document([ + $attribute = attributesCallback(new Document([ '$collection' => $collectionId, '$id' => $attributeId, 'type' => Database::VAR_BOOLEAN, @@ -605,6 +641,8 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean') 'default' => $default, 'array' => $array, ]), $response, $dbForExternal, $database, $audits); + + $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_BOOLEAN); }); App::get('/v1/database/collections/:collectionId/attributes') @@ -682,8 +720,24 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') $attribute = new Document([\array_merge($attributes[$attributeIndex], [ 'collectionId' => $collectionId, ])]); + + $type = $attribute->getAttribute('type'); + $format = json_decode($attribute->getAttribute('format'), true); + + $model = match($type) { + Database::VAR_BOOLEAN => Response::MODEL_ATTRIBUTE_BOOLEAN, + Database::VAR_INTEGER => Response::MODEL_ATTRIBUTE_INTEGER, + Database::VAR_FLOAT => Response::MODEL_ATTRIBUTE_FLOAT, + Database::VAR_STRING => match($format['name']) { + APP_DATABASE_ATTRIBUTE_URL => Response::MODEL_ATTRIBUTE_EMAIL, + APP_DATABASE_ATTRIBUTE_IP => Response::MODEL_ATTRIBUTE_IP, + APP_DATABASE_ATTRIBUTE_URL => Response::MODEL_ATTRIBUTE_URL, + default => Response::MODEL_ATTRIBUTE_STRING, + }, + default => Response::MODEL_ATTRIBUTE, + }; - $response->dynamic($attribute, Response::MODEL_ATTRIBUTE); + $response->dynamic($attribute, $model); }); App::delete('/v1/database/collections/:collectionId/attributes/:attributeId') From 89f55a17277361f14b84fac7f07659920c5e74eb Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 18 Aug 2021 16:42:03 +0300 Subject: [PATCH 013/130] Re-orgenized controller flow --- app/controllers/api/database.php | 12 ++++++------ app/controllers/api/functions.php | 28 ++++++++++++++-------------- app/controllers/api/projects.php | 12 ++++++------ app/controllers/api/storage.php | 12 ++++++------ app/controllers/api/teams.php | 12 ++++++------ 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 6cc933e6c..01f3e7df7 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -193,12 +193,6 @@ App::get('/v1/database/collections') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForExternal */ - $queries = []; - - if (!empty($search)) { - $queries[] = new Query('name', Query::TYPE_SEARCH, [$search]); - } - if (!empty($after)) { $afterCollection = $dbForExternal->getDocument('collections', $after); @@ -207,6 +201,12 @@ App::get('/v1/database/collections') } } + $queries = []; + + if (!empty($search)) { + $queries[] = new Query('name', Query::TYPE_SEARCH, [$search]); + } + $response->dynamic(new Document([ 'collections' => $dbForExternal->find(Database::COLLECTIONS, $queries, $limit, $offset, [], [$orderType], $afterCollection ?? null), 'sum' => $dbForExternal->count(Database::COLLECTIONS, $queries, APP_LIMIT_COUNT), diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index f862bee85..f0f7f6133 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -98,12 +98,6 @@ App::get('/v1/functions') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $queries = []; - - if (!empty($search)) { - $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); - } - if (!empty($after)) { $afterFunction = $dbForInternal->getDocument('functions', $after); @@ -111,6 +105,12 @@ App::get('/v1/functions') throw new Exception("Function '{$after}' for the 'after' value not found.", 400); } } + + $queries = []; + + if (!empty($search)) { + $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); + } $response->dynamic(new Document([ 'functions' => $dbForInternal->find('functions', $queries, $limit, $offset, [], [$orderType], $afterFunction ?? null), @@ -538,14 +538,6 @@ App::get('/v1/functions/:functionId/tags') throw new Exception('Function not found', 404); } - $queries = []; - - if (!empty($search)) { - $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); - } - - $queries[] = new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]); - if (!empty($after)) { $afterTag = $dbForInternal->getDocument('tags', $after); @@ -554,6 +546,14 @@ App::get('/v1/functions/:functionId/tags') } } + $queries = []; + + if (!empty($search)) { + $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); + } + + $queries[] = new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]); + $results = $dbForInternal->find('tags', $queries, $limit, $offset, [], [$orderType], $afterTag ?? null); $sum = $dbForInternal->count('tags', $queries, APP_LIMIT_COUNT); diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 0a2336920..df740eb42 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -171,12 +171,6 @@ App::get('/v1/projects') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForConsole */ - $queries = []; - - if (!empty($search)) { - $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); - } - if (!empty($after)) { $afterProject = $dbForConsole->getDocument('projects', $after); @@ -185,6 +179,12 @@ App::get('/v1/projects') } } + $queries = []; + + if (!empty($search)) { + $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); + } + $results = $dbForConsole->find('projects', $queries, $limit, $offset, [], [$orderType], $afterProject ?? null); $sum = $dbForConsole->count('projects', $queries, APP_LIMIT_COUNT); diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index d6735c138..b73e3facf 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -181,12 +181,6 @@ App::get('/v1/storage/files') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $queries = []; - - if (!empty($search)) { - $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); - } - if (!empty($after)) { $afterFile = $dbForInternal->getDocument('files', $after); @@ -195,6 +189,12 @@ App::get('/v1/storage/files') } } + $queries = []; + + if (!empty($search)) { + $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); + } + $response->dynamic(new Document([ 'files' => $dbForInternal->find('files', $queries, $limit, $offset, [], [$orderType], $afterFile ?? null), 'sum' => $dbForInternal->count('files', $queries, APP_LIMIT_COUNT), diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 903668d52..75d6f91cb 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -108,12 +108,6 @@ App::get('/v1/teams') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $queries = []; - - if (!empty($search)) { - $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); - } - if (!empty($after)) { $afterTeam = $dbForInternal->getDocument('teams', $after); @@ -122,6 +116,12 @@ App::get('/v1/teams') } } + $queries = []; + + if (!empty($search)) { + $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); + } + $results = $dbForInternal->find('teams', $queries, $limit, $offset, [], [$orderType], $afterTeam ?? null); $sum = $dbForInternal->count('teams', $queries, APP_LIMIT_COUNT); From 19e144346986b82a4160f627e7541227abde5cd8 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 23 Aug 2021 15:06:47 -0400 Subject: [PATCH 014/130] Fix breaking bugs introduced in merge conflict resolution --- app/init.php | 1 - src/Appwrite/Utopia/Response/Model/Attribute.php | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/init.php b/app/init.php index e84317b6c..4d999b7a4 100644 --- a/app/init.php +++ b/app/init.php @@ -230,7 +230,6 @@ Database::addFilter('encrypt', * DB Formats */ Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function() { -Structure::addFormat('email', function() { return new Email(); }, Database::VAR_STRING); diff --git a/src/Appwrite/Utopia/Response/Model/Attribute.php b/src/Appwrite/Utopia/Response/Model/Attribute.php index f88b5ac4e..cc5c2a7f8 100644 --- a/src/Appwrite/Utopia/Response/Model/Attribute.php +++ b/src/Appwrite/Utopia/Response/Model/Attribute.php @@ -63,3 +63,4 @@ class Attribute extends Model { return Response::MODEL_ATTRIBUTE; } +} From 0eb0ceb6d593ef3d1529ee6ab708ee9f880f0710 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 23 Aug 2021 17:25:32 -0400 Subject: [PATCH 015/130] Explicitly add attribute model rules --- .../Response/Model/AttributeBoolean.php | 31 ++++++++++++++++ .../Utopia/Response/Model/AttributeEmail.php | 35 +++++++++++++++++-- .../Utopia/Response/Model/AttributeFloat.php | 31 ++++++++++++++++ .../Utopia/Response/Model/AttributeIP.php | 35 +++++++++++++++++-- .../Response/Model/AttributeInteger.php | 31 ++++++++++++++++ .../Utopia/Response/Model/AttributeString.php | 31 ++++++++++++++++ .../Utopia/Response/Model/AttributeURL.php | 35 +++++++++++++++++-- 7 files changed, 223 insertions(+), 6 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php index 17c2e8960..93dbe5725 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php @@ -10,6 +10,37 @@ class AttributeBoolean extends Model public function __construct() { $this + ->addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'fullName', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('status', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, or `failed`', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('required', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute required?', + 'default' => false, + 'example' => true, + ]) + ->addRule('array', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute an array?', + 'default' => false, + 'example' => false, + 'require' => false + ]) ->addRule('default', [ 'type' => self::TYPE_BOOLEAN, 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', diff --git a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php index af090b77f..861356df3 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php @@ -10,11 +10,42 @@ class AttributeEmail extends AttributeString public function __construct() { $this + ->addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'fullName', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('status', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, or `failed`', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('required', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute required?', + 'default' => false, + 'example' => true, + ]) + ->addRule('array', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute an array?', + 'default' => false, + 'example' => false, + 'require' => false + ]) ->addRule('format', [ 'type' => self::TYPE_STRING, 'description' => 'String format.', - 'default' => \json_encode(['name'=>APP_DATABASE_ATTRIBUTE_EMAIL]), - 'example' => \json_encode(['name'=>APP_DATABASE_ATTRIBUTE_EMAIL]), + 'default' => APP_DATABASE_ATTRIBUTE_EMAIL, + 'example' => APP_DATABASE_ATTRIBUTE_EMAIL, 'array' => false, 'require' => true, ]) diff --git a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php index ec90cc7ed..85adbe285 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php @@ -10,6 +10,37 @@ class AttributeFloat extends Attribute public function __construct() { $this + ->addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'fullName', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('status', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, or `failed`', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('required', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute required?', + 'default' => false, + 'example' => true, + ]) + ->addRule('array', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute an array?', + 'default' => false, + 'example' => false, + 'require' => false + ]) ->addRule('format', [ 'type' => self::TYPE_FLOAT, 'description' => 'Float format.', diff --git a/src/Appwrite/Utopia/Response/Model/AttributeIP.php b/src/Appwrite/Utopia/Response/Model/AttributeIP.php index ef6f5b4db..36e000e15 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeIP.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeIP.php @@ -10,11 +10,42 @@ class AttributeIP extends AttributeString public function __construct() { $this + ->addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'fullName', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('status', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, or `failed`', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('required', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute required?', + 'default' => false, + 'example' => true, + ]) + ->addRule('array', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute an array?', + 'default' => false, + 'example' => false, + 'require' => false + ]) ->addRule('format', [ 'type' => self::TYPE_STRING, 'description' => 'String format.', - 'default' => \json_encode(['name'=>APP_DATABASE_ATTRIBUTE_IP]), - 'example' => \json_encode(['name'=>APP_DATABASE_ATTRIBUTE_IP]), + 'default' => APP_DATABASE_ATTRIBUTE_IP, + 'example' => APP_DATABASE_ATTRIBUTE_IP, 'array' => false, 'require' => true, ]) diff --git a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php index 161a9cb9b..366ecd556 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php @@ -10,6 +10,37 @@ class AttributeInteger extends Attribute public function __construct() { $this + ->addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'fullName', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('status', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, or `failed`', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('required', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute required?', + 'default' => false, + 'example' => true, + ]) + ->addRule('array', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute an array?', + 'default' => false, + 'example' => false, + 'require' => false + ]) ->addRule('format', [ 'type' => self::TYPE_STRING, 'description' => 'Integer format.', diff --git a/src/Appwrite/Utopia/Response/Model/AttributeString.php b/src/Appwrite/Utopia/Response/Model/AttributeString.php index 9dd4b1829..e8d156039 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeString.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeString.php @@ -10,6 +10,37 @@ class AttributeString extends Attribute public function __construct() { $this + ->addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'fullName', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('status', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, or `failed`', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('required', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute required?', + 'default' => false, + 'example' => true, + ]) + ->addRule('array', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute an array?', + 'default' => false, + 'example' => false, + 'require' => false + ]) ->addRule('size', [ 'type' => self::TYPE_STRING, 'description' => 'Attribute size.', diff --git a/src/Appwrite/Utopia/Response/Model/AttributeURL.php b/src/Appwrite/Utopia/Response/Model/AttributeURL.php index 20e7f09fa..0d3365f3a 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeURL.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeURL.php @@ -10,11 +10,42 @@ class AttributeURL extends AttributeString public function __construct() { $this + ->addRule('key', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute Key.', + 'default' => '', + 'example' => 'fullName', + ]) + ->addRule('type', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute type.', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('status', [ + 'type' => self::TYPE_STRING, + 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, or `failed`', + 'default' => '', + 'example' => 'string', + ]) + ->addRule('required', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute required?', + 'default' => false, + 'example' => true, + ]) + ->addRule('array', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Is attribute an array?', + 'default' => false, + 'example' => false, + 'require' => false + ]) ->addRule('format', [ 'type' => self::TYPE_STRING, 'description' => 'String format.', - 'default' => \json_encode(['name'=>APP_DATABASE_ATTRIBUTE_URL]), - 'example' => \json_encode(['name'=>APP_DATABASE_ATTRIBUTE_URL]), + 'default' => APP_DATABASE_ATTRIBUTE_URL, + 'example' => APP_DATABASE_ATTRIBUTE_URL, 'array' => false, 'required' => true, ]) From ebcd1b23b6ad063a6dfab2da4a120b5bd49828ed Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 23 Aug 2021 17:29:05 -0400 Subject: [PATCH 016/130] Size not returned for integer attributes --- tests/e2e/Services/Database/DatabaseBase.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index 3e109ca33..a097777f5 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -73,7 +73,6 @@ trait DatabaseBase $this->assertEquals($releaseYear['headers']['status-code'], 201); $this->assertEquals($releaseYear['body']['key'], '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); From 741779cdb88c0feaf38bd03773424aae8f27f4cf Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 23 Aug 2021 17:33:57 -0400 Subject: [PATCH 017/130] Do not throw exception if rule default is null but not required --- src/Appwrite/Utopia/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 53d574f47..1c73ac14c 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -325,7 +325,7 @@ class Response extends SwooleResponse $document = $model->filter($document); foreach ($model->getRules() as $key => $rule) { - if (!$document->isSet($key)) { + if (!$document->isSet($key) && $rule['require']) { // do not set attribute in response if not required if (!is_null($rule['default'])) { $document->setAttribute($key, $rule['default']); } else { From 4b0dc9dab31808462dbaac0453c533a738c56d4d Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 24 Aug 2021 09:46:41 -0400 Subject: [PATCH 018/130] Use param validation from framework --- app/controllers/api/database.php | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index e15b472b1..317528733 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -500,7 +500,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email') ->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).') ->param('attributeId', '', new Key(), 'Attribute ID.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Text(0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) + ->param('default', null, new Email(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForInternal') @@ -512,12 +512,6 @@ App::post('/v1/database/collections/:collectionId/attributes/email') /** @var Appwrite\Event\Event $database */ /** @var Appwrite\Event\Event $audits */ - // Ensure attribute default is valid email - $validator = new Email(); - if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception($validator->getDescription(), 400); - } - $attribute = attributesCallback($collectionId, new Document([ '$id' => $attributeId, 'type' => Database::VAR_STRING, @@ -546,7 +540,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip') ->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).') ->param('attributeId', '', new Key(), 'Attribute ID.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Text(0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) + ->param('default', null, new IP(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForInternal') @@ -558,12 +552,6 @@ App::post('/v1/database/collections/:collectionId/attributes/ip') /** @var Appwrite\Event\Event $database */ /** @var Appwrite\Event\Event $audits */ - // Ensure attribute default is valid IP address - $validator = new IP(); - if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception($validator->getDescription(), 400); - } - $attribute = attributesCallback($collectionId, new Document([ '$id' => $attributeId, 'type' => Database::VAR_STRING, @@ -592,7 +580,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url') ->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).') ->param('attributeId', '', new Key(), 'Attribute ID.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Text(0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) + ->param('default', null, new URL(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForInternal') @@ -604,12 +592,6 @@ App::post('/v1/database/collections/:collectionId/attributes/url') /** @var Appwrite\Event\Event $database */ /** @var Appwrite\Event\Event $audits */ - // Ensure attribute default is valid URL - $validator = new URL(); - if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception($validator->getDescription(), 400); - } - $attribute = attributesCallback($collectionId, new Document([ '$id' => $attributeId, 'type' => Database::VAR_STRING, From c4cb72731e8cf0eadce93df13bd593bf7c6db92a Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 24 Aug 2021 09:49:58 -0400 Subject: [PATCH 019/130] Refactor attributeCallback to function createAttribute --- app/controllers/api/database.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 317528733..2dd5c3f0e 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -42,7 +42,7 @@ use DeviceDetector\DeviceDetector; * * @return Document Newly created attribute document */ -function attributesCallback($collectionId, $attribute, $response, $dbForInternal, $database, $audits): Document +function createAttribute($collectionId, $attribute, $response, $dbForInternal, $database, $audits): Document { $attributeId = $attribute->getId(); @@ -473,7 +473,7 @@ App::post('/v1/database/collections/:collectionId/attributes/string') throw new Exception($validator->getDescription(), 400); } - $attribute = attributesCallback($collectionId, new Document([ + $attribute = createAttribute($collectionId, new Document([ '$id' => $attributeId, 'type' => Database::VAR_STRING, 'size' => $size, @@ -512,7 +512,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email') /** @var Appwrite\Event\Event $database */ /** @var Appwrite\Event\Event $audits */ - $attribute = attributesCallback($collectionId, new Document([ + $attribute = createAttribute($collectionId, new Document([ '$id' => $attributeId, 'type' => Database::VAR_STRING, 'size' => 254, @@ -552,7 +552,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip') /** @var Appwrite\Event\Event $database */ /** @var Appwrite\Event\Event $audits */ - $attribute = attributesCallback($collectionId, new Document([ + $attribute = createAttribute($collectionId, new Document([ '$id' => $attributeId, 'type' => Database::VAR_STRING, 'size' => 39, @@ -592,7 +592,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url') /** @var Appwrite\Event\Event $database */ /** @var Appwrite\Event\Event $audits */ - $attribute = attributesCallback($collectionId, new Document([ + $attribute = createAttribute($collectionId, new Document([ '$id' => $attributeId, 'type' => Database::VAR_STRING, 'size' => 2000, @@ -643,7 +643,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer') throw new Exception($validator->getDescription(), 400); } - $attribute = attributesCallback($collectionId, new Document([ + $attribute = createAttribute($collectionId, new Document([ '$id' => $attributeId, 'type' => Database::VAR_INTEGER, 'size' => 0, @@ -698,7 +698,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float') throw new Exception($validator->getDescription(), 400); } - $attribute = attributesCallback($collectionId, new Document([ + $attribute = createAttribute($collectionId, new Document([ '$id' => $attributeId, 'type' => Database::VAR_FLOAT, 'required' => $required, @@ -742,7 +742,7 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean') /** @var Appwrite\Event\Event $database */ /** @var Appwrite\Event\Event $audits */ - $attribute = attributesCallback($collectionId, new Document([ + $attribute = createAttribute($collectionId, new Document([ '$id' => $attributeId, 'type' => Database::VAR_BOOLEAN, 'size' => 0, From af2e64ef7b65b7852b3be8bc83135c57d9c2713a Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 24 Aug 2021 10:31:02 -0400 Subject: [PATCH 020/130] Construct parent models to properly inherit rules --- .../Response/Model/AttributeBoolean.php | 37 ++----------------- .../Utopia/Response/Model/AttributeEmail.php | 33 +---------------- .../Utopia/Response/Model/AttributeFloat.php | 33 +---------------- .../Utopia/Response/Model/AttributeIP.php | 33 +---------------- .../Response/Model/AttributeInteger.php | 36 ++---------------- .../Utopia/Response/Model/AttributeString.php | 33 +---------------- .../Utopia/Response/Model/AttributeURL.php | 33 +---------------- 7 files changed, 17 insertions(+), 221 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php index 93dbe5725..4076bc9eb 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php @@ -3,44 +3,15 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; -use Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Response\Model\Attribute; -class AttributeBoolean extends Model +class AttributeBoolean extends Attribute { public function __construct() { + parent::__construct(); + $this - ->addRule('key', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', - 'default' => '', - 'example' => 'fullName', - ]) - ->addRule('type', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', - 'default' => '', - 'example' => 'string', - ]) - ->addRule('status', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, or `failed`', - 'default' => '', - 'example' => 'string', - ]) - ->addRule('required', [ - 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute required?', - 'default' => false, - 'example' => true, - ]) - ->addRule('array', [ - 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute an array?', - 'default' => false, - 'example' => false, - 'require' => false - ]) ->addRule('default', [ 'type' => self::TYPE_BOOLEAN, 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', diff --git a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php index 861356df3..1c058e6b0 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php @@ -9,38 +9,9 @@ class AttributeEmail extends AttributeString { public function __construct() { + parent::__construct(); + $this - ->addRule('key', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', - 'default' => '', - 'example' => 'fullName', - ]) - ->addRule('type', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', - 'default' => '', - 'example' => 'string', - ]) - ->addRule('status', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, or `failed`', - 'default' => '', - 'example' => 'string', - ]) - ->addRule('required', [ - 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute required?', - 'default' => false, - 'example' => true, - ]) - ->addRule('array', [ - 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute an array?', - 'default' => false, - 'example' => false, - 'require' => false - ]) ->addRule('format', [ 'type' => self::TYPE_STRING, 'description' => 'String format.', diff --git a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php index 85adbe285..669aa4dd7 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php @@ -9,38 +9,9 @@ class AttributeFloat extends Attribute { public function __construct() { + parent::__construct(); + $this - ->addRule('key', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', - 'default' => '', - 'example' => 'fullName', - ]) - ->addRule('type', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', - 'default' => '', - 'example' => 'string', - ]) - ->addRule('status', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, or `failed`', - 'default' => '', - 'example' => 'string', - ]) - ->addRule('required', [ - 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute required?', - 'default' => false, - 'example' => true, - ]) - ->addRule('array', [ - 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute an array?', - 'default' => false, - 'example' => false, - 'require' => false - ]) ->addRule('format', [ 'type' => self::TYPE_FLOAT, 'description' => 'Float format.', diff --git a/src/Appwrite/Utopia/Response/Model/AttributeIP.php b/src/Appwrite/Utopia/Response/Model/AttributeIP.php index 36e000e15..8272c2760 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeIP.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeIP.php @@ -9,38 +9,9 @@ class AttributeIP extends AttributeString { public function __construct() { + parent::__construct(); + $this - ->addRule('key', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', - 'default' => '', - 'example' => 'fullName', - ]) - ->addRule('type', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', - 'default' => '', - 'example' => 'string', - ]) - ->addRule('status', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, or `failed`', - 'default' => '', - 'example' => 'string', - ]) - ->addRule('required', [ - 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute required?', - 'default' => false, - 'example' => true, - ]) - ->addRule('array', [ - 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute an array?', - 'default' => false, - 'example' => false, - 'require' => false - ]) ->addRule('format', [ 'type' => self::TYPE_STRING, 'description' => 'String format.', diff --git a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php index 366ecd556..d1ff6346f 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php @@ -9,38 +9,9 @@ class AttributeInteger extends Attribute { public function __construct() { + parent::__construct(); + $this - ->addRule('key', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', - 'default' => '', - 'example' => 'fullName', - ]) - ->addRule('type', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', - 'default' => '', - 'example' => 'string', - ]) - ->addRule('status', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, or `failed`', - 'default' => '', - 'example' => 'string', - ]) - ->addRule('required', [ - 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute required?', - 'default' => false, - 'example' => true, - ]) - ->addRule('array', [ - 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute an array?', - 'default' => false, - 'example' => false, - 'require' => false - ]) ->addRule('format', [ 'type' => self::TYPE_STRING, 'description' => 'Integer format.', @@ -65,8 +36,7 @@ class AttributeInteger extends Attribute } /** - * Get Name - * + * Get Name * * @return string */ public function getName():string diff --git a/src/Appwrite/Utopia/Response/Model/AttributeString.php b/src/Appwrite/Utopia/Response/Model/AttributeString.php index e8d156039..22f7e52a3 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeString.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeString.php @@ -9,38 +9,9 @@ class AttributeString extends Attribute { public function __construct() { + parent::__construct(); + $this - ->addRule('key', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', - 'default' => '', - 'example' => 'fullName', - ]) - ->addRule('type', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', - 'default' => '', - 'example' => 'string', - ]) - ->addRule('status', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, or `failed`', - 'default' => '', - 'example' => 'string', - ]) - ->addRule('required', [ - 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute required?', - 'default' => false, - 'example' => true, - ]) - ->addRule('array', [ - 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute an array?', - 'default' => false, - 'example' => false, - 'require' => false - ]) ->addRule('size', [ 'type' => self::TYPE_STRING, 'description' => 'Attribute size.', diff --git a/src/Appwrite/Utopia/Response/Model/AttributeURL.php b/src/Appwrite/Utopia/Response/Model/AttributeURL.php index 0d3365f3a..3bb05f4fa 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeURL.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeURL.php @@ -9,38 +9,9 @@ class AttributeURL extends AttributeString { public function __construct() { + parent::__construct(); + $this - ->addRule('key', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute Key.', - 'default' => '', - 'example' => 'fullName', - ]) - ->addRule('type', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute type.', - 'default' => '', - 'example' => 'string', - ]) - ->addRule('status', [ - 'type' => self::TYPE_STRING, - 'description' => 'Attribute status. Possible values: `available`, `processing`, `deleting`, or `failed`', - 'default' => '', - 'example' => 'string', - ]) - ->addRule('required', [ - 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute required?', - 'default' => false, - 'example' => true, - ]) - ->addRule('array', [ - 'type' => self::TYPE_BOOLEAN, - 'description' => 'Is attribute an array?', - 'default' => false, - 'example' => false, - 'require' => false - ]) ->addRule('format', [ 'type' => self::TYPE_STRING, 'description' => 'String format.', From 7506142ab91f1a3457eda35274dd251df2e215dd Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 24 Aug 2021 14:01:53 -0400 Subject: [PATCH 021/130] Parse min and max from formatOptions for response model --- app/controllers/api/database.php | 17 ++++++++++++++-- .../Utopia/Response/Model/AttributeFloat.php | 20 +++++++++++-------- .../Response/Model/AttributeInteger.php | 20 +++++++++++-------- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 2dd5c3f0e..0490712e1 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -831,13 +831,14 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') ])]); $type = $attribute->getAttribute('type'); - $format = json_decode($attribute->getAttribute('format'), true); + $format = $attribute->getAttribute('format'); + $formatOptions = $attribute->getAttribute('formatOptions'); $model = match($type) { Database::VAR_BOOLEAN => Response::MODEL_ATTRIBUTE_BOOLEAN, Database::VAR_INTEGER => Response::MODEL_ATTRIBUTE_INTEGER, Database::VAR_FLOAT => Response::MODEL_ATTRIBUTE_FLOAT, - Database::VAR_STRING => match($format['name']) { + Database::VAR_STRING => match($format) { APP_DATABASE_ATTRIBUTE_URL => Response::MODEL_ATTRIBUTE_EMAIL, APP_DATABASE_ATTRIBUTE_IP => Response::MODEL_ATTRIBUTE_IP, APP_DATABASE_ATTRIBUTE_URL => Response::MODEL_ATTRIBUTE_URL, @@ -845,6 +846,18 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') }, default => Response::MODEL_ATTRIBUTE, }; + + // Format response if options are provided + // And response model needs to be modified + if (!empty($formatOptions) && + ($type === Response::MODEL_ATTRIBUTE_INTEGER || + $type === Response::MODEL_ATTRIBUTE_FLOAT)) + { + $attribute->setAttribute('min', $formatOptions['min'], $type); + $attribute->setAttribute('max', $formatOptions['max'], $type); + // unset($attribute['format']); + // unset($attribute['formatOptions']); + } $response->dynamic($attribute, $model); }); diff --git a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php index 669aa4dd7..a0f68ec6a 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php @@ -12,15 +12,19 @@ class AttributeFloat extends Attribute parent::__construct(); $this - ->addRule('format', [ - 'type' => self::TYPE_FLOAT, - 'description' => 'Float format.', + ->addRule('min', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Minimum value to enforce for new documents.', 'default' => null, - 'example' => \json_encode([ - 'name' => APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, - 'min' => 1.5, - 'max' => 2.5, - ]), + 'example' => 1, + 'array' => false, + 'require' => false, + ]) + ->addRule('max', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Maximum value to enforce for new documents.', + 'default' => null, + 'example' => 10, 'array' => false, 'require' => false, ]) diff --git a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php index d1ff6346f..f11344d79 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php @@ -12,15 +12,19 @@ class AttributeInteger extends Attribute parent::__construct(); $this - ->addRule('format', [ - 'type' => self::TYPE_STRING, - 'description' => 'Integer format.', + ->addRule('min', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Minimum value to enforce for new documents.', 'default' => null, - 'example' => \json_encode([ - 'name' => APP_DATABASE_ATTRIBUTE_INT_RANGE, - 'min' => 0, - 'max' => 10, - ]), + 'example' => 1, + 'array' => false, + 'require' => false, + ]) + ->addRule('max', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Maximum value to enforce for new documents.', + 'default' => null, + 'example' => 10, 'array' => false, 'require' => false, ]) From 247c3adb48776a6b7c2ea4b3ac5f8bedcf529cf4 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 24 Aug 2021 14:02:33 -0400 Subject: [PATCH 022/130] Test for attribute response models --- tests/e2e/Services/Database/DatabaseBase.php | 183 +++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index a097777f5..9068987ba 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -100,6 +100,189 @@ trait DatabaseBase return $data; } + // /** + // * @depends testCreateAttributes + // */ + // public function testAttributeResponseModels(array $data): array + public function testAttributeResponseModels() + { + $collection= $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'] + ]), [ + 'collectionId' => 'unique()', + 'name' => 'Response Models', + 'read' => ['role:all'], + 'write' => ['role:all'], + 'permission' => 'document', + ]); + + $this->assertEquals($collection['headers']['status-code'], 201); + $this->assertEquals($collection['body']['name'], 'Response Models'); + + $collectionId = $collection['body']['$id']; + + $string = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'string', + 'size' => 16, + 'required' => true, + ]); + + $email = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/email', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'email', + 'required' => true, + ]); + + $ip = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/ip', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'ip', + 'required' => true, + ]); + + $url = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/url', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'url', + 'required' => true, + ]); + + $integer = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'integer', + 'required' => true, + 'min' => 1, + 'max' => 5, + 'default' => 3 + ]); + + $float = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/float', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'float', + 'required' => true, + 'min' => 1.5, + 'max' => 5.5, + 'default' => 3.5 + ]); + + $boolean = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/boolean', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'boolean', + 'required' => true, + ]); + + $this->assertEquals(201, $string['headers']['status-code']); + $this->assertEquals('string', $string['body']['key']); + $this->assertEquals('string', $string['body']['type']); + $this->assertEquals('processing', $string['body']['status']); + $this->assertEquals(true, $string['body']['required']); + $this->assertEquals(false, $string['body']['array']); + + $this->assertEquals(16, $string['body']['size']); + + $this->assertEquals(201, $email['headers']['status-code']); + $this->assertEquals('email', $email['body']['key']); + $this->assertEquals('string', $email['body']['type']); + $this->assertEquals('processing', $email['body']['status']); + $this->assertEquals(true, $email['body']['required']); + $this->assertEquals(false, $email['body']['array']); + + $this->assertEquals('email', $string['body']['format']); + + $this->assertEquals(201, $ip['headers']['status-code']); + $this->assertEquals('ip', $ip['body']['key']); + $this->assertEquals('string', $ip['body']['type']); + $this->assertEquals('processing', $ip['body']['status']); + $this->assertEquals(true, $ip['body']['required']); + $this->assertEquals(false, $ip['body']['array']); + + $this->assertEquals('ip', $ip['body']['format']); + + $this->assertEquals(201, $url['headers']['status-code']); + $this->assertEquals('url', $url['body']['key']); + $this->assertEquals('string', $url['body']['type']); + $this->assertEquals('processing', $url['body']['status']); + $this->assertEquals(true, $url['body']['required']); + $this->assertEquals(false, $url['body']['array']); + + $this->assertEquals('url', $url['body']['format']); + + $this->assertEquals(201, $integer['headers']['status-code']); + $this->assertEquals('integer', $integer['body']['key']); + $this->assertEquals('integer', $integer['body']['type']); + $this->assertEquals('processing', $integer['body']['status']); + $this->assertEquals(true, $integer['body']['required']); + $this->assertEquals(false, $integer['body']['array']); + + $this->assertEquals(1, $integer['body']['min']); + $this->assertEquals(5, $integer['body']['max']); + $this->assertEquals(3, $integer['body']['default']); + + $this->assertEquals(201, $float['headers']['status-code']); + $this->assertEquals('float', $float['body']['key']); + $this->assertEquals('double', $float['body']['type']); + $this->assertEquals('processing', $float['body']['status']); + $this->assertEquals(true, $float['body']['required']); + $this->assertEquals(false, $float['body']['array']); + + $this->assertEquals(1.5, $float['body']['min']); + $this->assertEquals(5.5, $float['body']['max']); + $this->assertEquals(3.5, $float['body']['default']); + + $this->assertEquals(201, $boolean['headers']['status-code']); + $this->assertEquals('boolean', $boolean['body']['key']); + $this->assertEquals('boolean', $boolean['body']['type']); + $this->assertEquals('processing', $boolean['body']['status']); + $this->assertEquals(true, $boolean['body']['required']); + $this->assertEquals(false, $boolean['body']['array']); + + + // wait for database worker to create attributes + sleep(2); + + $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), []); + + var_dump($collection); + + $this->assertIsArray($collection['body']['attributes']); + $this->assertCount(7, $collection['body']['attributes']); + $this->assertEquals($collection['body']['attributes'][0]['key'], $string['body']['key']); + $this->assertEquals($collection['body']['attributes'][1]['key'], $email['body']['key']); + $this->assertEquals($collection['body']['attributes'][2]['key'], $ip['body']['key']); + $this->assertEquals($collection['body']['attributes'][3]['key'], $url['body']['key']); + $this->assertEquals($collection['body']['attributes'][4]['key'], $integer['body']['key']); + $this->assertEquals($collection['body']['attributes'][5]['key'], $float['body']['key']); + $this->assertEquals($collection['body']['attributes'][6]['key'], $boolean['body']['key']); + + // return $data; + } + /** * @depends testCreateAttributes */ From 6df9d579c4d5ada9bdf92b158e33d9750a10a035 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 24 Aug 2021 14:57:19 -0400 Subject: [PATCH 023/130] Comment out default values for non string attributes --- tests/e2e/Services/Database/DatabaseBase.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index 9068987ba..8a59f074d 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -169,7 +169,7 @@ trait DatabaseBase 'required' => true, 'min' => 1, 'max' => 5, - 'default' => 3 + // 'default' => 3 ]); $float = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/float', array_merge([ @@ -181,7 +181,7 @@ trait DatabaseBase 'required' => true, 'min' => 1.5, 'max' => 5.5, - 'default' => 3.5 + // 'default' => 3.5 ]); $boolean = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/boolean', array_merge([ @@ -209,7 +209,7 @@ trait DatabaseBase $this->assertEquals(true, $email['body']['required']); $this->assertEquals(false, $email['body']['array']); - $this->assertEquals('email', $string['body']['format']); + $this->assertEquals('email', $email['body']['format']); $this->assertEquals(201, $ip['headers']['status-code']); $this->assertEquals('ip', $ip['body']['key']); @@ -238,7 +238,7 @@ trait DatabaseBase $this->assertEquals(1, $integer['body']['min']); $this->assertEquals(5, $integer['body']['max']); - $this->assertEquals(3, $integer['body']['default']); + // $this->assertEquals(3, $integer['body']['default']); $this->assertEquals(201, $float['headers']['status-code']); $this->assertEquals('float', $float['body']['key']); @@ -249,7 +249,7 @@ trait DatabaseBase $this->assertEquals(1.5, $float['body']['min']); $this->assertEquals(5.5, $float['body']['max']); - $this->assertEquals(3.5, $float['body']['default']); + // $this->assertEquals(3.5, $float['body']['default']); $this->assertEquals(201, $boolean['headers']['status-code']); $this->assertEquals('boolean', $boolean['body']['key']); @@ -260,7 +260,7 @@ trait DatabaseBase // wait for database worker to create attributes - sleep(2); + sleep(5); $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId, array_merge([ 'content-type' => 'application/json', From 5c040bd5f370d745427baa00adaee898e4d3b15c Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 24 Aug 2021 14:57:34 -0400 Subject: [PATCH 024/130] Add min and max for response model --- app/controllers/api/database.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 0490712e1..8648aee54 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -657,6 +657,13 @@ App::post('/v1/database/collections/:collectionId/attributes/integer') ], ]), $response, $dbForInternal, $database, $audits); + $formatOptions = $attribute->getAttribute('formatOptions', []); + + if (!empty($formatOptions)) { + $attribute->setAttribute('min', \intval($formatOptions['min'])); + $attribute->setAttribute('max', \intval($formatOptions['max'])); + } + $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_INTEGER); }); @@ -712,6 +719,13 @@ App::post('/v1/database/collections/:collectionId/attributes/float') ], ]), $response, $dbForInternal, $database, $audits); + $formatOptions = $attribute->getAttribute('formatOptions', []); + + if (!empty($formatOptions)) { + $attribute->setAttribute('min', \floatval($formatOptions['min'])); + $attribute->setAttribute('max', \floatval($formatOptions['max'])); + } + $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_FLOAT); }); From 84f9f71b81dc392950545d8a7186ac01cf653603 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 25 Aug 2021 15:36:45 -0400 Subject: [PATCH 025/130] Fix race condition between database controller and worker --- app/controllers/api/database.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 8648aee54..70dc62048 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -91,16 +91,20 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $ $dbForInternal->purgeDocument('collections', $collectionId); + // Pass clone of $attribute object to workers + // so we can later modify Document to fit response model + $clone = clone $attribute; + $database ->setParam('type', DATABASE_TYPE_CREATE_ATTRIBUTE) ->setParam('collection', $collection) - ->setParam('document', $attribute) + ->setParam('document', $clone) ; $audits ->setParam('event', 'database.attributes.create') ->setParam('resource', 'database/collection/'.$collection->getId()) - ->setParam('data', $attribute) + ->setParam('data', $clone) ; $response->setStatusCode(Response::STATUS_CODE_CREATED); From 1c52b0b4dc7ab171e8a77cf9cf673aa5eb126a9e Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 25 Aug 2021 15:37:07 -0400 Subject: [PATCH 026/130] Prefer Document array data structure for attributes --- app/controllers/api/database.php | 34 +++++++++++++++----------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 70dc62048..cf7fcec02 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -835,19 +835,22 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') throw new Exception('Collection not found', 404); } - $attributes = $collection->getAttributes(); + // Search for matching attribute in collection + $attribute = null; + $attributes = $collection->getAttribute('attributes'); /** @var Document[] $attributes */ - // Search for attribute - $attributeIndex = array_search($attributeId, array_column($attributes, '$id')); + foreach ($attributes as $a) { + if ($a->getId() === $attributeId) { + $attribute = $a; + break; // stop once the attribute is found + } + } - if ($attributeIndex === false) { + if (\is_null($attribute)) { throw new Exception('Attribute not found', 404); } - $attribute = new Document([\array_merge($attributes[$attributeIndex], [ - 'collectionId' => $collectionId, - ])]); - + // Select response model based on type and format $type = $attribute->getAttribute('type'); $format = $attribute->getAttribute('format'); $formatOptions = $attribute->getAttribute('formatOptions'); @@ -857,7 +860,7 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') Database::VAR_INTEGER => Response::MODEL_ATTRIBUTE_INTEGER, Database::VAR_FLOAT => Response::MODEL_ATTRIBUTE_FLOAT, Database::VAR_STRING => match($format) { - APP_DATABASE_ATTRIBUTE_URL => Response::MODEL_ATTRIBUTE_EMAIL, + APP_DATABASE_ATTRIBUTE_EMAIL => Response::MODEL_ATTRIBUTE_EMAIL, APP_DATABASE_ATTRIBUTE_IP => Response::MODEL_ATTRIBUTE_IP, APP_DATABASE_ATTRIBUTE_URL => Response::MODEL_ATTRIBUTE_URL, default => Response::MODEL_ATTRIBUTE_STRING, @@ -865,16 +868,11 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') default => Response::MODEL_ATTRIBUTE, }; - // Format response if options are provided - // And response model needs to be modified - if (!empty($formatOptions) && - ($type === Response::MODEL_ATTRIBUTE_INTEGER || - $type === Response::MODEL_ATTRIBUTE_FLOAT)) + // Format response + if ($model === Response::MODEL_ATTRIBUTE_INTEGER || $model === Response::MODEL_ATTRIBUTE_FLOAT) { - $attribute->setAttribute('min', $formatOptions['min'], $type); - $attribute->setAttribute('max', $formatOptions['max'], $type); - // unset($attribute['format']); - // unset($attribute['formatOptions']); + $attribute->setAttribute('min', $formatOptions['min']); + $attribute->setAttribute('max', $formatOptions['max']); } $response->dynamic($attribute, $model); From 0f3c42368d0ab5b694c4e5200a6bdecfafc13502 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 25 Aug 2021 15:37:21 -0400 Subject: [PATCH 027/130] Test for proper response models for getAttribute --- tests/e2e/Services/Database/DatabaseBase.php | 111 +++++++++++++++++-- 1 file changed, 103 insertions(+), 8 deletions(-) diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index 8a59f074d..3b8ca2ca1 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -199,7 +199,6 @@ trait DatabaseBase $this->assertEquals('processing', $string['body']['status']); $this->assertEquals(true, $string['body']['required']); $this->assertEquals(false, $string['body']['array']); - $this->assertEquals(16, $string['body']['size']); $this->assertEquals(201, $email['headers']['status-code']); @@ -208,7 +207,6 @@ trait DatabaseBase $this->assertEquals('processing', $email['body']['status']); $this->assertEquals(true, $email['body']['required']); $this->assertEquals(false, $email['body']['array']); - $this->assertEquals('email', $email['body']['format']); $this->assertEquals(201, $ip['headers']['status-code']); @@ -217,7 +215,6 @@ trait DatabaseBase $this->assertEquals('processing', $ip['body']['status']); $this->assertEquals(true, $ip['body']['required']); $this->assertEquals(false, $ip['body']['array']); - $this->assertEquals('ip', $ip['body']['format']); $this->assertEquals(201, $url['headers']['status-code']); @@ -226,7 +223,6 @@ trait DatabaseBase $this->assertEquals('processing', $url['body']['status']); $this->assertEquals(true, $url['body']['required']); $this->assertEquals(false, $url['body']['array']); - $this->assertEquals('url', $url['body']['format']); $this->assertEquals(201, $integer['headers']['status-code']); @@ -235,7 +231,6 @@ trait DatabaseBase $this->assertEquals('processing', $integer['body']['status']); $this->assertEquals(true, $integer['body']['required']); $this->assertEquals(false, $integer['body']['array']); - $this->assertEquals(1, $integer['body']['min']); $this->assertEquals(5, $integer['body']['max']); // $this->assertEquals(3, $integer['body']['default']); @@ -246,7 +241,6 @@ trait DatabaseBase $this->assertEquals('processing', $float['body']['status']); $this->assertEquals(true, $float['body']['required']); $this->assertEquals(false, $float['body']['array']); - $this->assertEquals(1.5, $float['body']['min']); $this->assertEquals(5.5, $float['body']['max']); // $this->assertEquals(3.5, $float['body']['default']); @@ -258,16 +252,117 @@ trait DatabaseBase $this->assertEquals(true, $boolean['body']['required']); $this->assertEquals(false, $boolean['body']['array']); - // wait for database worker to create attributes sleep(5); + $stringResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$string['body']['key']}",array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $emailResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$email['body']['key']}",array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $ipResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$ip['body']['key']}",array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $urlResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$url['body']['key']}",array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $integerResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$integer['body']['key']}",array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $floatResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$float['body']['key']}",array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $booleanResponse = $this->client->call(Client::METHOD_GET, "/database/collections/{$collectionId}/attributes/{$collectionId}_{$boolean['body']['key']}",array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $stringResponse['headers']['status-code']); + $this->assertEquals($string['body']['key'], $stringResponse['body']['key']); + $this->assertEquals($string['body']['type'], $stringResponse['body']['type']); + $this->assertEquals('available', $stringResponse['body']['status']); + $this->assertEquals($string['body']['required'], $stringResponse['body']['required']); + $this->assertEquals($string['body']['array'], $stringResponse['body']['array']); + $this->assertEquals(16, $stringResponse['body']['size']); + + $this->assertEquals(200, $emailResponse['headers']['status-code']); + $this->assertEquals($email['body']['key'], $emailResponse['body']['key']); + $this->assertEquals($email['body']['type'], $emailResponse['body']['type']); + $this->assertEquals('available', $emailResponse['body']['status']); + $this->assertEquals($email['body']['required'], $emailResponse['body']['required']); + $this->assertEquals($email['body']['array'], $emailResponse['body']['array']); + $this->assertEquals($email['body']['format'], $emailResponse['body']['format']); + + $this->assertEquals(200, $ipResponse['headers']['status-code']); + $this->assertEquals($ip['body']['key'], $ipResponse['body']['key']); + $this->assertEquals($ip['body']['type'], $ipResponse['body']['type']); + $this->assertEquals('available', $ipResponse['body']['status']); + $this->assertEquals($ip['body']['required'], $ipResponse['body']['required']); + $this->assertEquals($ip['body']['array'], $ipResponse['body']['array']); + $this->assertEquals($ip['body']['format'], $ipResponse['body']['format']); + + $this->assertEquals(200, $urlResponse['headers']['status-code']); + $this->assertEquals($url['body']['key'], $urlResponse['body']['key']); + $this->assertEquals($url['body']['type'], $urlResponse['body']['type']); + $this->assertEquals('available', $urlResponse['body']['status']); + $this->assertEquals($url['body']['required'], $urlResponse['body']['required']); + $this->assertEquals($url['body']['array'], $urlResponse['body']['array']); + $this->assertEquals($url['body']['format'], $urlResponse['body']['format']); + + $this->assertEquals(200, $integerResponse['headers']['status-code']); + $this->assertEquals($integer['body']['key'], $integerResponse['body']['key']); + $this->assertEquals($integer['body']['type'], $integerResponse['body']['type']); + $this->assertEquals('available', $integerResponse['body']['status']); + $this->assertEquals($integer['body']['required'], $integerResponse['body']['required']); + $this->assertEquals($integer['body']['array'], $integerResponse['body']['array']); + $this->assertEquals($integer['body']['min'], $integerResponse['body']['min']); + $this->assertEquals($integer['body']['max'], $integerResponse['body']['max']); + // $this->assertEquals(3, $integer['body']['default']); + + $this->assertEquals(200, $floatResponse['headers']['status-code']); + $this->assertEquals($float['body']['key'], $floatResponse['body']['key']); + $this->assertEquals($float['body']['type'], $floatResponse['body']['type']); + $this->assertEquals('available', $floatResponse['body']['status']); + $this->assertEquals($float['body']['required'], $floatResponse['body']['required']); + $this->assertEquals($float['body']['array'], $floatResponse['body']['array']); + $this->assertEquals($float['body']['min'], $floatResponse['body']['min']); + $this->assertEquals($float['body']['max'], $floatResponse['body']['max']); + // $this->assertEquals(3.5, $float['body']['default']); + + $this->assertEquals(200, $booleanResponse['headers']['status-code']); + $this->assertEquals($boolean['body']['key'], $booleanResponse['body']['key']); + $this->assertEquals($boolean['body']['type'], $booleanResponse['body']['type']); + $this->assertEquals('available', $booleanResponse['body']['status']); + $this->assertEquals($boolean['body']['required'], $booleanResponse['body']['required']); + $this->assertEquals($boolean['body']['array'], $booleanResponse['body']['array']); + $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), []); + ])); + // TODO@kodumbeats test for proper attribute response in collection var_dump($collection); $this->assertIsArray($collection['body']['attributes']); From 547241523153922eb1206a05dc7c97a19d68f634 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 25 Aug 2021 16:43:15 -0400 Subject: [PATCH 028/130] Encode attribute default as json string --- app/controllers/api/database.php | 16 ++++++++++++++-- app/workers/database.php | 3 +++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index cf7fcec02..080ddb954 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -55,7 +55,8 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $ $formatOptions = $attribute->getAttribute('formatOptions', []); $filters = $attribute->getAttribute('filters', []); // filters are hidden from the endpoint $default = $attribute->getAttribute('default', null); - $default = (empty($default)) ? null : (int)$default; + // $default = (empty($default)) ? null : (int)$default; + $defaultEncoded = (\is_null($default)) ? '' : json_encode(['value'=>$default]); $collection = $dbForInternal->getDocument('collections', $collectionId); @@ -69,6 +70,11 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $ } } + // Must throw here since dbForExternal->createAttribute is performed by db worker + if ($required && $default) { + throw new Exception('Cannot set default value for required attribute', 400); + } + try { $attribute = $dbForInternal->createDocument('attributes', new Document([ '$id' => $collectionId.'_'.$attributeId, @@ -79,7 +85,7 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $ 'size' => $size, 'required' => $required, 'signed' => $signed, - 'default' => $default, + 'default' => $defaultEncoded, 'array' => $array, 'format' => $format, 'formatOptions' => $formatOptions, @@ -91,6 +97,9 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $ $dbForInternal->purgeDocument('collections', $collectionId); + // Only attributes table needs $default encoded as a string, reset before response + $attribute->setAttribute('default', $default); + // Pass clone of $attribute object to workers // so we can later modify Document to fit response model $clone = clone $attribute; @@ -869,6 +878,9 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') }; // Format response + $default = \json_decode($attribute->getAttribute('default', []), true)['value'] ?? null; + $attribute->setAttribute('default', $default); + if ($model === Response::MODEL_ATTRIBUTE_INTEGER || $model === Response::MODEL_ATTRIBUTE_FLOAT) { $attribute->setAttribute('min', $formatOptions['min']); diff --git a/app/workers/database.php b/app/workers/database.php index fd3824615..381fb1c5e 100644 --- a/app/workers/database.php +++ b/app/workers/database.php @@ -88,6 +88,9 @@ class DatabaseV1 extends Worker if(!$dbForExternal->createAttribute($collectionId, $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { throw new Exception('Failed to create Attribute'); } + if (!\is_null($default)) { + $attribute->setAttribute('default', json_encode(['value' => $default])); + } $dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available')); } catch (\Throwable $th) { Console::error($th->getMessage()); From 2ead9c52f6e01ee0b19f1569a42379ebeef93d02 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 25 Aug 2021 16:44:07 -0400 Subject: [PATCH 029/130] Test attribute default values have proper casting in response model --- tests/e2e/Services/Database/DatabaseBase.php | 57 ++++++++++++-------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index 3b8ca2ca1..b175f8a79 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -130,7 +130,8 @@ trait DatabaseBase ]), [ 'attributeId' => 'string', 'size' => 16, - 'required' => true, + 'required' => false, + 'default' => 'default', ]); $email = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/email', array_merge([ @@ -139,7 +140,8 @@ trait DatabaseBase 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'attributeId' => 'email', - 'required' => true, + 'required' => false, + 'default' => 'default@example.com', ]); $ip = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/ip', array_merge([ @@ -148,7 +150,8 @@ trait DatabaseBase 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'attributeId' => 'ip', - 'required' => true, + 'required' => false, + 'default' => '192.0.2.0', ]); $url = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/url', array_merge([ @@ -157,7 +160,8 @@ trait DatabaseBase 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'attributeId' => 'url', - 'required' => true, + 'required' => false, + 'default' => 'http://example.com', ]); $integer = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([ @@ -166,10 +170,10 @@ trait DatabaseBase 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'attributeId' => 'integer', - 'required' => true, + 'required' => false, 'min' => 1, 'max' => 5, - // 'default' => 3 + 'default' => 3 ]); $float = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/float', array_merge([ @@ -178,10 +182,10 @@ trait DatabaseBase 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'attributeId' => 'float', - 'required' => true, + 'required' => false, 'min' => 1.5, 'max' => 5.5, - // 'default' => 3.5 + 'default' => 3.5 ]); $boolean = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/boolean', array_merge([ @@ -190,67 +194,73 @@ trait DatabaseBase 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'attributeId' => 'boolean', - 'required' => true, + 'required' => false, + 'default' => true, ]); $this->assertEquals(201, $string['headers']['status-code']); $this->assertEquals('string', $string['body']['key']); $this->assertEquals('string', $string['body']['type']); $this->assertEquals('processing', $string['body']['status']); - $this->assertEquals(true, $string['body']['required']); + $this->assertEquals(false, $string['body']['required']); $this->assertEquals(false, $string['body']['array']); $this->assertEquals(16, $string['body']['size']); + $this->assertEquals('default', $string['body']['default']); $this->assertEquals(201, $email['headers']['status-code']); $this->assertEquals('email', $email['body']['key']); $this->assertEquals('string', $email['body']['type']); $this->assertEquals('processing', $email['body']['status']); - $this->assertEquals(true, $email['body']['required']); + $this->assertEquals(false, $email['body']['required']); $this->assertEquals(false, $email['body']['array']); $this->assertEquals('email', $email['body']['format']); + $this->assertEquals('default@example.com', $email['body']['default']); $this->assertEquals(201, $ip['headers']['status-code']); $this->assertEquals('ip', $ip['body']['key']); $this->assertEquals('string', $ip['body']['type']); $this->assertEquals('processing', $ip['body']['status']); - $this->assertEquals(true, $ip['body']['required']); + $this->assertEquals(false, $ip['body']['required']); $this->assertEquals(false, $ip['body']['array']); $this->assertEquals('ip', $ip['body']['format']); + $this->assertEquals('192.0.2.0', $ip['body']['default']); $this->assertEquals(201, $url['headers']['status-code']); $this->assertEquals('url', $url['body']['key']); $this->assertEquals('string', $url['body']['type']); $this->assertEquals('processing', $url['body']['status']); - $this->assertEquals(true, $url['body']['required']); + $this->assertEquals(false, $url['body']['required']); $this->assertEquals(false, $url['body']['array']); $this->assertEquals('url', $url['body']['format']); + $this->assertEquals('http://example.com', $url['body']['default']); $this->assertEquals(201, $integer['headers']['status-code']); $this->assertEquals('integer', $integer['body']['key']); $this->assertEquals('integer', $integer['body']['type']); $this->assertEquals('processing', $integer['body']['status']); - $this->assertEquals(true, $integer['body']['required']); + $this->assertEquals(false, $integer['body']['required']); $this->assertEquals(false, $integer['body']['array']); $this->assertEquals(1, $integer['body']['min']); $this->assertEquals(5, $integer['body']['max']); - // $this->assertEquals(3, $integer['body']['default']); + $this->assertEquals(3, $integer['body']['default']); $this->assertEquals(201, $float['headers']['status-code']); $this->assertEquals('float', $float['body']['key']); $this->assertEquals('double', $float['body']['type']); $this->assertEquals('processing', $float['body']['status']); - $this->assertEquals(true, $float['body']['required']); + $this->assertEquals(false, $float['body']['required']); $this->assertEquals(false, $float['body']['array']); $this->assertEquals(1.5, $float['body']['min']); $this->assertEquals(5.5, $float['body']['max']); - // $this->assertEquals(3.5, $float['body']['default']); + $this->assertEquals(3.5, $float['body']['default']); $this->assertEquals(201, $boolean['headers']['status-code']); $this->assertEquals('boolean', $boolean['body']['key']); $this->assertEquals('boolean', $boolean['body']['type']); $this->assertEquals('processing', $boolean['body']['status']); - $this->assertEquals(true, $boolean['body']['required']); + $this->assertEquals(false, $boolean['body']['required']); $this->assertEquals(false, $boolean['body']['array']); + $this->assertEquals(true, $boolean['body']['default']); // wait for database worker to create attributes sleep(5); @@ -304,6 +314,7 @@ trait DatabaseBase $this->assertEquals($string['body']['required'], $stringResponse['body']['required']); $this->assertEquals($string['body']['array'], $stringResponse['body']['array']); $this->assertEquals(16, $stringResponse['body']['size']); + $this->assertEquals($string['body']['default'], $stringResponse['body']['default']); $this->assertEquals(200, $emailResponse['headers']['status-code']); $this->assertEquals($email['body']['key'], $emailResponse['body']['key']); @@ -312,6 +323,7 @@ trait DatabaseBase $this->assertEquals($email['body']['required'], $emailResponse['body']['required']); $this->assertEquals($email['body']['array'], $emailResponse['body']['array']); $this->assertEquals($email['body']['format'], $emailResponse['body']['format']); + $this->assertEquals($email['body']['default'], $emailResponse['body']['default']); $this->assertEquals(200, $ipResponse['headers']['status-code']); $this->assertEquals($ip['body']['key'], $ipResponse['body']['key']); @@ -320,6 +332,7 @@ trait DatabaseBase $this->assertEquals($ip['body']['required'], $ipResponse['body']['required']); $this->assertEquals($ip['body']['array'], $ipResponse['body']['array']); $this->assertEquals($ip['body']['format'], $ipResponse['body']['format']); + $this->assertEquals($ip['body']['default'], $ipResponse['body']['default']); $this->assertEquals(200, $urlResponse['headers']['status-code']); $this->assertEquals($url['body']['key'], $urlResponse['body']['key']); @@ -328,6 +341,7 @@ trait DatabaseBase $this->assertEquals($url['body']['required'], $urlResponse['body']['required']); $this->assertEquals($url['body']['array'], $urlResponse['body']['array']); $this->assertEquals($url['body']['format'], $urlResponse['body']['format']); + $this->assertEquals($url['body']['default'], $urlResponse['body']['default']); $this->assertEquals(200, $integerResponse['headers']['status-code']); $this->assertEquals($integer['body']['key'], $integerResponse['body']['key']); @@ -337,7 +351,7 @@ trait DatabaseBase $this->assertEquals($integer['body']['array'], $integerResponse['body']['array']); $this->assertEquals($integer['body']['min'], $integerResponse['body']['min']); $this->assertEquals($integer['body']['max'], $integerResponse['body']['max']); - // $this->assertEquals(3, $integer['body']['default']); + $this->assertEquals($integer['body']['default'], $integerResponse['body']['default']); $this->assertEquals(200, $floatResponse['headers']['status-code']); $this->assertEquals($float['body']['key'], $floatResponse['body']['key']); @@ -347,7 +361,7 @@ trait DatabaseBase $this->assertEquals($float['body']['array'], $floatResponse['body']['array']); $this->assertEquals($float['body']['min'], $floatResponse['body']['min']); $this->assertEquals($float['body']['max'], $floatResponse['body']['max']); - // $this->assertEquals(3.5, $float['body']['default']); + $this->assertEquals($float['body']['default'], $floatResponse['body']['default']); $this->assertEquals(200, $booleanResponse['headers']['status-code']); $this->assertEquals($boolean['body']['key'], $booleanResponse['body']['key']); @@ -355,6 +369,7 @@ trait DatabaseBase $this->assertEquals('available', $booleanResponse['body']['status']); $this->assertEquals($boolean['body']['required'], $booleanResponse['body']['required']); $this->assertEquals($boolean['body']['array'], $booleanResponse['body']['array']); + $this->assertEquals($boolean['body']['default'], $booleanResponse['body']['default']); $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId, array_merge([ 'content-type' => 'application/json', @@ -363,7 +378,7 @@ trait DatabaseBase ])); // TODO@kodumbeats test for proper attribute response in collection - var_dump($collection); + // var_dump($collection); $this->assertIsArray($collection['body']['attributes']); $this->assertCount(7, $collection['body']['attributes']); From 87de870093c8d15e96718cfe0702e5f290b141f1 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 13:09:56 -0400 Subject: [PATCH 030/130] Accept callback to get nested type of rule for Document arrays --- src/Appwrite/Utopia/Response.php | 7 ++++--- src/Appwrite/Utopia/Response/Model.php | 9 ++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 1c73ac14c..67d47eea4 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -340,11 +340,12 @@ class Response extends SwooleResponse foreach ($data[$key] as &$item) { if ($item instanceof Document) { - if (!array_key_exists($rule['type'], $this->models)) { - throw new Exception('Missing model for rule: '. $rule['type']); + $ruleType = (!\is_null($rule['getNestedType'])) ? $rule['getNestedType']($item) : $rule['type']; + if (!array_key_exists($ruleType, $this->models)) { + throw new Exception('Missing model for rule: '. $ruleType); } - $item = $this->output($item, $rule['type']); + $item = $this->output($item, $ruleType); } } } diff --git a/src/Appwrite/Utopia/Response/Model.php b/src/Appwrite/Utopia/Response/Model.php index 73b660963..40a882527 100644 --- a/src/Appwrite/Utopia/Response/Model.php +++ b/src/Appwrite/Utopia/Response/Model.php @@ -68,8 +68,14 @@ abstract class Model /** * Add a New Rule + * If rule is an array of documents with varying models + * Pass callable $getNestedType that accepts Document and returns the nested response type + * + * @param string $key + * @param array $options + * @param callable $getNestedType function(Document $value): string */ - protected function addRule(string $key, array $options): self + protected function addRule(string $key, array $options, callable $getNestedType = null): self { $this->rules[$key] = array_merge([ 'require' => true, @@ -78,6 +84,7 @@ abstract class Model 'default' => null, 'example' => '', 'array' => false, + 'getNestedType' => $getNestedType ], $options); return $this; From ddde87f32ac4f4291221df6ebf20bfbfbda9918b Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 13:11:29 -0400 Subject: [PATCH 031/130] Create custom response model for attributeList --- src/Appwrite/Utopia/Response.php | 3 +- .../Utopia/Response/Model/AttributeList.php | 62 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/Appwrite/Utopia/Response/Model/AttributeList.php diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 67d47eea4..a653ddbb0 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -11,6 +11,7 @@ use Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response\Model\None; use Appwrite\Utopia\Response\Model\Any; use Appwrite\Utopia\Response\Model\Attribute; +use Appwrite\Utopia\Response\Model\AttributeList; use Appwrite\Utopia\Response\Model\AttributeString; use Appwrite\Utopia\Response\Model\AttributeInteger; use Appwrite\Utopia\Response\Model\AttributeFloat; @@ -170,7 +171,6 @@ class Response extends SwooleResponse ->setModel(new ErrorDev()) // Lists ->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('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER)) @@ -195,6 +195,7 @@ class Response extends SwooleResponse // Entities ->setModel(new Collection()) ->setModel(new Attribute()) + ->setModel(new AttributeList()) ->setModel(new AttributeString()) ->setModel(new AttributeInteger()) ->setModel(new AttributeFloat()) diff --git a/src/Appwrite/Utopia/Response/Model/AttributeList.php b/src/Appwrite/Utopia/Response/Model/AttributeList.php new file mode 100644 index 000000000..5484db61f --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/AttributeList.php @@ -0,0 +1,62 @@ +addRule('sum', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total sum of items in the list.', + 'default' => 0, + 'example' => 5, + ]) + ->addRule('attributes', [ + 'type' => Response::MODEL_ATTRIBUTE, + 'description' => 'List of attributes.', + 'default' => [], + 'array' => true, + 'getNestedType' => function(Document $attribute) { + return match($attribute->getAttribute('type')) { + self::TYPE_BOOLEAN => Response::MODEL_ATTRIBUTE_BOOLEAN, + self::TYPE_INTEGER=> Response::MODEL_ATTRIBUTE_INTEGER, + self::TYPE_FLOAT => Response::MODEL_ATTRIBUTE_FLOAT, + self::TYPE_STRING => match($attribute->getAttribute('format')) { + APP_DATABASE_ATTRIBUTE_EMAIL => Response::MODEL_ATTRIBUTE_EMAIL, + APP_DATABASE_ATTRIBUTE_IP => Response::MODEL_ATTRIBUTE_IP, + APP_DATABASE_ATTRIBUTE_URL => Response::MODEL_ATTRIBUTE_URL, + default => Response::MODEL_ATTRIBUTE_STRING, + }, + default => Response::MODEL_ATTRIBUTE, + }; + }, + ]) + ; + } + + /** + * Get Name + * + * @return string + */ + public function getName():string + { + return 'Attributes List'; + } + + /** + * Get Collection + * + * @return string + */ + public function getType():string + { + return Response::MODEL_ATTRIBUTE_LIST; + } +} \ No newline at end of file From a619c26aff08dc7343bcc797c49f2dc1997eb436 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 13:12:16 -0400 Subject: [PATCH 032/130] Use database filter to encode default value as JSON string to preseve type --- app/config/collections2.php | 2 +- app/controllers/api/database.php | 15 ++------------- app/init.php | 14 +++++++++++++- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/app/config/collections2.php b/app/config/collections2.php index 8079bdd7c..a01133fdd 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -174,7 +174,7 @@ $collections = [ 'required' => false, 'default' => null, 'array' => false, - 'filters' => [], + 'filters' => ['defaultValue'], ], [ '$id' => 'signed', diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 080ddb954..c01d38fa6 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -55,8 +55,6 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $ $formatOptions = $attribute->getAttribute('formatOptions', []); $filters = $attribute->getAttribute('filters', []); // filters are hidden from the endpoint $default = $attribute->getAttribute('default', null); - // $default = (empty($default)) ? null : (int)$default; - $defaultEncoded = (\is_null($default)) ? '' : json_encode(['value'=>$default]); $collection = $dbForInternal->getDocument('collections', $collectionId); @@ -85,7 +83,7 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $ 'size' => $size, 'required' => $required, 'signed' => $signed, - 'default' => $defaultEncoded, + 'default' => $default, 'array' => $array, 'format' => $format, 'formatOptions' => $formatOptions, @@ -97,9 +95,6 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $ $dbForInternal->purgeDocument('collections', $collectionId); - // Only attributes table needs $default encoded as a string, reset before response - $attribute->setAttribute('default', $default); - // Pass clone of $attribute object to workers // so we can later modify Document to fit response model $clone = clone $attribute; @@ -805,13 +800,7 @@ App::get('/v1/database/collections/:collectionId/attributes') throw new Exception('Collection not found', 404); } - $attributes = $collection->getAttributes(); - - $attributes = array_map(function ($attribute) use ($collection) { - return new Document([\array_merge($attribute, [ - 'collectionId' => $collection->getId(), - ])]); - }, $attributes); + $attributes = $collection->getAttribute('attributes', []); $response->dynamic(new Document([ 'sum' => \count($attributes), diff --git a/app/init.php b/app/init.php index f6b82bba3..8f93c717d 100644 --- a/app/init.php +++ b/app/init.php @@ -145,7 +145,7 @@ if(!empty($user) || !empty($pass)) { } /** - * DB Filters + * Old DB Filters */ DatabaseOld::addFilter('json', function($value) { @@ -181,6 +181,18 @@ DatabaseOld::addFilter('encrypt', } ); +/** + * New DB Filters + */ +Database::addFilter('defaultValue', + function($value) { + return \json_encode(['value' => $value]); + }, + function($value) { + return \json_decode($value, true)['value']; + } +); + Database::addFilter('subQueryAttributes', function($value) { return null; From 6c5ac312fa95878cc3d755d187ad2b8a0d188074 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 16:24:48 -0400 Subject: [PATCH 033/130] Inherit from Attribute model since custom strings should not respond with size --- src/Appwrite/Utopia/Response/Model/AttributeEmail.php | 4 ++-- src/Appwrite/Utopia/Response/Model/AttributeIP.php | 4 ++-- src/Appwrite/Utopia/Response/Model/AttributeURL.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php index 1c058e6b0..15bc4674e 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php @@ -3,9 +3,9 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; -use Appwrite\Utopia\Response\Model\AttributeString; +use Appwrite\Utopia\Response\Model\Attribute; -class AttributeEmail extends AttributeString +class AttributeEmail extends Attribute { public function __construct() { diff --git a/src/Appwrite/Utopia/Response/Model/AttributeIP.php b/src/Appwrite/Utopia/Response/Model/AttributeIP.php index 8272c2760..ec3a9cfed 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeIP.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeIP.php @@ -3,9 +3,9 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; -use Appwrite\Utopia\Response\Model\AttributeString; +use Appwrite\Utopia\Response\Model\Attribute; -class AttributeIP extends AttributeString +class AttributeIP extends Attribute { public function __construct() { diff --git a/src/Appwrite/Utopia/Response/Model/AttributeURL.php b/src/Appwrite/Utopia/Response/Model/AttributeURL.php index 3bb05f4fa..3caddbc10 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeURL.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeURL.php @@ -3,9 +3,9 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; -use Appwrite\Utopia\Response\Model\AttributeString; +use Appwrite\Utopia\Response\Model\Attribute; -class AttributeURL extends AttributeString +class AttributeURL extends Attribute { public function __construct() { From fb80088286aee0c37d980779c6cbe0109f919a13 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 16:27:48 -0400 Subject: [PATCH 034/130] Create range filter for numeric attributes --- app/config/collections2.php | 2 +- app/controllers/api/database.php | 21 ++++++--------------- app/init.php | 26 ++++++++++++++++++++++++-- app/workers/database.php | 3 --- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/app/config/collections2.php b/app/config/collections2.php index a01133fdd..a14d58cf2 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -214,7 +214,7 @@ $collections = [ 'required' => false, 'default' => new stdClass, 'array' => false, - 'filters' => ['json'], + 'filters' => ['json', 'range'], ], [ '$id' => 'filters', diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index c01d38fa6..3b839cae6 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -800,7 +800,9 @@ App::get('/v1/database/collections/:collectionId/attributes') throw new Exception('Collection not found', 404); } - $attributes = $collection->getAttribute('attributes', []); + $attributes = $dbForInternal->find('attributes', [ + new Query('collectionId', Query::TYPE_EQUAL, [$collection->getId()]) + ], 100); $response->dynamic(new Document([ 'sum' => \count($attributes), @@ -833,18 +835,9 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') throw new Exception('Collection not found', 404); } - // Search for matching attribute in collection - $attribute = null; - $attributes = $collection->getAttribute('attributes'); /** @var Document[] $attributes */ + $attribute = $dbForInternal->getDocument('attributes', $attributeId); - foreach ($attributes as $a) { - if ($a->getId() === $attributeId) { - $attribute = $a; - break; // stop once the attribute is found - } - } - - if (\is_null($attribute)) { + if ($attribute === false) { throw new Exception('Attribute not found', 404); } @@ -867,9 +860,7 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') }; // Format response - $default = \json_decode($attribute->getAttribute('default', []), true)['value'] ?? null; - $attribute->setAttribute('default', $default); - + // TODO@kodumbeats test if this is necessary with range filter if ($model === Response::MODEL_ATTRIBUTE_INTEGER || $model === Response::MODEL_ATTRIBUTE_FLOAT) { $attribute->setAttribute('min', $formatOptions['min']); diff --git a/app/init.php b/app/init.php index 8f93c717d..eaf8358a1 100644 --- a/app/init.php +++ b/app/init.php @@ -186,10 +186,32 @@ DatabaseOld::addFilter('encrypt', */ Database::addFilter('defaultValue', function($value) { - return \json_encode(['value' => $value]); + return json_encode(['value' => $value]); }, function($value) { - return \json_decode($value, true)['value']; + return json_decode($value, true)['value']; + } +); + +Database::addFilter('range', + function($value, Document $attribute) { + if ($attribute->isSet('min')) { + $attribute->removeAttribute('min'); + } + if ($attribute->isSet('max')) { + $attribute->removeAttribute('max'); + } + return $value; + }, + function($value, Document $attribute) { + $formatOptions = json_decode($attribute->getAttribute('formatOptions', []), true); + if (isset($formatOptions['min']) || isset($formatOptions['max'])) { + $attribute + ->setAttribute('min', $formatOptions['min']) + ->setAttribute('max', $formatOptions['max']) + ; + } + return $value; } ); diff --git a/app/workers/database.php b/app/workers/database.php index 381fb1c5e..fd3824615 100644 --- a/app/workers/database.php +++ b/app/workers/database.php @@ -88,9 +88,6 @@ class DatabaseV1 extends Worker if(!$dbForExternal->createAttribute($collectionId, $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { throw new Exception('Failed to create Attribute'); } - if (!\is_null($default)) { - $attribute->setAttribute('default', json_encode(['value' => $default])); - } $dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available')); } catch (\Throwable $th) { Console::error($th->getMessage()); From 783e9d86c0049fe06dc070078cb57f35b7c7fc74 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 16:27:59 -0400 Subject: [PATCH 035/130] Fix response models --- src/Appwrite/Utopia/Response/Model.php | 2 +- src/Appwrite/Utopia/Response/Model/AttributeFloat.php | 8 ++++---- src/Appwrite/Utopia/Response/Model/AttributeList.php | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Model.php b/src/Appwrite/Utopia/Response/Model.php index 40a882527..a763692c7 100644 --- a/src/Appwrite/Utopia/Response/Model.php +++ b/src/Appwrite/Utopia/Response/Model.php @@ -8,7 +8,7 @@ abstract class Model { const TYPE_STRING = 'string'; const TYPE_INTEGER = 'integer'; - const TYPE_FLOAT = 'float'; + const TYPE_FLOAT = 'double'; const TYPE_BOOLEAN = 'boolean'; const TYPE_JSON = 'json'; diff --git a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php index a0f68ec6a..7c4ead9e7 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php @@ -13,18 +13,18 @@ class AttributeFloat extends Attribute $this ->addRule('min', [ - 'type' => self::TYPE_INTEGER, + 'type' => self::TYPE_FLOAT, 'description' => 'Minimum value to enforce for new documents.', 'default' => null, - 'example' => 1, + 'example' => 1.5, 'array' => false, 'require' => false, ]) ->addRule('max', [ - 'type' => self::TYPE_INTEGER, + 'type' => self::TYPE_FLOAT, 'description' => 'Maximum value to enforce for new documents.', 'default' => null, - 'example' => 10, + 'example' => 10.5, 'array' => false, 'require' => false, ]) diff --git a/src/Appwrite/Utopia/Response/Model/AttributeList.php b/src/Appwrite/Utopia/Response/Model/AttributeList.php index 5484db61f..26ea146ec 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeList.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeList.php @@ -25,7 +25,7 @@ class AttributeList extends Model 'getNestedType' => function(Document $attribute) { return match($attribute->getAttribute('type')) { self::TYPE_BOOLEAN => Response::MODEL_ATTRIBUTE_BOOLEAN, - self::TYPE_INTEGER=> Response::MODEL_ATTRIBUTE_INTEGER, + self::TYPE_INTEGER => Response::MODEL_ATTRIBUTE_INTEGER, self::TYPE_FLOAT => Response::MODEL_ATTRIBUTE_FLOAT, self::TYPE_STRING => match($attribute->getAttribute('format')) { APP_DATABASE_ATTRIBUTE_EMAIL => Response::MODEL_ATTRIBUTE_EMAIL, From a9c1a91a3eb37488293dfa91d3c38775aae3ce94 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 16:30:26 -0400 Subject: [PATCH 036/130] Test response model for listAttributes endpoint --- tests/e2e/Services/Database/DatabaseBase.php | 74 +++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index b175f8a79..52de1e89f 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -371,13 +371,83 @@ trait DatabaseBase $this->assertEquals($boolean['body']['array'], $booleanResponse['body']['array']); $this->assertEquals($boolean['body']['default'], $booleanResponse['body']['default']); - $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId, array_merge([ + $attributes = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId . '/attributes', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - // TODO@kodumbeats test for proper attribute response in collection + $this->assertEquals(200, $attributes['headers']['status-code']); + $this->assertEquals(7, $attributes['body']['sum']); + + $attributes = $attributes['body']['attributes']; + + $this->assertIsArray($attributes); + $this->assertCount(7, $attributes); + + $this->assertEquals($stringResponse['body']['key'], $attributes[0]['key']); + $this->assertEquals($stringResponse['body']['type'], $attributes[0]['type']); + $this->assertEquals($stringResponse['body']['status'], $attributes[0]['status']); + $this->assertEquals($stringResponse['body']['required'], $attributes[0]['required']); + $this->assertEquals($stringResponse['body']['array'], $attributes[0]['array']); + $this->assertEquals($stringResponse['body']['size'], $attributes[0]['size']); + $this->assertEquals($stringResponse['body']['default'], $attributes[0]['default']); + + $this->assertEquals($emailResponse['body']['key'], $attributes[1]['key']); + $this->assertEquals($emailResponse['body']['type'], $attributes[1]['type']); + $this->assertEquals($emailResponse['body']['status'], $attributes[1]['status']); + $this->assertEquals($emailResponse['body']['required'], $attributes[1]['required']); + $this->assertEquals($emailResponse['body']['array'], $attributes[1]['array']); + $this->assertEquals($emailResponse['body']['default'], $attributes[1]['default']); + $this->assertEquals($emailResponse['body']['format'], $attributes[1]['format']); + + $this->assertEquals($ipResponse['body']['key'], $attributes[2]['key']); + $this->assertEquals($ipResponse['body']['type'], $attributes[2]['type']); + $this->assertEquals($ipResponse['body']['status'], $attributes[2]['status']); + $this->assertEquals($ipResponse['body']['required'], $attributes[2]['required']); + $this->assertEquals($ipResponse['body']['array'], $attributes[2]['array']); + $this->assertEquals($ipResponse['body']['default'], $attributes[2]['default']); + $this->assertEquals($ipResponse['body']['format'], $attributes[2]['format']); + + $this->assertEquals($urlResponse['body']['key'], $attributes[3]['key']); + $this->assertEquals($urlResponse['body']['type'], $attributes[3]['type']); + $this->assertEquals($urlResponse['body']['status'], $attributes[3]['status']); + $this->assertEquals($urlResponse['body']['required'], $attributes[3]['required']); + $this->assertEquals($urlResponse['body']['array'], $attributes[3]['array']); + $this->assertEquals($urlResponse['body']['default'], $attributes[3]['default']); + $this->assertEquals($urlResponse['body']['format'], $attributes[3]['format']); + + $this->assertEquals($integerResponse['body']['key'], $attributes[4]['key']); + $this->assertEquals($integerResponse['body']['type'], $attributes[4]['type']); + $this->assertEquals($integerResponse['body']['status'], $attributes[4]['status']); + $this->assertEquals($integerResponse['body']['required'], $attributes[4]['required']); + $this->assertEquals($integerResponse['body']['array'], $attributes[4]['array']); + $this->assertEquals($integerResponse['body']['default'], $attributes[4]['default']); + $this->assertEquals($integerResponse['body']['min'], $attributes[4]['min']); + $this->assertEquals($integerResponse['body']['max'], $attributes[4]['max']); + + $this->assertEquals($floatResponse['body']['key'], $attributes[5]['key']); + $this->assertEquals($floatResponse['body']['type'], $attributes[5]['type']); + $this->assertEquals($floatResponse['body']['status'], $attributes[5]['status']); + $this->assertEquals($floatResponse['body']['required'], $attributes[5]['required']); + $this->assertEquals($floatResponse['body']['array'], $attributes[5]['array']); + $this->assertEquals($floatResponse['body']['default'], $attributes[5]['default']); + $this->assertEquals($floatResponse['body']['min'], $attributes[5]['min']); + $this->assertEquals($floatResponse['body']['max'], $attributes[5]['max']); + + $this->assertEquals($booleanResponse['body']['key'], $attributes[6]['key']); + $this->assertEquals($booleanResponse['body']['type'], $attributes[6]['type']); + $this->assertEquals($booleanResponse['body']['status'], $attributes[6]['status']); + $this->assertEquals($booleanResponse['body']['required'], $attributes[6]['required']); + $this->assertEquals($booleanResponse['body']['array'], $attributes[6]['array']); + $this->assertEquals($booleanResponse['body']['default'], $attributes[6]['default']); + + $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + // var_dump($collection); $this->assertIsArray($collection['body']['attributes']); From f5b25c28dfbdaf12254eb8763dc69ee6b721bf44 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 16:44:53 -0400 Subject: [PATCH 037/130] Pick up changes to encodeAttribute for range filter --- composer.json | 2 +- composer.lock | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/composer.json b/composer.json index 8467da809..ea8e195fb 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "utopia-php/cache": "0.4.*", "utopia-php/cli": "0.11.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "dev-feat-adjusted-query-validator as 0.10.0", + "utopia-php/database": "dev-feat-adjust-encodeAttribute as 0.10.0", "utopia-php/locale": "0.4.*", "utopia-php/registry": "0.5.*", "utopia-php/preloader": "0.2.*", diff --git a/composer.lock b/composer.lock index 792a6214d..63709836d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "aac413429914bd9299a7ae722f00cef8", + "content-hash": "95e162b8789c0727b4afac3191962fff", "packages": [ { "name": "adhocore/jwt", @@ -355,16 +355,16 @@ }, { "name": "composer/package-versions-deprecated", - "version": "1.11.99.2", + "version": "1.11.99.3", "source": { "type": "git", "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "c6522afe5540d5fc46675043d3ed5a45a740b27c" + "reference": "fff576ac850c045158a250e7e27666e146e78d18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/c6522afe5540d5fc46675043d3ed5a45a740b27c", - "reference": "c6522afe5540d5fc46675043d3ed5a45a740b27c", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/fff576ac850c045158a250e7e27666e146e78d18", + "reference": "fff576ac850c045158a250e7e27666e146e78d18", "shasum": "" }, "require": { @@ -408,7 +408,7 @@ "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", "support": { "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.2" + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.3" }, "funding": [ { @@ -424,7 +424,7 @@ "type": "tidelift" } ], - "time": "2021-05-24T07:46:03+00:00" + "time": "2021-08-17T13:49:14+00:00" }, { "name": "dragonmantank/cron-expression", @@ -1984,16 +1984,16 @@ }, { "name": "utopia-php/database", - "version": "dev-feat-adjusted-query-validator", + "version": "dev-feat-adjust-encodeAttribute", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "cb73391371f70ddb54bc0000064b15c5f173cb7a" + "reference": "5ef32ec85143daf78796e7826453244d24f8a86a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/cb73391371f70ddb54bc0000064b15c5f173cb7a", - "reference": "cb73391371f70ddb54bc0000064b15c5f173cb7a", + "url": "https://api.github.com/repos/utopia-php/database/zipball/5ef32ec85143daf78796e7826453244d24f8a86a", + "reference": "5ef32ec85143daf78796e7826453244d24f8a86a", "shasum": "" }, "require": { @@ -2041,9 +2041,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/feat-adjusted-query-validator" + "source": "https://github.com/utopia-php/database/tree/feat-adjust-encodeAttribute" }, - "time": "2021-08-23T14:18:47+00:00" + "time": "2021-08-27T20:39:51+00:00" }, { "name": "utopia-php/domains", @@ -6257,7 +6257,7 @@ "aliases": [ { "package": "utopia-php/database", - "version": "dev-feat-adjusted-query-validator", + "version": "dev-feat-adjust-encodeAttribute", "alias": "0.10.0", "alias_normalized": "0.10.0.0" } @@ -6287,5 +6287,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.0.0" + "plugin-api-version": "2.1.0" } From d135a14bd4fcaf99895807150bd4b127c7cff93c Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 16:45:33 -0400 Subject: [PATCH 038/130] Test for attribute response model in collection subquery --- tests/e2e/Services/Database/DatabaseBase.php | 74 +++++++++++++++++--- 1 file changed, 65 insertions(+), 9 deletions(-) diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index 52de1e89f..b2d604ead 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -450,15 +450,71 @@ trait DatabaseBase // var_dump($collection); - $this->assertIsArray($collection['body']['attributes']); - $this->assertCount(7, $collection['body']['attributes']); - $this->assertEquals($collection['body']['attributes'][0]['key'], $string['body']['key']); - $this->assertEquals($collection['body']['attributes'][1]['key'], $email['body']['key']); - $this->assertEquals($collection['body']['attributes'][2]['key'], $ip['body']['key']); - $this->assertEquals($collection['body']['attributes'][3]['key'], $url['body']['key']); - $this->assertEquals($collection['body']['attributes'][4]['key'], $integer['body']['key']); - $this->assertEquals($collection['body']['attributes'][5]['key'], $float['body']['key']); - $this->assertEquals($collection['body']['attributes'][6]['key'], $boolean['body']['key']); + $this->assertEquals(200, $collection['headers']['status-code']); + + $attributes = $collection['body']['attributes']; + + $this->assertIsArray($attributes); + $this->assertCount(7, $attributes); + + var_dump($attributes); + + $this->assertEquals($stringResponse['body']['key'], $attributes[0]['key']); + $this->assertEquals($stringResponse['body']['type'], $attributes[0]['type']); + $this->assertEquals($stringResponse['body']['status'], $attributes[0]['status']); + $this->assertEquals($stringResponse['body']['required'], $attributes[0]['required']); + $this->assertEquals($stringResponse['body']['array'], $attributes[0]['array']); + $this->assertEquals($stringResponse['body']['size'], $attributes[0]['size']); + $this->assertEquals($stringResponse['body']['default'], $attributes[0]['default']); + + $this->assertEquals($emailResponse['body']['key'], $attributes[1]['key']); + $this->assertEquals($emailResponse['body']['type'], $attributes[1]['type']); + $this->assertEquals($emailResponse['body']['status'], $attributes[1]['status']); + $this->assertEquals($emailResponse['body']['required'], $attributes[1]['required']); + $this->assertEquals($emailResponse['body']['array'], $attributes[1]['array']); + $this->assertEquals($emailResponse['body']['default'], $attributes[1]['default']); + $this->assertEquals($emailResponse['body']['format'], $attributes[1]['format']); + + $this->assertEquals($ipResponse['body']['key'], $attributes[2]['key']); + $this->assertEquals($ipResponse['body']['type'], $attributes[2]['type']); + $this->assertEquals($ipResponse['body']['status'], $attributes[2]['status']); + $this->assertEquals($ipResponse['body']['required'], $attributes[2]['required']); + $this->assertEquals($ipResponse['body']['array'], $attributes[2]['array']); + $this->assertEquals($ipResponse['body']['default'], $attributes[2]['default']); + $this->assertEquals($ipResponse['body']['format'], $attributes[2]['format']); + + $this->assertEquals($urlResponse['body']['key'], $attributes[3]['key']); + $this->assertEquals($urlResponse['body']['type'], $attributes[3]['type']); + $this->assertEquals($urlResponse['body']['status'], $attributes[3]['status']); + $this->assertEquals($urlResponse['body']['required'], $attributes[3]['required']); + $this->assertEquals($urlResponse['body']['array'], $attributes[3]['array']); + $this->assertEquals($urlResponse['body']['default'], $attributes[3]['default']); + $this->assertEquals($urlResponse['body']['format'], $attributes[3]['format']); + + $this->assertEquals($integerResponse['body']['key'], $attributes[4]['key']); + $this->assertEquals($integerResponse['body']['type'], $attributes[4]['type']); + $this->assertEquals($integerResponse['body']['status'], $attributes[4]['status']); + $this->assertEquals($integerResponse['body']['required'], $attributes[4]['required']); + $this->assertEquals($integerResponse['body']['array'], $attributes[4]['array']); + $this->assertEquals($integerResponse['body']['default'], $attributes[4]['default']); + $this->assertEquals($integerResponse['body']['min'], $attributes[4]['min']); + $this->assertEquals($integerResponse['body']['max'], $attributes[4]['max']); + + $this->assertEquals($floatResponse['body']['key'], $attributes[5]['key']); + $this->assertEquals($floatResponse['body']['type'], $attributes[5]['type']); + $this->assertEquals($floatResponse['body']['status'], $attributes[5]['status']); + $this->assertEquals($floatResponse['body']['required'], $attributes[5]['required']); + $this->assertEquals($floatResponse['body']['array'], $attributes[5]['array']); + $this->assertEquals($floatResponse['body']['default'], $attributes[5]['default']); + $this->assertEquals($floatResponse['body']['min'], $attributes[5]['min']); + $this->assertEquals($floatResponse['body']['max'], $attributes[5]['max']); + + $this->assertEquals($booleanResponse['body']['key'], $attributes[6]['key']); + $this->assertEquals($booleanResponse['body']['type'], $attributes[6]['type']); + $this->assertEquals($booleanResponse['body']['status'], $attributes[6]['status']); + $this->assertEquals($booleanResponse['body']['required'], $attributes[6]['required']); + $this->assertEquals($booleanResponse['body']['array'], $attributes[6]['array']); + $this->assertEquals($booleanResponse['body']['default'], $attributes[6]['default']); // return $data; } From 8d7b4a9c2d45d737953babc180a957398259a348 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 19:42:24 -0400 Subject: [PATCH 039/130] Get proper attribute models in collection document --- .../Utopia/Response/Model/Collection.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Response/Model/Collection.php b/src/Appwrite/Utopia/Response/Model/Collection.php index 398bcf770..f023be261 100644 --- a/src/Appwrite/Utopia/Response/Model/Collection.php +++ b/src/Appwrite/Utopia/Response/Model/Collection.php @@ -4,6 +4,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; +use Utopia\Database\Document; use stdClass; class Collection extends Model @@ -48,7 +49,21 @@ class Collection extends Model 'description' => 'Collection attributes.', 'default' => [], 'example' => new stdClass, - 'array' => true + 'array' => true, + 'getNestedType' => function(Document $attribute) { + return match($attribute->getAttribute('type')) { + self::TYPE_BOOLEAN => Response::MODEL_ATTRIBUTE_BOOLEAN, + self::TYPE_INTEGER => Response::MODEL_ATTRIBUTE_INTEGER, + self::TYPE_FLOAT => Response::MODEL_ATTRIBUTE_FLOAT, + self::TYPE_STRING => match($attribute->getAttribute('format')) { + APP_DATABASE_ATTRIBUTE_EMAIL => Response::MODEL_ATTRIBUTE_EMAIL, + APP_DATABASE_ATTRIBUTE_IP => Response::MODEL_ATTRIBUTE_IP, + APP_DATABASE_ATTRIBUTE_URL => Response::MODEL_ATTRIBUTE_URL, + default => Response::MODEL_ATTRIBUTE_STRING, + }, + default => Response::MODEL_ATTRIBUTE, + }; + }, ]) ->addRule('indexes', [ 'type' => Response::MODEL_INDEX, From ed705829459f5d668587af0fb70640c6250ffe35 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 19:42:53 -0400 Subject: [PATCH 040/130] Fix error on null default values --- app/init.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/init.php b/app/init.php index eaf8358a1..b8fcbdb11 100644 --- a/app/init.php +++ b/app/init.php @@ -189,6 +189,9 @@ Database::addFilter('defaultValue', return json_encode(['value' => $value]); }, function($value) { + if (is_null($value)) { + return null; + } return json_decode($value, true)['value']; } ); From c3534b43835f7d4ec37c2911dc6ffa99f7cd6ff8 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 19:45:18 -0400 Subject: [PATCH 041/130] Clean up code --- app/controllers/api/database.php | 9 --------- tests/e2e/Services/Database/DatabaseBase.php | 15 +++++---------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 2c134022d..b97c1dee0 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -846,7 +846,6 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') // Select response model based on type and format $type = $attribute->getAttribute('type'); $format = $attribute->getAttribute('format'); - $formatOptions = $attribute->getAttribute('formatOptions'); $model = match($type) { Database::VAR_BOOLEAN => Response::MODEL_ATTRIBUTE_BOOLEAN, @@ -861,14 +860,6 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') default => Response::MODEL_ATTRIBUTE, }; - // Format response - // TODO@kodumbeats test if this is necessary with range filter - if ($model === Response::MODEL_ATTRIBUTE_INTEGER || $model === Response::MODEL_ATTRIBUTE_FLOAT) - { - $attribute->setAttribute('min', $formatOptions['min']); - $attribute->setAttribute('max', $formatOptions['max']); - } - $response->dynamic($attribute, $model); }); diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index b2d604ead..380f6bd26 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -100,11 +100,10 @@ trait DatabaseBase return $data; } - // /** - // * @depends testCreateAttributes - // */ - // public function testAttributeResponseModels(array $data): array - public function testAttributeResponseModels() + /** + * @depends testCreateAttributes + */ + public function testAttributeResponseModels(array $data): array { $collection= $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([ 'content-type' => 'application/json', @@ -448,8 +447,6 @@ trait DatabaseBase 'x-appwrite-key' => $this->getProject()['apiKey'] ])); - // var_dump($collection); - $this->assertEquals(200, $collection['headers']['status-code']); $attributes = $collection['body']['attributes']; @@ -457,8 +454,6 @@ trait DatabaseBase $this->assertIsArray($attributes); $this->assertCount(7, $attributes); - var_dump($attributes); - $this->assertEquals($stringResponse['body']['key'], $attributes[0]['key']); $this->assertEquals($stringResponse['body']['type'], $attributes[0]['type']); $this->assertEquals($stringResponse['body']['status'], $attributes[0]['status']); @@ -516,7 +511,7 @@ trait DatabaseBase $this->assertEquals($booleanResponse['body']['array'], $attributes[6]['array']); $this->assertEquals($booleanResponse['body']['default'], $attributes[6]['default']); - // return $data; + return $data; } /** From 559fe95b7cb63208398c258b1a00ac537f5c7010 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 20:32:44 -0400 Subject: [PATCH 042/130] Prefer single query to internal DB --- app/controllers/api/database.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index b97c1dee0..a38c342b2 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -802,9 +802,7 @@ App::get('/v1/database/collections/:collectionId/attributes') throw new Exception('Collection not found', 404); } - $attributes = $dbForInternal->find('attributes', [ - new Query('collectionId', Query::TYPE_EQUAL, [$collection->getId()]) - ], 100); + $attributes = $collection->getAttribute('attributes'); $response->dynamic(new Document([ 'sum' => \count($attributes), @@ -837,9 +835,9 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') throw new Exception('Collection not found', 404); } - $attribute = $dbForInternal->getDocument('attributes', $attributeId); + $attribute = $collection->find('$id', $attributeId, 'attributes'); - if ($attribute === false) { + if (!$attribute) { throw new Exception('Attribute not found', 404); } From 5a1ab3b059cea6dccf8a8f5ff9ddc40c939eace7 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 11 Aug 2021 21:05:19 -0400 Subject: [PATCH 043/130] Add enforce param to collections --- app/controllers/api/database.php | 41 ++++++++++++++++++++++++++++++++ composer.lock | 12 +++++----- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index dd275c9c2..c02d8d108 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -15,6 +15,7 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Database\Adapter\MariaDB; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\QueryValidator; @@ -1138,6 +1139,14 @@ App::post('/v1/database/collections/:collectionId/documents') throw new Exception('Collection not found', 404); } + // Check collection permissions when enforced + if ($collection->getAttribute('enforce') === 'collection') { + $validator = new Authorization($collection, 'write'); + if (!$validator->isValid($collection->getWrite())) { + throw new Exception('Unauthorized permissions', 401); + } + } + $data['$collection'] = $collection->getId(); // Adding this param to make API easier for developers $data['$id'] = $documentId == 'unique()' ? $dbForExternal->getId() : $documentId; $data['$read'] = (is_null($read) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $read ?? []; // By default set read permissions for user @@ -1195,6 +1204,14 @@ App::get('/v1/database/collections/:collectionId/documents') throw new Exception('Collection not found', 404); } + // Check collection permissions when enforced + if ($collection->getAttribute('enforce') === 'collection') { + $validator = new Authorization($collection, 'read'); + if (!$validator->isValid($collection->getRead())) { + throw new Exception('Unauthorized permissions', 401); + } + } + $queries = \array_map(function ($query) { return Query::parse($query); }, $queries); @@ -1247,6 +1264,14 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId') throw new Exception('Collection not found', 404); } + // Check collection permissions when enforced + if ($collection->getAttribute('enforce') === 'collection') { + $validator = new Authorization($collection, 'read'); + if (!$validator->isValid($collection->getRead())) { + throw new Exception('Unauthorized permissions', 401); + } + } + $document = $dbForExternal->getDocument($collectionId, $documentId); if ($document->isEmpty()) { @@ -1289,6 +1314,14 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') throw new Exception('Collection not found', 404); } + // Check collection permissions when enforced + if ($collection->getAttribute('enforce') === 'collection') { + $validator = new Authorization($collection, 'write'); + if (!$validator->isValid($collection->getWrite())) { + throw new Exception('Unauthorized permissions', 401); + } + } + $document = $dbForExternal->getDocument($collectionId, $documentId); if ($document->isEmpty()) { @@ -1361,6 +1394,14 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId') throw new Exception('Collection not found', 404); } + // Check collection permissions when enforced + if ($collection->getAttribute('enforce') === 'collection') { + $validator = new Authorization($collection, 'write'); + if (!$validator->isValid($collection->getWrite())) { + throw new Exception('Unauthorized permissions', 401); + } + } + $document = $dbForExternal->getDocument($collectionId, $documentId); if ($document->isEmpty()) { diff --git a/composer.lock b/composer.lock index a5f2a3e06..8dac7bcc6 100644 --- a/composer.lock +++ b/composer.lock @@ -355,16 +355,16 @@ }, { "name": "composer/package-versions-deprecated", - "version": "1.11.99.2", + "version": "1.11.99.3", "source": { "type": "git", "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "c6522afe5540d5fc46675043d3ed5a45a740b27c" + "reference": "fff576ac850c045158a250e7e27666e146e78d18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/c6522afe5540d5fc46675043d3ed5a45a740b27c", - "reference": "c6522afe5540d5fc46675043d3ed5a45a740b27c", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/fff576ac850c045158a250e7e27666e146e78d18", + "reference": "fff576ac850c045158a250e7e27666e146e78d18", "shasum": "" }, "require": { @@ -408,7 +408,7 @@ "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", "support": { "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.2" + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.3" }, "funding": [ { @@ -424,7 +424,7 @@ "type": "tidelift" } ], - "time": "2021-05-24T07:46:03+00:00" + "time": "2021-08-17T13:49:14+00:00" }, { "name": "dragonmantank/cron-expression", From 7db7e39f78f9ea30cab29bd357862750f34a0d45 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 11 Aug 2021 21:07:46 -0400 Subject: [PATCH 044/130] Test enforce collection permissions --- tests/e2e/Services/Database/DatabaseBase.php | 63 ++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index 3e109ca33..f62e9d902 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -1089,4 +1089,67 @@ trait DatabaseBase return $data; } + + public function testEnforceCollectionPermissions() + { + $user = 'user:' . $this->getUser()['$id']; + $collection = $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'] + ]), [ + 'collectionId' => 'unique()', + 'name' => 'enforceCollectionPermissions', + 'enforce' => 'collection', + 'read' => [$user], + 'write' => [$user] + ]); + + var_dump($collection); + + $this->assertEquals($collection['headers']['status-code'], 201); + $this->assertEquals($collection['body']['name'], 'enforceCollectionPermissions'); + $this->assertEquals($collection['body']['enforce'], 'collection'); + + $collectionId = $collection['body']['$id']; + + $attribute = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'attribute', + 'size' => 64, + 'required' => true, + ]); + + $this->assertEquals($attribute['headers']['status-code'], 201); + $this->assertEquals($attribute['body']['$id'], 'attribute'); + + // wait for db to add attribute + sleep(3); + + $document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => 'unique()', + 'data' => [ + 'attribute' => 'documen1', + ], + 'read' => [], + 'write' => [], + ]); + + $this->assertEquals($document['headers']['status-code'], 201); + + $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + // var_dump($documents); + + $this->assertCount(1, $documents['body']['documents']); + } } \ No newline at end of file From 0e68b4a1e4d0307f78ef05e8818edcabcdd9360e Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 11 Aug 2021 21:25:51 -0400 Subject: [PATCH 045/130] Remove var dumps --- tests/e2e/Services/Database/DatabaseBase.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index f62e9d902..3d1041272 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -1105,8 +1105,6 @@ trait DatabaseBase 'write' => [$user] ]); - var_dump($collection); - $this->assertEquals($collection['headers']['status-code'], 201); $this->assertEquals($collection['body']['name'], 'enforceCollectionPermissions'); $this->assertEquals($collection['body']['enforce'], 'collection'); @@ -1148,8 +1146,6 @@ trait DatabaseBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - // var_dump($documents); - $this->assertCount(1, $documents['body']['documents']); } } \ No newline at end of file From 2b057c06176fbd1ea43fa71657909ba0bbe088c8 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 11 Aug 2021 21:26:31 -0400 Subject: [PATCH 046/130] Skip authorization on document routes if collection permissions are met --- app/controllers/api/database.php | 49 ++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index c02d8d108..9b62bc85b 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -1153,7 +1153,14 @@ App::post('/v1/database/collections/:collectionId/documents') $data['$write'] = (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? []; // By default set write permissions for user try { - $document = $dbForExternal->createDocument($collectionId, new Document($data)); + if ($collection->getAttribute('enforce') === 'collection') { + /** @var Document $document */ + $document = Authorization::skip(function() use ($dbForExternal, $collectionId, $data) { + return $dbForExternal->createDocument($collectionId, new Document($data)); + }); + } else { + $document = $dbForExternal->createDocument($collectionId, new Document($data)); + } } catch (StructureException $exception) { throw new Exception($exception->getMessage(), 400); @@ -1227,13 +1234,22 @@ App::get('/v1/database/collections/:collectionId/documents') $afterDocument = $dbForExternal->getDocument($collectionId, $after); if ($afterDocument->isEmpty()) { - throw new Exception("Document '{$after}' for the 'after' value not found.", 400); + throw new Exception("Document \'{$after}\' for the \'after\' value not found.", 400); } } + if ($collection->getAttribute('enforce') === 'collection') { + /** @var Document[] $documents */ + $documents = Authorization::skip(function() use ($dbForExternal, $collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument) { + return $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument ?? null); + }); + } else { + $documents = $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument ?? null); + } + $response->dynamic(new Document([ 'sum' => $dbForExternal->count($collectionId, $queries, APP_LIMIT_COUNT), - 'documents' => $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument ?? null), + 'documents' => $documents, ]), Response::MODEL_DOCUMENT_LIST); }); @@ -1272,7 +1288,14 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId') } } - $document = $dbForExternal->getDocument($collectionId, $documentId); + if ($collection->getAttribute('enforce') === 'collection') { + /** @var Document $document */ + $document = Authorization::skip(function() use ($dbForExternal, $collectionId, $documentId) { + return $dbForExternal->getDocument($collectionId, $documentId); + }); + } else { + $document = $dbForExternal->getDocument($collectionId, $documentId); + } if ($document->isEmpty()) { throw new Exception('No document found', 404); @@ -1346,7 +1369,14 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') $data['$write'] = (is_null($write)) ? ($document->getWrite() ?? []) : $write; // By default inherit write permissions try { - $document = $dbForExternal->updateDocument($collection->getId(), $document->getId(), new Document($data)); + if ($collection->getAttribute('enforce') === 'collection') { + /** @var Document $document */ + $document = Authorization::skip(function() use ($dbForExternal, $collection, $document, $data) { + return $dbForExternal->updateDocument($collection->getId(), $document->getId(), new Document($data)); + }); + } else { + $document = $dbForExternal->updateDocument($collection->getId(), $document->getId(), new Document($data)); + } } catch (AuthorizationException $exception) { throw new Exception('Unauthorized permissions', 401); @@ -1402,7 +1432,14 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId') } } - $document = $dbForExternal->getDocument($collectionId, $documentId); + if ($collection->getAttribute('enforce') === 'collection') { + /** @var Document $document */ + $document = Authorization::skip(function() use ($dbForExternal, $collectionId, $documentId) { + return $dbForExternal->getDocument($collectionId, $documentId); + }); + } else { + $document = $dbForExternal->getDocument($collectionId, $documentId); + } if ($document->isEmpty()) { throw new Exception('No document found', 404); From 95f505b61657ff66a177c20b8fed55f3aa2ecfbe Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 13 Aug 2021 16:20:54 -0400 Subject: [PATCH 047/130] Add tests for collection permissions --- tests/e2e/Services/Database/DatabaseBase.php | 106 +++++++++++++++++-- 1 file changed, 98 insertions(+), 8 deletions(-) diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index 3d1041272..43d7f51b0 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -654,6 +654,8 @@ trait DatabaseBase 'required' => false, ]); + sleep(2); + $ip = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/ip', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -663,6 +665,8 @@ trait DatabaseBase 'required' => false, ]); + sleep(2); + $url = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/url', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -673,6 +677,8 @@ trait DatabaseBase 'required' => false, ]); + sleep(2); + $range = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -684,6 +690,8 @@ trait DatabaseBase 'max' => 10, ]); + sleep(2); + // TODO@kodumbeats min and max are rounded in error message $floatRange = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/float', array_merge([ 'content-type' => 'application/json', @@ -696,6 +704,8 @@ trait DatabaseBase 'max' => 1.4, ]); + sleep(2); + // TODO@kodumbeats float validator rejects 0.0 and 1.0 as floats // $probability = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/float', array_merge([ // 'content-type' => 'application/json', @@ -718,6 +728,8 @@ trait DatabaseBase 'max' => 10, ]); + sleep(2); + $lowerBound = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1111,6 +1123,8 @@ trait DatabaseBase $collectionId = $collection['body']['$id']; + sleep(2); + $attribute = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1121,31 +1135,107 @@ trait DatabaseBase 'required' => true, ]); - $this->assertEquals($attribute['headers']['status-code'], 201); - $this->assertEquals($attribute['body']['$id'], 'attribute'); + $this->assertEquals(201, $attribute['headers']['status-code'], 201); + $this->assertEquals('attribute', $attribute['body']['$id']); // wait for db to add attribute - sleep(3); + sleep(2); - $document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([ + $index = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'indexId' => 'key_attribute', + 'type' => 'key', + 'attributes' => [$attribute['body']['$id']], + ]); + + $this->assertEquals(201, $index['headers']['status-code']); + $this->assertEquals('key_attribute', $index['body']['$id']); + + // wait for db to add attribute + sleep(2); + + $document1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'documentId' => 'unique()', 'data' => [ - 'attribute' => 'documen1', + 'attribute' => 'one', ], - 'read' => [], - 'write' => [], + 'read' => [$user], + 'write' => [$user], ]); - $this->assertEquals($document['headers']['status-code'], 201); + $this->assertEquals(201, $document1['headers']['status-code']); $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); + $this->assertEquals(1, $documents['body']['sum']); $this->assertCount(1, $documents['body']['documents']); + + /* + * Test for Failure + */ + + // Remove write permission + $collection = $this->client->call(Client::METHOD_PUT, '/database/collections/' . $collectionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'name' => 'enforceCollectionPermissions', + 'enforce' => 'collection', + 'read' => [$user], + 'write' => [] + ]); + + $this->assertEquals(200, $collection['headers']['status-code']); + + $badDocument = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => 'unique()', + 'data' => [ + 'attribute' => 'bad', + ], + 'read' => [$user], + 'write' => [$user], + ]); + + if($this->getSide() == 'client') { + $this->assertEquals(401, $badDocument['headers']['status-code']); + } + + if($this->getSide() == 'server') { + $this->assertEquals(201, $badDocument['headers']['status-code']); + } + + // Remove read permission + $collection = $this->client->call(Client::METHOD_PUT, '/database/collections/' . $collectionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'name' => 'enforceCollectionPermissions', + 'enforce' => 'collection', + 'read' => [], + 'write' => [] + ]); + + $this->assertEquals(200, $collection['headers']['status-code']); + + $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ])); + + $this->assertEquals(401, $documents['headers']['status-code']); } } \ No newline at end of file From a33faab21393ceb2c3ab6cafb8fc5a931c1bb2a3 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 21:45:15 -0400 Subject: [PATCH 048/130] Enforce attribute refactored to permission --- app/controllers/api/database.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 9b62bc85b..14feb5df3 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -1140,7 +1140,7 @@ App::post('/v1/database/collections/:collectionId/documents') } // Check collection permissions when enforced - if ($collection->getAttribute('enforce') === 'collection') { + if ($collection->getAttribute('permission') === 'collection') { $validator = new Authorization($collection, 'write'); if (!$validator->isValid($collection->getWrite())) { throw new Exception('Unauthorized permissions', 401); @@ -1153,7 +1153,7 @@ App::post('/v1/database/collections/:collectionId/documents') $data['$write'] = (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? []; // By default set write permissions for user try { - if ($collection->getAttribute('enforce') === 'collection') { + if ($collection->getAttribute('permission') === 'collection') { /** @var Document $document */ $document = Authorization::skip(function() use ($dbForExternal, $collectionId, $data) { return $dbForExternal->createDocument($collectionId, new Document($data)); @@ -1212,7 +1212,7 @@ App::get('/v1/database/collections/:collectionId/documents') } // Check collection permissions when enforced - if ($collection->getAttribute('enforce') === 'collection') { + if ($collection->getAttribute('permission') === 'collection') { $validator = new Authorization($collection, 'read'); if (!$validator->isValid($collection->getRead())) { throw new Exception('Unauthorized permissions', 401); @@ -1238,7 +1238,7 @@ App::get('/v1/database/collections/:collectionId/documents') } } - if ($collection->getAttribute('enforce') === 'collection') { + if ($collection->getAttribute('permission') === 'collection') { /** @var Document[] $documents */ $documents = Authorization::skip(function() use ($dbForExternal, $collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument) { return $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument ?? null); @@ -1281,14 +1281,14 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId') } // Check collection permissions when enforced - if ($collection->getAttribute('enforce') === 'collection') { + if ($collection->getAttribute('permission') === 'collection') { $validator = new Authorization($collection, 'read'); if (!$validator->isValid($collection->getRead())) { throw new Exception('Unauthorized permissions', 401); } } - if ($collection->getAttribute('enforce') === 'collection') { + if ($collection->getAttribute('permission') === 'collection') { /** @var Document $document */ $document = Authorization::skip(function() use ($dbForExternal, $collectionId, $documentId) { return $dbForExternal->getDocument($collectionId, $documentId); @@ -1338,7 +1338,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') } // Check collection permissions when enforced - if ($collection->getAttribute('enforce') === 'collection') { + if ($collection->getAttribute('permission') === 'collection') { $validator = new Authorization($collection, 'write'); if (!$validator->isValid($collection->getWrite())) { throw new Exception('Unauthorized permissions', 401); @@ -1369,7 +1369,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') $data['$write'] = (is_null($write)) ? ($document->getWrite() ?? []) : $write; // By default inherit write permissions try { - if ($collection->getAttribute('enforce') === 'collection') { + if ($collection->getAttribute('permission') === 'collection') { /** @var Document $document */ $document = Authorization::skip(function() use ($dbForExternal, $collection, $document, $data) { return $dbForExternal->updateDocument($collection->getId(), $document->getId(), new Document($data)); @@ -1425,14 +1425,14 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId') } // Check collection permissions when enforced - if ($collection->getAttribute('enforce') === 'collection') { + if ($collection->getAttribute('permission') === 'collection') { $validator = new Authorization($collection, 'write'); if (!$validator->isValid($collection->getWrite())) { throw new Exception('Unauthorized permissions', 401); } } - if ($collection->getAttribute('enforce') === 'collection') { + if ($collection->getAttribute('permission') === 'collection') { /** @var Document $document */ $document = Authorization::skip(function() use ($dbForExternal, $collectionId, $documentId) { return $dbForExternal->getDocument($collectionId, $documentId); From 4af81db28072263f651a14a6aa7d7f0daace9f00 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 21:45:42 -0400 Subject: [PATCH 049/130] Authorization validator only accepts one argument --- app/controllers/api/database.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 14feb5df3..517651c3e 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -1141,7 +1141,7 @@ App::post('/v1/database/collections/:collectionId/documents') // Check collection permissions when enforced if ($collection->getAttribute('permission') === 'collection') { - $validator = new Authorization($collection, 'write'); + $validator = new Authorization('write'); if (!$validator->isValid($collection->getWrite())) { throw new Exception('Unauthorized permissions', 401); } @@ -1213,7 +1213,7 @@ App::get('/v1/database/collections/:collectionId/documents') // Check collection permissions when enforced if ($collection->getAttribute('permission') === 'collection') { - $validator = new Authorization($collection, 'read'); + $validator = new Authorization('read'); if (!$validator->isValid($collection->getRead())) { throw new Exception('Unauthorized permissions', 401); } @@ -1234,7 +1234,7 @@ App::get('/v1/database/collections/:collectionId/documents') $afterDocument = $dbForExternal->getDocument($collectionId, $after); if ($afterDocument->isEmpty()) { - throw new Exception("Document \'{$after}\' for the \'after\' value not found.", 400); + throw new Exception("Document '{$after}' for the 'after' value not found.", 400); } } @@ -1282,7 +1282,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId') // Check collection permissions when enforced if ($collection->getAttribute('permission') === 'collection') { - $validator = new Authorization($collection, 'read'); + $validator = new Authorization('read'); if (!$validator->isValid($collection->getRead())) { throw new Exception('Unauthorized permissions', 401); } @@ -1339,7 +1339,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') // Check collection permissions when enforced if ($collection->getAttribute('permission') === 'collection') { - $validator = new Authorization($collection, 'write'); + $validator = new Authorization('write'); if (!$validator->isValid($collection->getWrite())) { throw new Exception('Unauthorized permissions', 401); } @@ -1426,7 +1426,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId') // Check collection permissions when enforced if ($collection->getAttribute('permission') === 'collection') { - $validator = new Authorization($collection, 'write'); + $validator = new Authorization('write'); if (!$validator->isValid($collection->getWrite())) { throw new Exception('Unauthorized permissions', 401); } From e9541a9269af49c63d01e454b41dce8e4d8f7282 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 27 Aug 2021 21:46:33 -0400 Subject: [PATCH 050/130] Fix tests --- app/controllers/api/database.php | 1 + tests/e2e/Services/Database/DatabaseBase.php | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 517651c3e..4519da1be 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -1230,6 +1230,7 @@ App::get('/v1/database/collections/:collectionId/documents') throw new Exception($validator->getDescription(), 400); } + $afterDocument = null; if (!empty($after)) { $afterDocument = $dbForExternal->getDocument($collectionId, $after); diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index 43d7f51b0..e260f1477 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -1112,14 +1112,14 @@ trait DatabaseBase ]), [ 'collectionId' => 'unique()', 'name' => 'enforceCollectionPermissions', - 'enforce' => 'collection', + 'permission' => 'collection', 'read' => [$user], 'write' => [$user] ]); $this->assertEquals($collection['headers']['status-code'], 201); $this->assertEquals($collection['body']['name'], 'enforceCollectionPermissions'); - $this->assertEquals($collection['body']['enforce'], 'collection'); + $this->assertEquals($collection['body']['permission'], 'collection'); $collectionId = $collection['body']['$id']; @@ -1136,7 +1136,7 @@ trait DatabaseBase ]); $this->assertEquals(201, $attribute['headers']['status-code'], 201); - $this->assertEquals('attribute', $attribute['body']['$id']); + $this->assertEquals('attribute', $attribute['body']['key']); // wait for db to add attribute sleep(2); @@ -1148,11 +1148,11 @@ trait DatabaseBase ]), [ 'indexId' => 'key_attribute', 'type' => 'key', - 'attributes' => [$attribute['body']['$id']], + 'attributes' => [$attribute['body']['key']], ]); $this->assertEquals(201, $index['headers']['status-code']); - $this->assertEquals('key_attribute', $index['body']['$id']); + $this->assertEquals('key_attribute', $index['body']['key']); // wait for db to add attribute sleep(2); @@ -1190,7 +1190,7 @@ trait DatabaseBase 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'name' => 'enforceCollectionPermissions', - 'enforce' => 'collection', + 'permission' => 'collection', 'read' => [$user], 'write' => [] ]); @@ -1224,7 +1224,7 @@ trait DatabaseBase 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ 'name' => 'enforceCollectionPermissions', - 'enforce' => 'collection', + 'permission' => 'collection', 'read' => [], 'write' => [] ]); @@ -1236,6 +1236,6 @@ trait DatabaseBase 'x-appwrite-project' => $this->getProject()['$id'], ])); - $this->assertEquals(401, $documents['headers']['status-code']); + $this->assertEquals(404, $documents['headers']['status-code']); } } \ No newline at end of file From 82085df58a839a47b910a1f511196492aeccf558 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Mon, 30 Aug 2021 09:33:45 +0200 Subject: [PATCH 051/130] Migrated project platforms to it's own collection --- app/config/collections2.php | 107 ++++++++++++++++++++++++++++++- app/controllers/api/projects.php | 36 ++++++----- app/init.php | 12 ++++ 3 files changed, 138 insertions(+), 17 deletions(-) diff --git a/app/config/collections2.php b/app/config/collections2.php index 8079bdd7c..1e4e4fb30 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -511,7 +511,7 @@ $collections = [ 'required' => false, 'default' => null, 'array' => true, - 'filters' => ['json'], + 'filters' => ['subQueryProjectPlatforms'], ], [ '$id' => 'webhooks', @@ -558,6 +558,111 @@ $collections = [ ], ], + 'projectsPlatforms' => [ + '$collection' => Database::METADATA, + '$id' => 'projectsPlatforms', + 'name' => 'projectsPlatforms', + 'attributes' => [ + [ + '$id' => 'projectId', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'type', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'name', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'key', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'store', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'hostname', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'dateCreated', + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'dateUpdated', + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => '_key_project', + 'type' => Database::INDEX_KEY, + 'attributes' => ['projectId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + 'users' => [ '$collection' => Database::METADATA, '$id' => 'users', diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index b83dff6d9..2dc5ce9ac 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1047,8 +1047,13 @@ App::post('/v1/projects/:projectId/platforms') throw new Exception('Project not found', 404); } + $teamId = $project->getAttribute("teamId"); + $platform = new Document([ + 'projectId' => $project->getId(), '$id' => $dbForConsole->getId(), + '$read' => ['team:' . $teamId], + '$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'], 'type' => $type, 'name' => $name, 'key' => $key, @@ -1058,9 +1063,8 @@ App::post('/v1/projects/:projectId/platforms') 'dateUpdated' => \time(), ]); - $project = $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('platforms', $platform, Document::SET_TYPE_APPEND) - ); + $dbForConsole->createDocument("projectsPlatforms", $platform); + $dbForConsole->purgeDocument('projects', $project->getId()); $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($platform, Response::MODEL_PLATFORM); @@ -1083,13 +1087,17 @@ App::get('/v1/projects/:projectId/platforms') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForConsole */ + // TODO: Implement pagination + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { throw new Exception('Project not found', 404); } - $platforms = $project->getAttribute('platforms', []); + $platforms = $dbForConsole->find('projectsPlatforms', [ + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) + ]); $response->dynamic(new Document([ 'platforms' => $platforms, @@ -1121,7 +1129,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId') throw new Exception('Project not found', 404); } - $platform = $project->find('$id', $platformId, 'platforms'); + $platform = $dbForConsole->getDocument("projectsPlatforms", $platformId); if (empty($platform) || !$platform instanceof Document) { throw new Exception('Platform not found', 404); @@ -1158,7 +1166,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') throw new Exception('Project not found', 404); } - $platform = $project->find('$id', $platformId, 'platforms'); + $platform = $dbForConsole->getDocument("projectsPlatforms", $platformId); if (empty($platform) || !$platform instanceof Document) { throw new Exception('Platform not found', 404); @@ -1172,15 +1180,9 @@ App::put('/v1/projects/:projectId/platforms/:platformId') ->setAttribute('hostname', $hostname) ; - $project->findAndReplace('$id', $platform->getId(), $platform - ->setAttribute('name', $name) - ->setAttribute('dateUpdated', \time()) - ->setAttribute('key', $key) - ->setAttribute('store', $store) - ->setAttribute('hostname', $hostname) - , 'platforms'); + $dbForConsole->updateDocument('projectsPlatforms', $platform->getId(), $platform); - $dbForConsole->updateDocument('projects', $project->getId(), $project); + $dbForConsole->purgeDocument('projects', $project->getId()); $response->dynamic($platform, Response::MODEL_PLATFORM); }); @@ -1208,11 +1210,13 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') throw new Exception('Project not found', 404); } - if (!$project->findAndRemove('$id', $platformId, 'platforms')) { + $deleteRecord = $dbForConsole->deleteDocument("projectsPlatforms", $platformId); + + if($deleteRecord == false) { throw new Exception('Platform not found', 404); } - $dbForConsole->updateDocument('projects', $project->getId(), $project); + $dbForConsole->purgeDocument('projects', $project->getId()); $response->noContent(); }); diff --git a/app/init.php b/app/init.php index 57f2dc713..4da737ebb 100644 --- a/app/init.php +++ b/app/init.php @@ -200,6 +200,18 @@ Database::addFilter('subQueryIndexes', } ); +Database::addFilter('subQueryProjectPlatforms', + function($value) { + return null; + }, + function($value, Document $document, Database $database) { + return $database + ->find('projectsPlatforms', [ + new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()]) + ], 100, 0, []); + } +); + Database::addFilter('encrypt', function($value) { $key = App::getEnv('_APP_OPENSSL_KEY_V1'); From 8009adc11d478f83507203c9f94212cf9805fd67 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Mon, 30 Aug 2021 16:24:50 +0200 Subject: [PATCH 052/130] Migrated project services, providers, domains, webhooks and keys to it's own collection --- app/config/collections2.php | 378 ++++++++++++++++++++++++++++++- app/config/services.php | 4 + app/controllers/api/projects.php | 244 ++++++++++++++------ app/init.php | 86 ++++++- 4 files changed, 629 insertions(+), 83 deletions(-) diff --git a/app/config/collections2.php b/app/config/collections2.php index 1e4e4fb30..a29a9728c 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -478,7 +478,7 @@ $collections = [ 'required' => false, 'default' => null, 'array' => false, - 'filters' => ['json'], + 'filters' => ['subQueryProjectServices'], ], [ '$id' => 'auths', @@ -500,7 +500,7 @@ $collections = [ 'required' => false, 'default' => null, 'array' => false, - 'filters' => ['json'], + 'filters' => ['subQueryProjectProviders'], ], [ '$id' => 'platforms', @@ -510,7 +510,7 @@ $collections = [ 'signed' => true, 'required' => false, 'default' => null, - 'array' => true, + 'array' => false, 'filters' => ['subQueryProjectPlatforms'], ], [ @@ -521,8 +521,8 @@ $collections = [ 'signed' => true, 'required' => false, 'default' => null, - 'array' => true, - 'filters' => ['json'], + 'array' => false, + 'filters' => ['subQueryProjectWebhooks'], ], [ '$id' => 'keys', @@ -532,8 +532,8 @@ $collections = [ 'signed' => true, 'required' => false, 'default' => null, - 'array' => true, - 'filters' => ['json'], + 'array' => false, + 'filters' => ['subQueryProjectKeys'], ], [ '$id' => 'domains', @@ -543,8 +543,8 @@ $collections = [ 'signed' => true, 'required' => false, 'default' => null, - 'array' => true, - 'filters' => ['json'], + 'array' => false, + 'filters' => ['subQueryProjectDomains'], ], ], 'indexes' => [ @@ -663,6 +663,366 @@ $collections = [ ], ], + 'projectsServices' => [ + '$collection' => Database::METADATA, + '$id' => 'projectsServices', + 'name' => 'projectsServices', + 'attributes' => [ + [ + '$id' => 'projectId', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'key', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'status', + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ] + ], + 'indexes' => [ + [ + '$id' => '_key_project', + 'type' => Database::INDEX_KEY, + 'attributes' => ['projectId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'projectProviders' => [ + '$collection' => Database::METADATA, + '$id' => 'projectProviders', + 'name' => 'projectProviders', + 'attributes' => [ + [ + '$id' => 'projectId', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'key', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'appId', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'appSecret', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => '_key_project', + 'type' => Database::INDEX_KEY, + 'attributes' => ['projectId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'projectDomains' => [ + '$collection' => Database::METADATA, + '$id' => 'projectDomains', + 'name' => 'projectDomains', + 'attributes' => [ + [ + '$id' => 'projectId', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + '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' => [], + ], + [ + '$id' => 'domain', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'tld', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'registerable', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'verification', + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'certificateId', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => '_key_project', + 'type' => Database::INDEX_KEY, + 'attributes' => ['projectId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'projectKeys' => [ + '$collection' => Database::METADATA, + '$id' => 'projectKeys', + 'name' => 'projectKeys', + 'attributes' => [ + [ + '$id' => 'projectId', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'name', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'scopes', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => true, + 'filters' => [], + ], + [ + '$id' => 'secret', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 256, // var_dump of \bin2hex(\random_bytes(128)) => string(256) + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => '_key_project', + 'type' => Database::INDEX_KEY, + 'attributes' => ['projectId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + + 'projectWebhooks' => [ + '$collection' => Database::METADATA, + '$id' => 'projectWebhooks', + 'name' => 'projectWebhooks', + 'attributes' => [ + [ + '$id' => 'projectId', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'name', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'url', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'httpUser', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'httpPass', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'security', + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'events', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => true, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => '_key_project', + 'type' => Database::INDEX_KEY, + 'attributes' => ['projectId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + 'users' => [ '$collection' => Database::METADATA, '$id' => 'users', diff --git a/app/config/services.php b/app/config/services.php index 09ea35f74..17375c360 100644 --- a/app/config/services.php +++ b/app/config/services.php @@ -5,6 +5,7 @@ return [ 'key' => 'homepage', 'name' => 'Homepage', 'subtitle' => '', + 'description' => '', 'controller' => 'web/home.php', 'sdk' => false, 'docs' => false, @@ -16,6 +17,8 @@ return [ 'console' => [ 'key' => 'console', 'name' => 'Console', + 'subtitle' => '', + 'description' => '', 'controller' => 'web/console.php', 'sdk' => false, 'docs' => false, @@ -93,6 +96,7 @@ return [ 'key' => 'projects', 'name' => 'Projects', 'subtitle' => 'The Project service allows you to manage all the projects in your Appwrite server.', + 'description' => '', 'controller' => 'api/projects.php', 'sdk' => true, 'docs' => true, diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 2dc5ce9ac..15dee15f8 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -7,6 +7,7 @@ use Appwrite\Network\Validator\Domain as DomainValidator; use Appwrite\Network\Validator\URL; use Appwrite\Utopia\Response; use Utopia\App; +use Utopia\CLI\CLI; use Utopia\Config\Config; use Utopia\Database\Document; use Utopia\Database\Query; @@ -92,14 +93,17 @@ App::post('/v1/projects') 'legalCity' => $legalCity, 'legalAddress' => $legalAddress, 'legalTaxId' => $legalTaxId, - 'services' => new stdClass(), - 'platforms' => [], - 'webhooks' => [], - 'keys' => [], - 'domains' => [], - 'auths' => $auths, + 'services' => null, + 'platforms' => null, + 'providers' => null, + 'webhooks' => null, + 'keys' => null, + 'domains' => null, + 'auths' => null ])); + // TODO: Implement write of auths from $auths + $collections = Config::getParam('collections2', []); /** @var array $collections */ $dbForInternal->setNamespace('project_' . $project->getId() . '_internal'); @@ -468,12 +472,13 @@ App::patch('/v1/projects/:projectId/service') ->label('sdk.response.model', Response::MODEL_PROJECT) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('service', '', new WhiteList(array_keys(array_filter(Config::getParam('services'), function($element) {return $element['optional'];})), true), 'Service name.') - ->param('status', null, new Boolean(), 'Service status.') + ->param('status', true, new Boolean(), 'Service status.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $service, $status, $response, $dbForConsole) { + ->action(function ($projectId, $serviceKey, $status, $response, $dbForConsole) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForConsole */ + /** @var Boolean $status */ $project = $dbForConsole->getDocument('projects', $projectId); @@ -481,11 +486,37 @@ App::patch('/v1/projects/:projectId/service') throw new Exception('Project not found', 404); } - $services = $project->getAttribute('services', []); - $services[$service] = $status; + $service = $dbForConsole->findOne("projectsServices", [ + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), + new Query('key', Query::TYPE_EQUAL, [$serviceKey]), + ]); - $project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('services', $services)); + // TODO: Implement delete method. Do we delete for "true" or for "false"? Same for auth!!!! + if($service == false || $service->isEmpty()) { + $teamId = $project->getAttribute("teamId"); + $service = new Document([ + '$id' => $dbForConsole->getId(), + '$read' => ['team:' . $teamId], + '$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'], + 'projectId' => $project->getId(), + 'key' => $serviceKey, + 'status' => $status, + ]); + + $dbForConsole->createDocument("projectsServices", $service); + + $dbForConsole->purgeDocument('projects', $project->getId()); + } else { + if($service->getAttribute("status") != $status) { + $service->setAttribute("status", $status); + $dbForConsole->updateDocument("projectsServices", $service->getId(), $service); + + $dbForConsole->purgeDocument('projects', $project->getId()); + } + } + + $project = $dbForConsole->getDocument('projects', $projectId); $response->dynamic($project, Response::MODEL_PROJECT); }); @@ -505,7 +536,7 @@ App::patch('/v1/projects/:projectId/oauth2') ->param('secret', '', new text(512), 'Provider secret key. Max length: 512 chars.', true) ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $provider, $appId, $secret, $response, $dbForConsole) { + ->action(function ($projectId, $providerKey, $appId, $secret, $response, $dbForConsole) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForConsole */ @@ -515,12 +546,41 @@ App::patch('/v1/projects/:projectId/oauth2') throw new Exception('Project not found', 404); } - $providers = $project->getAttribute('providers', []); - $providers[$provider . 'Appid'] = $appId; - $providers[$provider . 'Secret'] = $secret; + $provider = $dbForConsole->findOne("projectProviders", [ + new Query('key', Query::TYPE_EQUAL, [$providerKey]), + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), + ]); - $project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('providers', $providers)); + if($provider && !$provider->isEmpty()) { + // Provider exists + $provider->setAttribute("appId", $appId) + ->setAttribute("appSecret", $secret); + + $dbForConsole->updateDocument("projectProviders", $provider->getId(), $provider); + + $dbForConsole->purgeDocument("projects", $project->getId()); + } else { + // Provider does not exist yet + + $teamId = $project->getAttribute("teamId"); + + $provider = new Document([ + '$id' => $dbForConsole->getId(), + '$read' => ['team:' . $teamId], + '$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'], + 'projectId' => $project->getId(), + 'key' => $providerKey, + 'appId' => $appId, + 'appSecret' => $secret + ]); + + $dbForConsole->createDocument("projectProviders", $provider); + + $dbForConsole->purgeDocument("projects", $project->getId()); + } + + $project = $dbForConsole->getDocument('projects', $projectId); $response->dynamic($project, Response::MODEL_PROJECT); }); @@ -674,8 +734,13 @@ App::post('/v1/projects/:projectId/webhooks') $security = ($security === '1' || $security === 'true' || $security === 1 || $security === true); + $teamId = $project->getAttribute("teamId"); + $webhook = new Document([ '$id' => $dbForConsole->getId(), + '$read' => ['team:' . $teamId], + '$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'], + 'projectId' => $project->getId(), 'name' => $name, 'events' => $events, 'url' => $url, @@ -684,9 +749,9 @@ App::post('/v1/projects/:projectId/webhooks') 'httpPass' => $httpPass, ]); - $project = $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('webhooks', $webhook, Document::SET_TYPE_APPEND) - ); + $webhook = $dbForConsole->createDocument("projectWebhooks", $webhook); + + $dbForConsole->purgeDocument("projects", $project->getId()); $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($webhook, Response::MODEL_WEBHOOK); @@ -715,7 +780,9 @@ App::get('/v1/projects/:projectId/webhooks') throw new Exception('Project not found', 404); } - $webhooks = $project->getAttribute('webhooks', []); + $webhooks = $dbForConsole->find("projectWebhooks", [ + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) + ]); $response->dynamic(new Document([ 'webhooks' => $webhooks, @@ -747,9 +814,9 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId') throw new Exception('Project not found', 404); } - $webhook = $project->find('$id', $webhookId, 'webhooks'); + $webhook = $dbForConsole->getDocument('projectWebhooks', $webhookId); - if (empty($webhook) || !$webhook instanceof Document) { + if ($webhook->isEmpty()) { throw new Exception('Webhook not found', 404); } @@ -788,22 +855,23 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') $security = ($security === '1' || $security === 'true' || $security === 1 || $security === true); - $webhook = $project->find('$id', $webhookId, 'webhooks'); + $webhook = $dbForConsole->getDocument('projectWebhooks', $webhookId); - if (empty($webhook) || !$webhook instanceof Document) { + if ($webhook->isEmpty()) { throw new Exception('Webhook not found', 404); } - $project->findAndReplace('$id', $webhook->getId(), $webhook - ->setAttribute('name', $name) - ->setAttribute('events', $events) - ->setAttribute('url', $url) - ->setAttribute('security', $security) - ->setAttribute('httpUser', $httpUser) - ->setAttribute('httpPass', $httpPass) - , 'webhooks'); + $webhook + ->setAttribute('name', $name) + ->setAttribute('events', $events) + ->setAttribute('url', $url) + ->setAttribute('security', $security) + ->setAttribute('httpUser', $httpUser) + ->setAttribute('httpPass', $httpPass); - $dbForConsole->updateDocument('projects', $project->getId(), $project); + $dbForConsole->updateDocument("projectWebhooks", $webhook->getId(), $webhook); + + $dbForConsole->purgeDocument('projects', $project->getId()); $response->dynamic($webhook, Response::MODEL_WEBHOOK); }); @@ -831,11 +899,15 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') throw new Exception('Project not found', 404); } - if (!$project->findAndRemove('$id', $webhookId, 'webhooks')) { + $webhook = $dbForConsole->getDocument("projectWebhooks", $webhookId); + + if($webhook->isEmpty()) { throw new Exception('Webhook not found', 404); } - $dbForConsole->updateDocument('projects', $project->getId(), $project); + $dbForConsole->deleteDocument("projectWebhooks", $webhook->getId()); + + $dbForConsole->purgeDocument('projects', $project->getId()); $response->noContent(); }); @@ -867,18 +939,24 @@ App::post('/v1/projects/:projectId/keys') throw new Exception('Project not found', 404); } + $teamId = $project->getAttribute("teamId"); + $key = new Document([ '$id' => $dbForConsole->getId(), + '$read' => ['team:' . $teamId], + '$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'], + 'projectId' => $project->getId(), 'name' => $name, 'scopes' => $scopes, 'secret' => \bin2hex(\random_bytes(128)), ]); - $project = $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('keys', $key, Document::SET_TYPE_APPEND) - ); + $key = $dbForConsole->createDocument("projectKeys", $key); + + $dbForConsole->purgeDocument("projects", $project->getId()); $response->setStatusCode(Response::STATUS_CODE_CREATED); + var_dump(Response::STATUS_CODE_CREATED); $response->dynamic($key, Response::MODEL_KEY); }); @@ -905,7 +983,10 @@ App::get('/v1/projects/:projectId/keys') throw new Exception('Project not found', 404); } - $keys = $project->getAttribute('keys', []); + // TODO: Implement pagination + $keys = $dbForConsole->find("projectKeys", [ + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), + ]); $response->dynamic(new Document([ 'keys' => $keys, @@ -928,15 +1009,18 @@ App::get('/v1/projects/:projectId/keys/:keyId') ->inject('response') ->inject('dbForConsole') ->action(function ($projectId, $keyId, $response, $dbForConsole) { + /** @var Appwrite\Utopia\Response $response */ + /** @var Utopia\Database\Database $dbForConsole */ + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { throw new Exception('Project not found', 404); } - $key = $project->find('$id', $keyId, 'keys'); + $key = $dbForConsole->getDocument("projectKeys", $keyId); - if (empty($key) || !$key instanceof Document) { + if ($key->isEmpty()) { throw new Exception('Key not found', 404); } @@ -969,18 +1053,18 @@ App::put('/v1/projects/:projectId/keys/:keyId') throw new Exception('Project not found', 404); } - $key = $project->find('$id', $keyId, 'keys'); + $key = $dbForConsole->getDocument("projectKeys", $keyId); - if (empty($key) || !$key instanceof Document) { + if ($key->isEmpty()) { throw new Exception('Key not found', 404); } - $project->findAndReplace('$id', $key->getId(), $key - ->setAttribute('name', $name) - ->setAttribute('scopes', $scopes) - , 'keys'); + $key->setAttribute('name', $name) + ->setAttribute('scopes', $scopes); - $dbForConsole->updateDocument('projects', $project->getId(), $project); + $dbForConsole->updateDocument("projectKeys", $key->getId(), $key); + + $dbForConsole->purgeDocument('projects', $project->getId()); $response->dynamic($key, Response::MODEL_KEY); }); @@ -1008,11 +1092,15 @@ App::delete('/v1/projects/:projectId/keys/:keyId') throw new Exception('Project not found', 404); } - if (!$project->findAndRemove('$id', $keyId, 'keys')) { + $key = $dbForConsole->getDocument("projectKeys", $keyId); + + if($key->isEmpty()) { throw new Exception('Key not found', 404); } - $dbForConsole->updateDocument('projects', $project->getId(), $project); + $dbForConsole->deleteDocument("projectKeys", $key->getId()); + + $dbForConsole->purgeDocument('projects', $project->getId()); $response->noContent(); }); @@ -1050,10 +1138,10 @@ App::post('/v1/projects/:projectId/platforms') $teamId = $project->getAttribute("teamId"); $platform = new Document([ - 'projectId' => $project->getId(), '$id' => $dbForConsole->getId(), '$read' => ['team:' . $teamId], '$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'], + 'projectId' => $project->getId(), 'type' => $type, 'name' => $name, 'key' => $key, @@ -1063,7 +1151,8 @@ App::post('/v1/projects/:projectId/platforms') 'dateUpdated' => \time(), ]); - $dbForConsole->createDocument("projectsPlatforms", $platform); + $platform = $dbForConsole->createDocument("projectsPlatforms", $platform); + $dbForConsole->purgeDocument('projects', $project->getId()); $response->setStatusCode(Response::STATUS_CODE_CREATED); @@ -1087,14 +1176,13 @@ App::get('/v1/projects/:projectId/platforms') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForConsole */ - // TODO: Implement pagination - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { throw new Exception('Project not found', 404); } + // TODO: Implement pagination $platforms = $dbForConsole->find('projectsPlatforms', [ new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); @@ -1131,7 +1219,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId') $platform = $dbForConsole->getDocument("projectsPlatforms", $platformId); - if (empty($platform) || !$platform instanceof Document) { + if ($platform->isEmpty()) { throw new Exception('Platform not found', 404); } @@ -1168,7 +1256,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') $platform = $dbForConsole->getDocument("projectsPlatforms", $platformId); - if (empty($platform) || !$platform instanceof Document) { + if ($platform->isEmpty()) { throw new Exception('Platform not found', 404); } @@ -1247,9 +1335,12 @@ App::post('/v1/projects/:projectId/domains') throw new Exception('Project not found', 404); } - $document = $project->find('domain', $domain, 'domains'); + $document = $dbForConsole->findOne("projectDomains", [ + new Query('domain', Query::TYPE_EQUAL, [$domain]), + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), + ]); - if ($document) { + if ($document && !$document->isEmpty()) { throw new Exception('Domain already exists', 409); } @@ -1259,10 +1350,15 @@ App::post('/v1/projects/:projectId/domains') throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.', 500); } + $teamId = $project->getAttribute("teamId"); + $domain = new Domain($domain); $domain = new Document([ '$id' => $dbForConsole->getId(), + '$read' => ['team:' . $teamId], + '$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'], + 'projectId' => $project->getId(), 'updated' => \time(), 'domain' => $domain->get(), 'tld' => $domain->getSuffix(), @@ -1271,9 +1367,9 @@ App::post('/v1/projects/:projectId/domains') 'certificateId' => null, ]); - $project = $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('domains', $domain, Document::SET_TYPE_APPEND) - ); + $domain = $dbForConsole->createDocument("projectDomains", $domain); + + $dbForConsole->purgeDocument("projects", $project->getId()); $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($domain, Response::MODEL_DOMAIN); @@ -1302,7 +1398,8 @@ App::get('/v1/projects/:projectId/domains') throw new Exception('Project not found', 404); } - $domains = $project->getAttribute('domains', []); + // TODO: Implement pagination + $domains = $dbForConsole->find("projectDomains", []); $response->dynamic(new Document([ 'domains' => $domains, @@ -1334,9 +1431,9 @@ App::get('/v1/projects/:projectId/domains/:domainId') throw new Exception('Project not found', 404); } - $domain = $project->find('$id', $domainId, 'domains'); + $domain = $dbForConsole->getDocument("projectDomains", $domainId); - if (empty($domain) || !$domain instanceof Document) { + if ($domain->isEmpty()) { throw new Exception('Domain not found', 404); } @@ -1367,9 +1464,9 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') throw new Exception('Project not found', 404); } - $domain = $project->find('$id', $domainId, 'domains'); + $domain = $dbForConsole->getDocument("projectDomains", $domainId); - if (empty($domain) || !$domain instanceof Document) { + if ($domain->isEmpty()) { throw new Exception('Domain not found', 404); } @@ -1389,11 +1486,10 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') throw new Exception('Failed to verify domain', 401); } - $project->findAndReplace('$id', $domain->getId(), $domain - ->setAttribute('verification', true) - , 'domains'); + $domain->setAttribute("verification", true); - $dbForConsole->updateDocument('projects', $project->getId(), $project); + $dbForConsole->updateDocument("projectDomains", $domain->getId(), $domain); + $dbForConsole->purgeDocument("projects", $project->getId()); // Issue a TLS certificate when domain is verified Resque::enqueue('v1-certificates', 'CertificatesV1', [ @@ -1428,13 +1524,15 @@ App::delete('/v1/projects/:projectId/domains/:domainId') throw new Exception('Project not found', 404); } - $domain = $project->find('$id', $domainId, 'domains'); + $domain = $dbForConsole->getDocument("projectDomains", $domainId); - if (!$project->findAndRemove('$id', $domainId, 'domains')) { + if ($domain->isEmpty()) { throw new Exception('Domain not found', 404); } - $dbForConsole->updateDocument('projects', $project->getId(), $project); + $dbForConsole->deleteDocument("projectDomains", $domain->getId()); + + $dbForConsole->purgeDocument("projects", $project->getId()); $deletes ->setParam('type', DELETE_TYPE_CERTIFICATES) diff --git a/app/init.php b/app/init.php index 4da737ebb..704788b25 100644 --- a/app/init.php +++ b/app/init.php @@ -30,6 +30,7 @@ use Appwrite\Network\Validator\URL; use Appwrite\OpenSSL\OpenSSL; use Appwrite\Stats\Stats; use Utopia\App; +use Utopia\CLI\Console; use Utopia\View; use Utopia\Config\Config; use Utopia\Locale\Locale; @@ -181,6 +182,7 @@ Database::addFilter('subQueryAttributes', return null; }, function($value, Document $document, Database $database) { + // TODO: Maybe implement pagination? return $database ->find('attributes', [ new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()]) @@ -193,6 +195,7 @@ Database::addFilter('subQueryIndexes', return null; }, function($value, Document $document, Database $database) { + // TODO: Maybe implement pagination? return $database ->find('indexes', [ new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()]) @@ -205,13 +208,94 @@ Database::addFilter('subQueryProjectPlatforms', return null; }, function($value, Document $document, Database $database) { + // TODO: Implement pagination return $database ->find('projectsPlatforms', [ - new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()]) + new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) ], 100, 0, []); } ); +Database::addFilter('subQueryProjectDomains', + function($value) { + return null; + }, + function($value, Document $document, Database $database) { + // TODO: Implement pagination + return $database + ->find('projectDomains', [ + new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) + ], 100, 0, []); + } +); + +Database::addFilter('subQueryProjectKeys', + function($value) { + return null; + }, + function($value, Document $document, Database $database) { + // TODO: Implement pagination + return $database + ->find('projectKeys', [ + new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) + ], 100, 0, []); + } +); + +Database::addFilter('subQueryProjectWebhooks', + function($value) { + return null; + }, + function($value, Document $document, Database $database) { + // TODO: Implement pagination + return $database + ->find('projectWebhooks', [ + new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) + ], 100, 0, []); + } +); + +Database::addFilter('subQueryProjectServices', + function($value) { + return null; + }, + function($value, Document $document, Database $database) { + // TODO: Implement pagination + $services = $database + ->find('projectsServices', [ + new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) + ], 100, 0, []); + + $responseJson = []; + foreach($services as $service) { + $responseJson[$service->getAttribute("key")] = $service->getAttribute("status", true); + } + + return $responseJson; + } +); + +Database::addFilter('subQueryProjectProviders', + function($value) { + return null; + }, + function($value, Document $document, Database $database) { + // TODO: Implement pagination + $providers = $database + ->find('projectProviders', [ + new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) + ], 100, 0, []); + + $responseJson = []; + foreach($providers as $provider) { + $responseJson[$provider->getAttribute("key") . 'Appid'] = $provider->getAttribute("appId"); + $responseJson[$provider->getAttribute("key") . 'Secret'] = $provider->getAttribute("appSecret"); + } + + return $responseJson; + } +); + Database::addFilter('encrypt', function($value) { $key = App::getEnv('_APP_OPENSSL_KEY_V1'); From abfe47787bb2d317884a293963ad641bbda3a9f0 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Mon, 30 Aug 2021 17:16:42 +0200 Subject: [PATCH 053/130] Added TODO --- app/controllers/api/projects.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 15dee15f8..845dc69b0 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -23,6 +23,8 @@ use Utopia\Validator\WhiteList; use Utopia\Audit\Audit; use Utopia\Abuse\Adapters\TimeLimit; +// TODO: Meldiron is lazy person from JS who uses "" instead of ''. Yell at him if this comment is still here. We dont want "" in PHP + App::init(function ($project) { /** @var Utopia\Database\Document $project */ From 94e330149f3e9ed1a8865aee99db1bc75a2728e4 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 4 Aug 2021 17:04:48 -0400 Subject: [PATCH 054/130] Catch limit exception on attributes --- app/controllers/api/database.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 7df317248..580dc951d 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -89,6 +89,8 @@ $attributesCallback = function ($collectionId, $attribute, $response, $dbForInte ])); } catch (DuplicateException $th) { throw new Exception('Attribute already exists', 409); + } catch (LimitException $e) { + throw new Exception($e->getMessage(), 400); } $dbForInternal->purgeDocument('collections', $collectionId); From 4f4db30a8a9a34a844f76050aa6603e20dc15b19 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 4 Aug 2021 17:05:09 -0400 Subject: [PATCH 055/130] Test for attribute limit exception --- .../Database/DatabaseCustomServerTest.php | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/e2e/Services/Database/DatabaseCustomServerTest.php b/tests/e2e/Services/Database/DatabaseCustomServerTest.php index e5a75fa68..ca869f79d 100644 --- a/tests/e2e/Services/Database/DatabaseCustomServerTest.php +++ b/tests/e2e/Services/Database/DatabaseCustomServerTest.php @@ -307,6 +307,54 @@ class DatabaseCustomServerTest extends Scope $this->assertEquals($response['headers']['status-code'], 404); } + /** + * @depends testDeleteCollection + */ + public function testAttributeCountLimit() + { + $collection = $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' => 'attributeCountLimit', + 'read' => ['role:all'], + 'write' => ['role:all'], + 'permission' => 'document', + ]); + + $collectionId = $collection['body']['$id']; + + // load the collection up to the limit + for ($i=0; $i < 1012; $i++) { + $attribute = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => "attribute{$i}", + 'required' => false, + ]); + + $this->assertEquals(201, $attribute['headers']['status-code']); + } + + sleep(20); + + $tooMany = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => "tooMany", + 'required' => false, + ]); + + $this->assertEquals(400, $tooMany['headers']['status-code']); + $this->assertEquals('Column limit reached. Cannot create new attribute.', $tooMany['body']['message']); + } + + public function testIndexLimitException() { $collection = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([ From 42ca474731baade1528d4dd600b2c37bde5f9da6 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 24 Aug 2021 20:01:11 -0400 Subject: [PATCH 056/130] Search for attributes from internal table --- app/controllers/api/database.php | 11 +++++++++++ .../Services/Database/DatabaseCustomServerTest.php | 7 ++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 580dc951d..609ffc7aa 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -56,6 +56,17 @@ $attributesCallback = function ($collectionId, $attribute, $response, $dbForInte throw new Exception('Collection not found', 404); } + $count = $dbForInternal->count('attributes', [ + new Query('collectionId', Query::TYPE_EQUAL, [$collectionId]) + ], 1012); + + // 1017 limit minus default attributes and one buffer for virtual columns + $limit = 1017 - MariaDB::getNumberOfDefaultAttributes() - 1; + + if ($count >= $limit) { + throw new Exception('Attribute limit exceeded', 400); + } + // TODO@kodumbeats how to depend on $size for Text validator length // Ensure attribute default is within required size if ($size > 0 && !\is_null($default)) { diff --git a/tests/e2e/Services/Database/DatabaseCustomServerTest.php b/tests/e2e/Services/Database/DatabaseCustomServerTest.php index ca869f79d..3884ca7a3 100644 --- a/tests/e2e/Services/Database/DatabaseCustomServerTest.php +++ b/tests/e2e/Services/Database/DatabaseCustomServerTest.php @@ -307,9 +307,6 @@ class DatabaseCustomServerTest extends Scope $this->assertEquals($response['headers']['status-code'], 404); } - /** - * @depends testDeleteCollection - */ public function testAttributeCountLimit() { $collection = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([ @@ -317,6 +314,7 @@ class DatabaseCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]), [ + 'collectionId' => 'unique()', 'name' => 'attributeCountLimit', 'read' => ['role:all'], 'write' => ['role:all'], @@ -351,10 +349,9 @@ class DatabaseCustomServerTest extends Scope ]); $this->assertEquals(400, $tooMany['headers']['status-code']); - $this->assertEquals('Column limit reached. Cannot create new attribute.', $tooMany['body']['message']); + $this->assertEquals('Attribute limit exceeded', $tooMany['body']['message']); } - public function testIndexLimitException() { $collection = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([ From 8f066259801f8c36ff8471e0b49c93e59a5a4490 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 31 Aug 2021 11:53:20 -0400 Subject: [PATCH 057/130] Reduce sleep time in test --- tests/e2e/Services/Database/DatabaseCustomServerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Database/DatabaseCustomServerTest.php b/tests/e2e/Services/Database/DatabaseCustomServerTest.php index 3884ca7a3..97b12aa27 100644 --- a/tests/e2e/Services/Database/DatabaseCustomServerTest.php +++ b/tests/e2e/Services/Database/DatabaseCustomServerTest.php @@ -337,7 +337,7 @@ class DatabaseCustomServerTest extends Scope $this->assertEquals(201, $attribute['headers']['status-code']); } - sleep(20); + sleep(5); $tooMany = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([ 'content-type' => 'application/json', From efa0b8629926520ba798a2380b8a20ecdf6d5faf Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 31 Aug 2021 12:35:03 -0400 Subject: [PATCH 058/130] Add assertions for row width limit --- .../Database/DatabaseCustomServerTest.php | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/e2e/Services/Database/DatabaseCustomServerTest.php b/tests/e2e/Services/Database/DatabaseCustomServerTest.php index 97b12aa27..2be62f8fa 100644 --- a/tests/e2e/Services/Database/DatabaseCustomServerTest.php +++ b/tests/e2e/Services/Database/DatabaseCustomServerTest.php @@ -352,6 +352,56 @@ class DatabaseCustomServerTest extends Scope $this->assertEquals('Attribute limit exceeded', $tooMany['body']['message']); } + public function testAttributeRowWidthLimit() + { + $collection = $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'] + ]), [ + 'collectionId' => 'attributeRowWidthLimit', + 'name' => 'attributeRowWidthLimit', + 'read' => ['role:all'], + 'write' => ['role:all'], + 'permission' => 'document', + ]); + + $this->assertEquals($collection['headers']['status-code'], 201); + $this->assertEquals($collection['body']['name'], 'attributeRowWidthLimit'); + + $collectionId = $collection['body']['$id']; + + // Add wide string attributes to approach row width limit + for ($i=0; $i < 15; $i++) { + $attribute = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => "attribute{$i}", + 'size' => 1024, + 'required' => true, + ]); + + $this->assertEquals($attribute['headers']['status-code'], 201); + } + + sleep(5); + + $tooWide = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'tooWide', + 'size' => 1024, + 'required' => true, + ]); + + // $this->assertEquals(400, $tooWide['headers']['status-code']); + // $this->assertEquals('Attribute limit exceeded', $tooWide['body']['message']); + } + public function testIndexLimitException() { $collection = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([ From 53b8a1c245800b9df14c4dfa70327f93a692bcf3 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 31 Aug 2021 14:00:28 -0400 Subject: [PATCH 059/130] Rename defaultValue filter to casting --- app/config/collections2.php | 2 +- app/init.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/config/collections2.php b/app/config/collections2.php index a14d58cf2..6e13f19f2 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -174,7 +174,7 @@ $collections = [ 'required' => false, 'default' => null, 'array' => false, - 'filters' => ['defaultValue'], + 'filters' => ['casting'], ], [ '$id' => 'signed', diff --git a/app/init.php b/app/init.php index b8fcbdb11..cdaf8cfeb 100644 --- a/app/init.php +++ b/app/init.php @@ -184,7 +184,7 @@ DatabaseOld::addFilter('encrypt', /** * New DB Filters */ -Database::addFilter('defaultValue', +Database::addFilter('casting', function($value) { return json_encode(['value' => $value]); }, From e2d1b2df58a74b2a3e4c3e51d91ea9ea991ba831 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 31 Aug 2021 21:13:36 -0400 Subject: [PATCH 060/130] Fetch new checkAttribute database method --- composer.json | 2 +- composer.lock | 46 +++++++++++++++++++++++----------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/composer.json b/composer.json index d48d2378a..9bd152fb9 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "utopia-php/cache": "0.4.*", "utopia-php/cli": "0.11.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "dev-feat-adjusted-query-validator as 0.10.0", + "utopia-php/database": "dev-feat-check-attribute-method as 0.10.0", "utopia-php/locale": "0.4.*", "utopia-php/registry": "0.5.*", "utopia-php/preloader": "0.2.*", diff --git a/composer.lock b/composer.lock index a5f2a3e06..829e34820 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "175f077512c575216c4c88f1d33c6d00", + "content-hash": "9a7739aabd503572b8010be91192b144", "packages": [ { "name": "adhocore/jwt", @@ -355,16 +355,16 @@ }, { "name": "composer/package-versions-deprecated", - "version": "1.11.99.2", + "version": "1.11.99.3", "source": { "type": "git", "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "c6522afe5540d5fc46675043d3ed5a45a740b27c" + "reference": "fff576ac850c045158a250e7e27666e146e78d18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/c6522afe5540d5fc46675043d3ed5a45a740b27c", - "reference": "c6522afe5540d5fc46675043d3ed5a45a740b27c", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/fff576ac850c045158a250e7e27666e146e78d18", + "reference": "fff576ac850c045158a250e7e27666e146e78d18", "shasum": "" }, "require": { @@ -408,7 +408,7 @@ "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", "support": { "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.2" + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.3" }, "funding": [ { @@ -424,7 +424,7 @@ "type": "tidelift" } ], - "time": "2021-05-24T07:46:03+00:00" + "time": "2021-08-17T13:49:14+00:00" }, { "name": "dragonmantank/cron-expression", @@ -1984,11 +1984,11 @@ }, { "name": "utopia-php/database", - "version": "dev-feat-adjusted-query-validator", + "version": "dev-feat-check-attribute-method", "source": { "type": "git", "url": "https://github.com/utopia-php/database", - "reference": "cb73391371f70ddb54bc0000064b15c5f173cb7a" + "reference": "2d1f48345f1284876f6847599c66eee145b7233e" }, "require": { "ext-mongodb": "*", @@ -2037,7 +2037,7 @@ "upf", "utopia" ], - "time": "2021-08-23T14:18:47+00:00" + "time": "2021-09-01T00:50:12+00:00" }, { "name": "utopia-php/domains", @@ -5313,16 +5313,16 @@ }, { "name": "symfony/console", - "version": "v5.3.6", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "51b71afd6d2dc8f5063199357b9880cea8d8bfe2" + "reference": "8b1008344647462ae6ec57559da166c2bfa5e16a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/51b71afd6d2dc8f5063199357b9880cea8d8bfe2", - "reference": "51b71afd6d2dc8f5063199357b9880cea8d8bfe2", + "url": "https://api.github.com/repos/symfony/console/zipball/8b1008344647462ae6ec57559da166c2bfa5e16a", + "reference": "8b1008344647462ae6ec57559da166c2bfa5e16a", "shasum": "" }, "require": { @@ -5392,7 +5392,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.3.6" + "source": "https://github.com/symfony/console/tree/v5.3.7" }, "funding": [ { @@ -5408,7 +5408,7 @@ "type": "tidelift" } ], - "time": "2021-07-27T19:10:22+00:00" + "time": "2021-08-25T20:02:16+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5882,16 +5882,16 @@ }, { "name": "symfony/string", - "version": "v5.3.3", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1" + "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1", - "reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1", + "url": "https://api.github.com/repos/symfony/string/zipball/8d224396e28d30f81969f083a58763b8b9ceb0a5", + "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5", "shasum": "" }, "require": { @@ -5945,7 +5945,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.3.3" + "source": "https://github.com/symfony/string/tree/v5.3.7" }, "funding": [ { @@ -5961,7 +5961,7 @@ "type": "tidelift" } ], - "time": "2021-06-27T11:44:38+00:00" + "time": "2021-08-26T08:00:08+00:00" }, { "name": "theseer/tokenizer", @@ -6251,7 +6251,7 @@ "aliases": [ { "package": "utopia-php/database", - "version": "dev-feat-adjusted-query-validator", + "version": "dev-feat-check-attribute-method", "alias": "0.10.0", "alias_normalized": "0.10.0.0" } From ae58a600659031a7d3669fa4dce824a83e06ce57 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 31 Aug 2021 21:13:58 -0400 Subject: [PATCH 061/130] Use checkAttribute to catch index limits --- app/controllers/api/database.php | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 609ffc7aa..7cda57662 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -56,17 +56,6 @@ $attributesCallback = function ($collectionId, $attribute, $response, $dbForInte throw new Exception('Collection not found', 404); } - $count = $dbForInternal->count('attributes', [ - new Query('collectionId', Query::TYPE_EQUAL, [$collectionId]) - ], 1012); - - // 1017 limit minus default attributes and one buffer for virtual columns - $limit = 1017 - MariaDB::getNumberOfDefaultAttributes() - 1; - - if ($count >= $limit) { - throw new Exception('Attribute limit exceeded', 400); - } - // TODO@kodumbeats how to depend on $size for Text validator length // Ensure attribute default is within required size if ($size > 0 && !\is_null($default)) { @@ -82,8 +71,7 @@ $attributesCallback = function ($collectionId, $attribute, $response, $dbForInte } } - try { - $attribute = $dbForInternal->createDocument('attributes', new Document([ + $attribute = new Document([ '$id' => $collectionId.'_'.$attributeId, 'key' => $attributeId, 'collectionId' => $collectionId, @@ -96,8 +84,16 @@ $attributesCallback = function ($collectionId, $attribute, $response, $dbForInte 'array' => $array, 'format' => $format, 'formatOptions' => $formatOptions, - 'filters' => $filters, - ])); + ]); + + try { + $dbForInternal->checkAttribute($collection, $attribute); + } catch (LimitException $exception) { + throw new Exception('Attribute limit exceeded', 400); + } + + try { + $attribute = $dbForInternal->createDocument('attributes', $attribute); } catch (DuplicateException $th) { throw new Exception('Attribute already exists', 409); } catch (LimitException $e) { From 577ab9c3ad27bed118f5ce4b90b48f03472d3119 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Wed, 1 Sep 2021 09:03:22 +0200 Subject: [PATCH 062/130] Fixed permissions, improved database filter docs, added 404 platform update test --- app/controllers/api/projects.php | 37 ++++++------------- app/init.php | 2 + .../Projects/ProjectsConsoleClientTest.php | 12 ++++++ 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 845dc69b0..8b6a59df4 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -495,12 +495,10 @@ App::patch('/v1/projects/:projectId/service') // TODO: Implement delete method. Do we delete for "true" or for "false"? Same for auth!!!! if($service == false || $service->isEmpty()) { - $teamId = $project->getAttribute("teamId"); - $service = new Document([ '$id' => $dbForConsole->getId(), - '$read' => ['team:' . $teamId], - '$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'], + '$read' => ['role:all'], + '$write' => ['role:all'], 'projectId' => $project->getId(), 'key' => $serviceKey, 'status' => $status, @@ -565,12 +563,10 @@ App::patch('/v1/projects/:projectId/oauth2') } else { // Provider does not exist yet - $teamId = $project->getAttribute("teamId"); - $provider = new Document([ '$id' => $dbForConsole->getId(), - '$read' => ['team:' . $teamId], - '$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'], + '$read' => ['role:all'], + '$write' => ['role:all'], 'projectId' => $project->getId(), 'key' => $providerKey, 'appId' => $appId, @@ -736,12 +732,10 @@ App::post('/v1/projects/:projectId/webhooks') $security = ($security === '1' || $security === 'true' || $security === 1 || $security === true); - $teamId = $project->getAttribute("teamId"); - $webhook = new Document([ '$id' => $dbForConsole->getId(), - '$read' => ['team:' . $teamId], - '$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'], + '$read' => ['role:all'], + '$write' => ['role:all'], 'projectId' => $project->getId(), 'name' => $name, 'events' => $events, @@ -941,12 +935,10 @@ App::post('/v1/projects/:projectId/keys') throw new Exception('Project not found', 404); } - $teamId = $project->getAttribute("teamId"); - $key = new Document([ '$id' => $dbForConsole->getId(), - '$read' => ['team:' . $teamId], - '$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'], + '$read' => ['role:all'], + '$write' => ['role:all'], 'projectId' => $project->getId(), 'name' => $name, 'scopes' => $scopes, @@ -958,7 +950,6 @@ App::post('/v1/projects/:projectId/keys') $dbForConsole->purgeDocument("projects", $project->getId()); $response->setStatusCode(Response::STATUS_CODE_CREATED); - var_dump(Response::STATUS_CODE_CREATED); $response->dynamic($key, Response::MODEL_KEY); }); @@ -1137,12 +1128,10 @@ App::post('/v1/projects/:projectId/platforms') throw new Exception('Project not found', 404); } - $teamId = $project->getAttribute("teamId"); - $platform = new Document([ '$id' => $dbForConsole->getId(), - '$read' => ['team:' . $teamId], - '$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'], + '$read' => ['role:all'], + '$write' => ['role:all'], 'projectId' => $project->getId(), 'type' => $type, 'name' => $name, @@ -1352,14 +1341,12 @@ App::post('/v1/projects/:projectId/domains') throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.', 500); } - $teamId = $project->getAttribute("teamId"); - $domain = new Domain($domain); $domain = new Document([ '$id' => $dbForConsole->getId(), - '$read' => ['team:' . $teamId], - '$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'], + '$read' => ['role:all'], + '$write' => ['role:all'], 'projectId' => $project->getId(), 'updated' => \time(), 'domain' => $domain->get(), diff --git a/app/init.php b/app/init.php index 704788b25..7c44ba12e 100644 --- a/app/init.php +++ b/app/init.php @@ -142,6 +142,8 @@ if(!empty($user) || !empty($pass)) { /** * DB Filters + * + * Make sure the value of an attribute that uses sub-query filters is set to 'null' otherwise the filters might not work properly */ DatabaseOld::addFilter('json', function($value) { diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 4d83efb41..f090158cd 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -7,6 +7,7 @@ use Tests\E2E\Scopes\ProjectConsole; use Tests\E2E\Scopes\SideClient; use Tests\E2E\Services\Projects\ProjectsBase; use Tests\E2E\Client; +use function array_merge; class ProjectsConsoleClientTest extends Scope { @@ -1430,6 +1431,17 @@ class ProjectsConsoleClientTest extends Scope /** * Test for FAILURE */ + $response = $this->client->call(Client::METHOD_PUT, '/projects/'.$id.'/platforms/error', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'Flutter App (Android) 2', + 'key' => 'com.example.android2', + 'store' => '', + 'hostname' => '', + ]); + + $this->assertEquals(404, $response['headers']['status-code']); return $data; } From 182630ed203e2174e2138bcdc3b397fff7799114 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Wed, 1 Sep 2021 09:52:43 +0200 Subject: [PATCH 063/130] Rollbacked auth attribute, set subquery limits to 5000, Replaced " with ' --- app/controllers/api/projects.php | 100 ++++++++++++++----------------- app/init.php | 30 ++++------ 2 files changed, 57 insertions(+), 73 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 8b6a59df4..cdc43d7b1 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -23,8 +23,6 @@ use Utopia\Validator\WhiteList; use Utopia\Audit\Audit; use Utopia\Abuse\Adapters\TimeLimit; -// TODO: Meldiron is lazy person from JS who uses "" instead of ''. Yell at him if this comment is still here. We dont want "" in PHP - App::init(function ($project) { /** @var Utopia\Database\Document $project */ @@ -101,11 +99,9 @@ App::post('/v1/projects') 'webhooks' => null, 'keys' => null, 'domains' => null, - 'auths' => null + 'auths' => $auths ])); - // TODO: Implement write of auths from $auths - $collections = Config::getParam('collections2', []); /** @var array $collections */ $dbForInternal->setNamespace('project_' . $project->getId() . '_internal'); @@ -116,7 +112,7 @@ App::post('/v1/projects') $audit = new Audit($dbForInternal); $audit->setup(); - $adapter = new TimeLimit("", 0, 1, $dbForInternal); + $adapter = new TimeLimit('', 0, 1, $dbForInternal); $adapter->setup(); foreach ($collections as $key => $collection) { @@ -488,12 +484,11 @@ App::patch('/v1/projects/:projectId/service') throw new Exception('Project not found', 404); } - $service = $dbForConsole->findOne("projectsServices", [ + $service = $dbForConsole->findOne('projectsServices', [ new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), new Query('key', Query::TYPE_EQUAL, [$serviceKey]), ]); - // TODO: Implement delete method. Do we delete for "true" or for "false"? Same for auth!!!! if($service == false || $service->isEmpty()) { $service = new Document([ '$id' => $dbForConsole->getId(), @@ -504,13 +499,13 @@ App::patch('/v1/projects/:projectId/service') 'status' => $status, ]); - $dbForConsole->createDocument("projectsServices", $service); + $dbForConsole->createDocument('projectsServices', $service); $dbForConsole->purgeDocument('projects', $project->getId()); } else { - if($service->getAttribute("status") != $status) { - $service->setAttribute("status", $status); - $dbForConsole->updateDocument("projectsServices", $service->getId(), $service); + if($service->getAttribute('status') != $status) { + $service->setAttribute('status', $status); + $dbForConsole->updateDocument('projectsServices', $service->getId(), $service); $dbForConsole->purgeDocument('projects', $project->getId()); } @@ -546,7 +541,7 @@ App::patch('/v1/projects/:projectId/oauth2') throw new Exception('Project not found', 404); } - $provider = $dbForConsole->findOne("projectProviders", [ + $provider = $dbForConsole->findOne('projectProviders', [ new Query('key', Query::TYPE_EQUAL, [$providerKey]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), ]); @@ -554,12 +549,12 @@ App::patch('/v1/projects/:projectId/oauth2') if($provider && !$provider->isEmpty()) { // Provider exists - $provider->setAttribute("appId", $appId) - ->setAttribute("appSecret", $secret); + $provider->setAttribute('appId', $appId) + ->setAttribute('appSecret', $secret); - $dbForConsole->updateDocument("projectProviders", $provider->getId(), $provider); + $dbForConsole->updateDocument('projectProviders', $provider->getId(), $provider); - $dbForConsole->purgeDocument("projects", $project->getId()); + $dbForConsole->purgeDocument('projects', $project->getId()); } else { // Provider does not exist yet @@ -573,9 +568,9 @@ App::patch('/v1/projects/:projectId/oauth2') 'appSecret' => $secret ]); - $dbForConsole->createDocument("projectProviders", $provider); + $dbForConsole->createDocument('projectProviders', $provider); - $dbForConsole->purgeDocument("projects", $project->getId()); + $dbForConsole->purgeDocument('projects', $project->getId()); } $project = $dbForConsole->getDocument('projects', $projectId); @@ -745,9 +740,9 @@ App::post('/v1/projects/:projectId/webhooks') 'httpPass' => $httpPass, ]); - $webhook = $dbForConsole->createDocument("projectWebhooks", $webhook); + $webhook = $dbForConsole->createDocument('projectWebhooks', $webhook); - $dbForConsole->purgeDocument("projects", $project->getId()); + $dbForConsole->purgeDocument('projects', $project->getId()); $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($webhook, Response::MODEL_WEBHOOK); @@ -776,7 +771,7 @@ App::get('/v1/projects/:projectId/webhooks') throw new Exception('Project not found', 404); } - $webhooks = $dbForConsole->find("projectWebhooks", [ + $webhooks = $dbForConsole->find('projectWebhooks', [ new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); @@ -865,7 +860,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') ->setAttribute('httpUser', $httpUser) ->setAttribute('httpPass', $httpPass); - $dbForConsole->updateDocument("projectWebhooks", $webhook->getId(), $webhook); + $dbForConsole->updateDocument('projectWebhooks', $webhook->getId(), $webhook); $dbForConsole->purgeDocument('projects', $project->getId()); @@ -895,13 +890,13 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') throw new Exception('Project not found', 404); } - $webhook = $dbForConsole->getDocument("projectWebhooks", $webhookId); + $webhook = $dbForConsole->getDocument('projectWebhooks', $webhookId); if($webhook->isEmpty()) { throw new Exception('Webhook not found', 404); } - $dbForConsole->deleteDocument("projectWebhooks", $webhook->getId()); + $dbForConsole->deleteDocument('projectWebhooks', $webhook->getId()); $dbForConsole->purgeDocument('projects', $project->getId()); @@ -945,9 +940,9 @@ App::post('/v1/projects/:projectId/keys') 'secret' => \bin2hex(\random_bytes(128)), ]); - $key = $dbForConsole->createDocument("projectKeys", $key); + $key = $dbForConsole->createDocument('projectKeys', $key); - $dbForConsole->purgeDocument("projects", $project->getId()); + $dbForConsole->purgeDocument('projects', $project->getId()); $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($key, Response::MODEL_KEY); @@ -976,10 +971,9 @@ App::get('/v1/projects/:projectId/keys') throw new Exception('Project not found', 404); } - // TODO: Implement pagination - $keys = $dbForConsole->find("projectKeys", [ + $keys = $dbForConsole->find('projectKeys', [ new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), - ]); + ], 5000); $response->dynamic(new Document([ 'keys' => $keys, @@ -1011,7 +1005,7 @@ App::get('/v1/projects/:projectId/keys/:keyId') throw new Exception('Project not found', 404); } - $key = $dbForConsole->getDocument("projectKeys", $keyId); + $key = $dbForConsole->getDocument('projectKeys', $keyId); if ($key->isEmpty()) { throw new Exception('Key not found', 404); @@ -1046,7 +1040,7 @@ App::put('/v1/projects/:projectId/keys/:keyId') throw new Exception('Project not found', 404); } - $key = $dbForConsole->getDocument("projectKeys", $keyId); + $key = $dbForConsole->getDocument('projectKeys', $keyId); if ($key->isEmpty()) { throw new Exception('Key not found', 404); @@ -1055,7 +1049,7 @@ App::put('/v1/projects/:projectId/keys/:keyId') $key->setAttribute('name', $name) ->setAttribute('scopes', $scopes); - $dbForConsole->updateDocument("projectKeys", $key->getId(), $key); + $dbForConsole->updateDocument('projectKeys', $key->getId(), $key); $dbForConsole->purgeDocument('projects', $project->getId()); @@ -1085,13 +1079,13 @@ App::delete('/v1/projects/:projectId/keys/:keyId') throw new Exception('Project not found', 404); } - $key = $dbForConsole->getDocument("projectKeys", $keyId); + $key = $dbForConsole->getDocument('projectKeys', $keyId); if($key->isEmpty()) { throw new Exception('Key not found', 404); } - $dbForConsole->deleteDocument("projectKeys", $key->getId()); + $dbForConsole->deleteDocument('projectKeys', $key->getId()); $dbForConsole->purgeDocument('projects', $project->getId()); @@ -1142,7 +1136,7 @@ App::post('/v1/projects/:projectId/platforms') 'dateUpdated' => \time(), ]); - $platform = $dbForConsole->createDocument("projectsPlatforms", $platform); + $platform = $dbForConsole->createDocument('projectsPlatforms', $platform); $dbForConsole->purgeDocument('projects', $project->getId()); @@ -1173,10 +1167,9 @@ App::get('/v1/projects/:projectId/platforms') throw new Exception('Project not found', 404); } - // TODO: Implement pagination $platforms = $dbForConsole->find('projectsPlatforms', [ new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) - ]); + ], 5000); $response->dynamic(new Document([ 'platforms' => $platforms, @@ -1208,7 +1201,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId') throw new Exception('Project not found', 404); } - $platform = $dbForConsole->getDocument("projectsPlatforms", $platformId); + $platform = $dbForConsole->getDocument('projectsPlatforms', $platformId); if ($platform->isEmpty()) { throw new Exception('Platform not found', 404); @@ -1245,7 +1238,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') throw new Exception('Project not found', 404); } - $platform = $dbForConsole->getDocument("projectsPlatforms", $platformId); + $platform = $dbForConsole->getDocument('projectsPlatforms', $platformId); if ($platform->isEmpty()) { throw new Exception('Platform not found', 404); @@ -1289,7 +1282,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') throw new Exception('Project not found', 404); } - $deleteRecord = $dbForConsole->deleteDocument("projectsPlatforms", $platformId); + $deleteRecord = $dbForConsole->deleteDocument('projectsPlatforms', $platformId); if($deleteRecord == false) { throw new Exception('Platform not found', 404); @@ -1326,7 +1319,7 @@ App::post('/v1/projects/:projectId/domains') throw new Exception('Project not found', 404); } - $document = $dbForConsole->findOne("projectDomains", [ + $document = $dbForConsole->findOne('projectDomains', [ new Query('domain', Query::TYPE_EQUAL, [$domain]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), ]); @@ -1356,9 +1349,9 @@ App::post('/v1/projects/:projectId/domains') 'certificateId' => null, ]); - $domain = $dbForConsole->createDocument("projectDomains", $domain); + $domain = $dbForConsole->createDocument('projectDomains', $domain); - $dbForConsole->purgeDocument("projects", $project->getId()); + $dbForConsole->purgeDocument('projects', $project->getId()); $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($domain, Response::MODEL_DOMAIN); @@ -1387,8 +1380,7 @@ App::get('/v1/projects/:projectId/domains') throw new Exception('Project not found', 404); } - // TODO: Implement pagination - $domains = $dbForConsole->find("projectDomains", []); + $domains = $dbForConsole->find('projectDomains', [], 5000); $response->dynamic(new Document([ 'domains' => $domains, @@ -1420,7 +1412,7 @@ App::get('/v1/projects/:projectId/domains/:domainId') throw new Exception('Project not found', 404); } - $domain = $dbForConsole->getDocument("projectDomains", $domainId); + $domain = $dbForConsole->getDocument('projectDomains', $domainId); if ($domain->isEmpty()) { throw new Exception('Domain not found', 404); @@ -1453,7 +1445,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') throw new Exception('Project not found', 404); } - $domain = $dbForConsole->getDocument("projectDomains", $domainId); + $domain = $dbForConsole->getDocument('projectDomains', $domainId); if ($domain->isEmpty()) { throw new Exception('Domain not found', 404); @@ -1475,10 +1467,10 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') throw new Exception('Failed to verify domain', 401); } - $domain->setAttribute("verification", true); + $domain->setAttribute('verification', true); - $dbForConsole->updateDocument("projectDomains", $domain->getId(), $domain); - $dbForConsole->purgeDocument("projects", $project->getId()); + $dbForConsole->updateDocument('projectDomains', $domain->getId(), $domain); + $dbForConsole->purgeDocument('projects', $project->getId()); // Issue a TLS certificate when domain is verified Resque::enqueue('v1-certificates', 'CertificatesV1', [ @@ -1513,15 +1505,15 @@ App::delete('/v1/projects/:projectId/domains/:domainId') throw new Exception('Project not found', 404); } - $domain = $dbForConsole->getDocument("projectDomains", $domainId); + $domain = $dbForConsole->getDocument('projectDomains', $domainId); if ($domain->isEmpty()) { throw new Exception('Domain not found', 404); } - $dbForConsole->deleteDocument("projectDomains", $domain->getId()); + $dbForConsole->deleteDocument('projectDomains', $domain->getId()); - $dbForConsole->purgeDocument("projects", $project->getId()); + $dbForConsole->purgeDocument('projects', $project->getId()); $deletes ->setParam('type', DELETE_TYPE_CERTIFICATES) diff --git a/app/init.php b/app/init.php index 7c44ba12e..adb42fbdd 100644 --- a/app/init.php +++ b/app/init.php @@ -184,11 +184,10 @@ Database::addFilter('subQueryAttributes', return null; }, function($value, Document $document, Database $database) { - // TODO: Maybe implement pagination? return $database ->find('attributes', [ new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()]) - ], 100, 0, []); + ], 5000, 0, []); } ); @@ -197,11 +196,10 @@ Database::addFilter('subQueryIndexes', return null; }, function($value, Document $document, Database $database) { - // TODO: Maybe implement pagination? return $database ->find('indexes', [ new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()]) - ], 100, 0, []); + ], 5000, 0, []); } ); @@ -210,11 +208,10 @@ Database::addFilter('subQueryProjectPlatforms', return null; }, function($value, Document $document, Database $database) { - // TODO: Implement pagination return $database ->find('projectsPlatforms', [ new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) - ], 100, 0, []); + ], 5000, 0, []); } ); @@ -223,11 +220,10 @@ Database::addFilter('subQueryProjectDomains', return null; }, function($value, Document $document, Database $database) { - // TODO: Implement pagination return $database ->find('projectDomains', [ new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) - ], 100, 0, []); + ], 5000, 0, []); } ); @@ -236,11 +232,10 @@ Database::addFilter('subQueryProjectKeys', return null; }, function($value, Document $document, Database $database) { - // TODO: Implement pagination return $database ->find('projectKeys', [ new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) - ], 100, 0, []); + ], 5000, 0, []); } ); @@ -249,11 +244,10 @@ Database::addFilter('subQueryProjectWebhooks', return null; }, function($value, Document $document, Database $database) { - // TODO: Implement pagination return $database ->find('projectWebhooks', [ new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) - ], 100, 0, []); + ], 5000, 0, []); } ); @@ -262,15 +256,14 @@ Database::addFilter('subQueryProjectServices', return null; }, function($value, Document $document, Database $database) { - // TODO: Implement pagination $services = $database ->find('projectsServices', [ new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) - ], 100, 0, []); + ], 5000, 0, []); $responseJson = []; foreach($services as $service) { - $responseJson[$service->getAttribute("key")] = $service->getAttribute("status", true); + $responseJson[$service->getAttribute('key')] = $service->getAttribute('status', true); } return $responseJson; @@ -282,16 +275,15 @@ Database::addFilter('subQueryProjectProviders', return null; }, function($value, Document $document, Database $database) { - // TODO: Implement pagination $providers = $database ->find('projectProviders', [ new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) - ], 100, 0, []); + ], 5000, 0, []); $responseJson = []; foreach($providers as $provider) { - $responseJson[$provider->getAttribute("key") . 'Appid'] = $provider->getAttribute("appId"); - $responseJson[$provider->getAttribute("key") . 'Secret'] = $provider->getAttribute("appSecret"); + $responseJson[$provider->getAttribute('key') . 'Appid'] = $provider->getAttribute('appId'); + $responseJson[$provider->getAttribute('key') . 'Secret'] = $provider->getAttribute('appSecret'); } return $responseJson; From 51b1a7e72c8aa3152d790c1165b01e1b0b9917e4 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Wed, 1 Sep 2021 10:25:54 +0200 Subject: [PATCH 064/130] Removed re-selecting queries, improved selecting from sub-tables --- app/controllers/api/projects.php | 122 +++++++++++++++++++++---------- 1 file changed, 85 insertions(+), 37 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index cdc43d7b1..97a914baf 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -473,7 +473,7 @@ App::patch('/v1/projects/:projectId/service') ->param('status', true, new Boolean(), 'Service status.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $serviceKey, $status, $response, $dbForConsole) { + ->action(function ($projectId, $service, $status, $response, $dbForConsole) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForConsole */ /** @var Boolean $status */ @@ -484,34 +484,38 @@ App::patch('/v1/projects/:projectId/service') throw new Exception('Project not found', 404); } - $service = $dbForConsole->findOne('projectsServices', [ + $document = $dbForConsole->findOne('projectsServices', [ new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), - new Query('key', Query::TYPE_EQUAL, [$serviceKey]), + new Query('key', Query::TYPE_EQUAL, [$service]), ]); - if($service == false || $service->isEmpty()) { - $service = new Document([ + if($document == false || $document->isEmpty()) { + $document = new Document([ '$id' => $dbForConsole->getId(), '$read' => ['role:all'], '$write' => ['role:all'], 'projectId' => $project->getId(), - 'key' => $serviceKey, + 'key' => $service, 'status' => $status, ]); - $dbForConsole->createDocument('projectsServices', $service); + $dbForConsole->createDocument('projectsServices', $document); + + $project + ->setAttribute('services', $document, Document::SET_TYPE_APPEND); $dbForConsole->purgeDocument('projects', $project->getId()); } else { - if($service->getAttribute('status') != $status) { - $service->setAttribute('status', $status); - $dbForConsole->updateDocument('projectsServices', $service->getId(), $service); + if($document->getAttribute('status') != $status) { + $document->setAttribute('status', $status); + $dbForConsole->updateDocument('projectsServices', $document->getId(), $document); + + $project->findAndReplace('$id', $document->getId(), $document, 'services'); $dbForConsole->purgeDocument('projects', $project->getId()); } } - $project = $dbForConsole->getDocument('projects', $projectId); $response->dynamic($project, Response::MODEL_PROJECT); }); @@ -554,6 +558,8 @@ App::patch('/v1/projects/:projectId/oauth2') $dbForConsole->updateDocument('projectProviders', $provider->getId(), $provider); + $project->findAndReplace('$id', $provider->getId(), $provider, 'providers'); + $dbForConsole->purgeDocument('projects', $project->getId()); } else { // Provider does not exist yet @@ -570,10 +576,12 @@ App::patch('/v1/projects/:projectId/oauth2') $dbForConsole->createDocument('projectProviders', $provider); + $project + ->setAttribute('providers', $provider, Document::SET_TYPE_APPEND); + $dbForConsole->purgeDocument('projects', $project->getId()); } - $project = $dbForConsole->getDocument('projects', $projectId); $response->dynamic($project, Response::MODEL_PROJECT); }); @@ -805,9 +813,12 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId') throw new Exception('Project not found', 404); } - $webhook = $dbForConsole->getDocument('projectWebhooks', $webhookId); + $webhook = $dbForConsole->findOne('projectWebhooks', [ + new Query('_uid', Query::TYPE_EQUAL, [$webhookId]), + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) + ]); - if ($webhook->isEmpty()) { + if ($webhook == false || $webhook->isEmpty()) { throw new Exception('Webhook not found', 404); } @@ -846,9 +857,12 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') $security = ($security === '1' || $security === 'true' || $security === 1 || $security === true); - $webhook = $dbForConsole->getDocument('projectWebhooks', $webhookId); + $webhook = $dbForConsole->findOne('projectWebhooks', [ + new Query('_uid', Query::TYPE_EQUAL, [$webhookId]), + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) + ]); - if ($webhook->isEmpty()) { + if ($webhook == false || $webhook->isEmpty()) { throw new Exception('Webhook not found', 404); } @@ -890,9 +904,12 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') throw new Exception('Project not found', 404); } - $webhook = $dbForConsole->getDocument('projectWebhooks', $webhookId); + $webhook = $dbForConsole->findOne('projectWebhooks', [ + new Query('_uid', Query::TYPE_EQUAL, [$webhookId]), + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) + ]); - if($webhook->isEmpty()) { + if($webhook == false || $webhook->isEmpty()) { throw new Exception('Webhook not found', 404); } @@ -1005,9 +1022,12 @@ App::get('/v1/projects/:projectId/keys/:keyId') throw new Exception('Project not found', 404); } - $key = $dbForConsole->getDocument('projectKeys', $keyId); + $key = $dbForConsole->findOne('projectKeys', [ + new Query('_uid', Query::TYPE_EQUAL, [$keyId]), + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) + ]); - if ($key->isEmpty()) { + if ($key !== true || $key->isEmpty()) { throw new Exception('Key not found', 404); } @@ -1040,9 +1060,12 @@ App::put('/v1/projects/:projectId/keys/:keyId') throw new Exception('Project not found', 404); } - $key = $dbForConsole->getDocument('projectKeys', $keyId); + $key = $dbForConsole->findOne('projectKeys', [ + new Query('_uid', Query::TYPE_EQUAL, [$keyId]), + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) + ]); - if ($key->isEmpty()) { + if ($key == false || $key->isEmpty()) { throw new Exception('Key not found', 404); } @@ -1079,9 +1102,12 @@ App::delete('/v1/projects/:projectId/keys/:keyId') throw new Exception('Project not found', 404); } - $key = $dbForConsole->getDocument('projectKeys', $keyId); + $key = $dbForConsole->findOne('projectKeys', [ + new Query('_uid', Query::TYPE_EQUAL, [$keyId]), + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) + ]); - if($key->isEmpty()) { + if($key == false || $key->isEmpty()) { throw new Exception('Key not found', 404); } @@ -1201,9 +1227,12 @@ App::get('/v1/projects/:projectId/platforms/:platformId') throw new Exception('Project not found', 404); } - $platform = $dbForConsole->getDocument('projectsPlatforms', $platformId); + $platform = $dbForConsole->findOne('projectsPlatforms', [ + new Query('_uid', Query::TYPE_EQUAL, [$platformId]), + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) + ]); - if ($platform->isEmpty()) { + if ($platform == false || $platform->isEmpty()) { throw new Exception('Platform not found', 404); } @@ -1238,9 +1267,12 @@ App::put('/v1/projects/:projectId/platforms/:platformId') throw new Exception('Project not found', 404); } - $platform = $dbForConsole->getDocument('projectsPlatforms', $platformId); + $platform = $dbForConsole->findOne('projectsPlatforms', [ + new Query('_uid', Query::TYPE_EQUAL, [$platformId]), + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) + ]); - if ($platform->isEmpty()) { + if ($platform == false || $platform->isEmpty()) { throw new Exception('Platform not found', 404); } @@ -1282,12 +1314,17 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') throw new Exception('Project not found', 404); } - $deleteRecord = $dbForConsole->deleteDocument('projectsPlatforms', $platformId); + $platform = $dbForConsole->findOne('projectsPlatforms', [ + new Query('_uid', Query::TYPE_EQUAL, [$platformId]), + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) + ]); - if($deleteRecord == false) { + if ($platform == false || $platform->isEmpty()) { throw new Exception('Platform not found', 404); } + $dbForConsole->deleteDocument('projectsPlatforms', $platformId); + $dbForConsole->purgeDocument('projects', $project->getId()); $response->noContent(); @@ -1380,7 +1417,9 @@ App::get('/v1/projects/:projectId/domains') throw new Exception('Project not found', 404); } - $domains = $dbForConsole->find('projectDomains', [], 5000); + $domains = $dbForConsole->find('projectDomains', [ + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) + ], 5000); $response->dynamic(new Document([ 'domains' => $domains, @@ -1412,9 +1451,12 @@ App::get('/v1/projects/:projectId/domains/:domainId') throw new Exception('Project not found', 404); } - $domain = $dbForConsole->getDocument('projectDomains', $domainId); + $domain = $dbForConsole->findOne('projectDomains', [ + new Query('_uid', Query::TYPE_EQUAL, [$domainId]), + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) + ]); - if ($domain->isEmpty()) { + if ($domain == false || $domain->isEmpty()) { throw new Exception('Domain not found', 404); } @@ -1445,9 +1487,12 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') throw new Exception('Project not found', 404); } - $domain = $dbForConsole->getDocument('projectDomains', $domainId); + $domain = $dbForConsole->findOne('projectDomains', [ + new Query('_uid', Query::TYPE_EQUAL, [$domainId]), + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) + ]); - if ($domain->isEmpty()) { + if ($domain == false || $domain->isEmpty()) { throw new Exception('Domain not found', 404); } @@ -1505,9 +1550,12 @@ App::delete('/v1/projects/:projectId/domains/:domainId') throw new Exception('Project not found', 404); } - $domain = $dbForConsole->getDocument('projectDomains', $domainId); + $domain = $dbForConsole->findOne('projectDomains', [ + new Query('_uid', Query::TYPE_EQUAL, [$domainId]), + new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) + ]); - if ($domain->isEmpty()) { + if ($domain == false || $domain->isEmpty()) { throw new Exception('Domain not found', 404); } From 0ab76a1c6f0ae30a07c881a05245f8eeae3767c1 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Wed, 1 Sep 2021 10:43:35 +0200 Subject: [PATCH 065/130] Update sub-collection names and filter names, improved error log, fixed docs typo --- app/config/collections2.php | 48 +++++++++---------- app/controllers/api/projects.php | 74 +++++++++++++++--------------- app/init.php | 24 +++++----- src/Appwrite/Database/Database.php | 4 +- 4 files changed, 75 insertions(+), 75 deletions(-) diff --git a/app/config/collections2.php b/app/config/collections2.php index a29a9728c..df2f2dbab 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -478,7 +478,7 @@ $collections = [ 'required' => false, 'default' => null, 'array' => false, - 'filters' => ['subQueryProjectServices'], + 'filters' => ['subQueryServices'], ], [ '$id' => 'auths', @@ -500,7 +500,7 @@ $collections = [ 'required' => false, 'default' => null, 'array' => false, - 'filters' => ['subQueryProjectProviders'], + 'filters' => ['subQueryProviders'], ], [ '$id' => 'platforms', @@ -511,7 +511,7 @@ $collections = [ 'required' => false, 'default' => null, 'array' => false, - 'filters' => ['subQueryProjectPlatforms'], + 'filters' => ['subQueryPlatforms'], ], [ '$id' => 'webhooks', @@ -522,7 +522,7 @@ $collections = [ 'required' => false, 'default' => null, 'array' => false, - 'filters' => ['subQueryProjectWebhooks'], + 'filters' => ['subQueryWebhooks'], ], [ '$id' => 'keys', @@ -533,7 +533,7 @@ $collections = [ 'required' => false, 'default' => null, 'array' => false, - 'filters' => ['subQueryProjectKeys'], + 'filters' => ['subQueryKeys'], ], [ '$id' => 'domains', @@ -544,7 +544,7 @@ $collections = [ 'required' => false, 'default' => null, 'array' => false, - 'filters' => ['subQueryProjectDomains'], + 'filters' => ['subQueryDomains'], ], ], 'indexes' => [ @@ -558,10 +558,10 @@ $collections = [ ], ], - 'projectsPlatforms' => [ + 'platforms' => [ '$collection' => Database::METADATA, - '$id' => 'projectsPlatforms', - 'name' => 'projectsPlatforms', + '$id' => 'platforms', + 'name' => 'platforms', 'attributes' => [ [ '$id' => 'projectId', @@ -663,10 +663,10 @@ $collections = [ ], ], - 'projectsServices' => [ + 'services' => [ '$collection' => Database::METADATA, - '$id' => 'projectsServices', - 'name' => 'projectsServices', + '$id' => 'services', + 'name' => 'services', 'attributes' => [ [ '$id' => 'projectId', @@ -713,10 +713,10 @@ $collections = [ ], ], - 'projectProviders' => [ + 'providers' => [ '$collection' => Database::METADATA, - '$id' => 'projectProviders', - 'name' => 'projectProviders', + '$id' => 'providers', + 'name' => 'providers', 'attributes' => [ [ '$id' => 'projectId', @@ -774,10 +774,10 @@ $collections = [ ], ], - 'projectDomains' => [ + 'domains' => [ '$collection' => Database::METADATA, - '$id' => 'projectDomains', - 'name' => 'projectDomains', + '$id' => 'domains', + 'name' => 'domains', 'attributes' => [ [ '$id' => 'projectId', @@ -868,10 +868,10 @@ $collections = [ ], ], - 'projectKeys' => [ + 'keys' => [ '$collection' => Database::METADATA, - '$id' => 'projectKeys', - 'name' => 'projectKeys', + '$id' => 'keys', + 'name' => 'keys', 'attributes' => [ [ '$id' => 'projectId', @@ -929,10 +929,10 @@ $collections = [ ], ], - 'projectWebhooks' => [ + 'webhooks' => [ '$collection' => Database::METADATA, - '$id' => 'projectWebhooks', - 'name' => 'projectWebhooks', + '$id' => 'webhooks', + 'name' => 'webhooks', 'attributes' => [ [ '$id' => 'projectId', diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 97a914baf..42cb3ebc1 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -217,7 +217,7 @@ App::get('/v1/projects/:projectId') }); App::get('/v1/projects/:projectId/usage') - ->desc('Get Project') + ->desc('Get Project Usage') ->groups(['api', 'projects']) ->label('scope', 'projects.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -484,7 +484,7 @@ App::patch('/v1/projects/:projectId/service') throw new Exception('Project not found', 404); } - $document = $dbForConsole->findOne('projectsServices', [ + $document = $dbForConsole->findOne('services', [ new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), new Query('key', Query::TYPE_EQUAL, [$service]), ]); @@ -499,7 +499,7 @@ App::patch('/v1/projects/:projectId/service') 'status' => $status, ]); - $dbForConsole->createDocument('projectsServices', $document); + $dbForConsole->createDocument('services', $document); $project ->setAttribute('services', $document, Document::SET_TYPE_APPEND); @@ -508,7 +508,7 @@ App::patch('/v1/projects/:projectId/service') } else { if($document->getAttribute('status') != $status) { $document->setAttribute('status', $status); - $dbForConsole->updateDocument('projectsServices', $document->getId(), $document); + $dbForConsole->updateDocument('services', $document->getId(), $document); $project->findAndReplace('$id', $document->getId(), $document, 'services'); @@ -545,7 +545,7 @@ App::patch('/v1/projects/:projectId/oauth2') throw new Exception('Project not found', 404); } - $provider = $dbForConsole->findOne('projectProviders', [ + $provider = $dbForConsole->findOne('providers', [ new Query('key', Query::TYPE_EQUAL, [$providerKey]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), ]); @@ -556,7 +556,7 @@ App::patch('/v1/projects/:projectId/oauth2') $provider->setAttribute('appId', $appId) ->setAttribute('appSecret', $secret); - $dbForConsole->updateDocument('projectProviders', $provider->getId(), $provider); + $dbForConsole->updateDocument('providers', $provider->getId(), $provider); $project->findAndReplace('$id', $provider->getId(), $provider, 'providers'); @@ -574,7 +574,7 @@ App::patch('/v1/projects/:projectId/oauth2') 'appSecret' => $secret ]); - $dbForConsole->createDocument('projectProviders', $provider); + $dbForConsole->createDocument('providers', $provider); $project ->setAttribute('providers', $provider, Document::SET_TYPE_APPEND); @@ -748,7 +748,7 @@ App::post('/v1/projects/:projectId/webhooks') 'httpPass' => $httpPass, ]); - $webhook = $dbForConsole->createDocument('projectWebhooks', $webhook); + $webhook = $dbForConsole->createDocument('webhooks', $webhook); $dbForConsole->purgeDocument('projects', $project->getId()); @@ -779,7 +779,7 @@ App::get('/v1/projects/:projectId/webhooks') throw new Exception('Project not found', 404); } - $webhooks = $dbForConsole->find('projectWebhooks', [ + $webhooks = $dbForConsole->find('webhooks', [ new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); @@ -813,7 +813,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId') throw new Exception('Project not found', 404); } - $webhook = $dbForConsole->findOne('projectWebhooks', [ + $webhook = $dbForConsole->findOne('webhooks', [ new Query('_uid', Query::TYPE_EQUAL, [$webhookId]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); @@ -857,7 +857,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') $security = ($security === '1' || $security === 'true' || $security === 1 || $security === true); - $webhook = $dbForConsole->findOne('projectWebhooks', [ + $webhook = $dbForConsole->findOne('webhooks', [ new Query('_uid', Query::TYPE_EQUAL, [$webhookId]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); @@ -874,7 +874,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') ->setAttribute('httpUser', $httpUser) ->setAttribute('httpPass', $httpPass); - $dbForConsole->updateDocument('projectWebhooks', $webhook->getId(), $webhook); + $dbForConsole->updateDocument('webhooks', $webhook->getId(), $webhook); $dbForConsole->purgeDocument('projects', $project->getId()); @@ -904,7 +904,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') throw new Exception('Project not found', 404); } - $webhook = $dbForConsole->findOne('projectWebhooks', [ + $webhook = $dbForConsole->findOne('webhooks', [ new Query('_uid', Query::TYPE_EQUAL, [$webhookId]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); @@ -913,7 +913,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') throw new Exception('Webhook not found', 404); } - $dbForConsole->deleteDocument('projectWebhooks', $webhook->getId()); + $dbForConsole->deleteDocument('webhooks', $webhook->getId()); $dbForConsole->purgeDocument('projects', $project->getId()); @@ -957,7 +957,7 @@ App::post('/v1/projects/:projectId/keys') 'secret' => \bin2hex(\random_bytes(128)), ]); - $key = $dbForConsole->createDocument('projectKeys', $key); + $key = $dbForConsole->createDocument('keys', $key); $dbForConsole->purgeDocument('projects', $project->getId()); @@ -988,7 +988,7 @@ App::get('/v1/projects/:projectId/keys') throw new Exception('Project not found', 404); } - $keys = $dbForConsole->find('projectKeys', [ + $keys = $dbForConsole->find('keys', [ new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), ], 5000); @@ -1022,12 +1022,12 @@ App::get('/v1/projects/:projectId/keys/:keyId') throw new Exception('Project not found', 404); } - $key = $dbForConsole->findOne('projectKeys', [ + $key = $dbForConsole->findOne('keys', [ new Query('_uid', Query::TYPE_EQUAL, [$keyId]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); - if ($key !== true || $key->isEmpty()) { + if ($key == false || $key->isEmpty()) { throw new Exception('Key not found', 404); } @@ -1060,7 +1060,7 @@ App::put('/v1/projects/:projectId/keys/:keyId') throw new Exception('Project not found', 404); } - $key = $dbForConsole->findOne('projectKeys', [ + $key = $dbForConsole->findOne('keys', [ new Query('_uid', Query::TYPE_EQUAL, [$keyId]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); @@ -1072,7 +1072,7 @@ App::put('/v1/projects/:projectId/keys/:keyId') $key->setAttribute('name', $name) ->setAttribute('scopes', $scopes); - $dbForConsole->updateDocument('projectKeys', $key->getId(), $key); + $dbForConsole->updateDocument('keys', $key->getId(), $key); $dbForConsole->purgeDocument('projects', $project->getId()); @@ -1102,7 +1102,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId') throw new Exception('Project not found', 404); } - $key = $dbForConsole->findOne('projectKeys', [ + $key = $dbForConsole->findOne('keys', [ new Query('_uid', Query::TYPE_EQUAL, [$keyId]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); @@ -1111,7 +1111,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId') throw new Exception('Key not found', 404); } - $dbForConsole->deleteDocument('projectKeys', $key->getId()); + $dbForConsole->deleteDocument('keys', $key->getId()); $dbForConsole->purgeDocument('projects', $project->getId()); @@ -1162,7 +1162,7 @@ App::post('/v1/projects/:projectId/platforms') 'dateUpdated' => \time(), ]); - $platform = $dbForConsole->createDocument('projectsPlatforms', $platform); + $platform = $dbForConsole->createDocument('platforms', $platform); $dbForConsole->purgeDocument('projects', $project->getId()); @@ -1193,7 +1193,7 @@ App::get('/v1/projects/:projectId/platforms') throw new Exception('Project not found', 404); } - $platforms = $dbForConsole->find('projectsPlatforms', [ + $platforms = $dbForConsole->find('platforms', [ new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ], 5000); @@ -1227,7 +1227,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId') throw new Exception('Project not found', 404); } - $platform = $dbForConsole->findOne('projectsPlatforms', [ + $platform = $dbForConsole->findOne('platforms', [ new Query('_uid', Query::TYPE_EQUAL, [$platformId]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); @@ -1267,7 +1267,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') throw new Exception('Project not found', 404); } - $platform = $dbForConsole->findOne('projectsPlatforms', [ + $platform = $dbForConsole->findOne('platforms', [ new Query('_uid', Query::TYPE_EQUAL, [$platformId]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); @@ -1284,7 +1284,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') ->setAttribute('hostname', $hostname) ; - $dbForConsole->updateDocument('projectsPlatforms', $platform->getId(), $platform); + $dbForConsole->updateDocument('platforms', $platform->getId(), $platform); $dbForConsole->purgeDocument('projects', $project->getId()); @@ -1314,7 +1314,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') throw new Exception('Project not found', 404); } - $platform = $dbForConsole->findOne('projectsPlatforms', [ + $platform = $dbForConsole->findOne('platforms', [ new Query('_uid', Query::TYPE_EQUAL, [$platformId]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); @@ -1323,7 +1323,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') throw new Exception('Platform not found', 404); } - $dbForConsole->deleteDocument('projectsPlatforms', $platformId); + $dbForConsole->deleteDocument('platforms', $platformId); $dbForConsole->purgeDocument('projects', $project->getId()); @@ -1356,7 +1356,7 @@ App::post('/v1/projects/:projectId/domains') throw new Exception('Project not found', 404); } - $document = $dbForConsole->findOne('projectDomains', [ + $document = $dbForConsole->findOne('domains', [ new Query('domain', Query::TYPE_EQUAL, [$domain]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), ]); @@ -1386,7 +1386,7 @@ App::post('/v1/projects/:projectId/domains') 'certificateId' => null, ]); - $domain = $dbForConsole->createDocument('projectDomains', $domain); + $domain = $dbForConsole->createDocument('domains', $domain); $dbForConsole->purgeDocument('projects', $project->getId()); @@ -1417,7 +1417,7 @@ App::get('/v1/projects/:projectId/domains') throw new Exception('Project not found', 404); } - $domains = $dbForConsole->find('projectDomains', [ + $domains = $dbForConsole->find('domains', [ new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ], 5000); @@ -1451,7 +1451,7 @@ App::get('/v1/projects/:projectId/domains/:domainId') throw new Exception('Project not found', 404); } - $domain = $dbForConsole->findOne('projectDomains', [ + $domain = $dbForConsole->findOne('domains', [ new Query('_uid', Query::TYPE_EQUAL, [$domainId]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); @@ -1487,7 +1487,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') throw new Exception('Project not found', 404); } - $domain = $dbForConsole->findOne('projectDomains', [ + $domain = $dbForConsole->findOne('domains', [ new Query('_uid', Query::TYPE_EQUAL, [$domainId]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); @@ -1514,7 +1514,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') $domain->setAttribute('verification', true); - $dbForConsole->updateDocument('projectDomains', $domain->getId(), $domain); + $dbForConsole->updateDocument('domains', $domain->getId(), $domain); $dbForConsole->purgeDocument('projects', $project->getId()); // Issue a TLS certificate when domain is verified @@ -1550,7 +1550,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId') throw new Exception('Project not found', 404); } - $domain = $dbForConsole->findOne('projectDomains', [ + $domain = $dbForConsole->findOne('domains', [ new Query('_uid', Query::TYPE_EQUAL, [$domainId]), new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); @@ -1559,7 +1559,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId') throw new Exception('Domain not found', 404); } - $dbForConsole->deleteDocument('projectDomains', $domain->getId()); + $dbForConsole->deleteDocument('domains', $domain->getId()); $dbForConsole->purgeDocument('projects', $project->getId()); diff --git a/app/init.php b/app/init.php index adb42fbdd..e30b7f696 100644 --- a/app/init.php +++ b/app/init.php @@ -203,61 +203,61 @@ Database::addFilter('subQueryIndexes', } ); -Database::addFilter('subQueryProjectPlatforms', +Database::addFilter('subQueryPlatforms', function($value) { return null; }, function($value, Document $document, Database $database) { return $database - ->find('projectsPlatforms', [ + ->find('platforms', [ new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) ], 5000, 0, []); } ); -Database::addFilter('subQueryProjectDomains', +Database::addFilter('subQueryDomains', function($value) { return null; }, function($value, Document $document, Database $database) { return $database - ->find('projectDomains', [ + ->find('domains', [ new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) ], 5000, 0, []); } ); -Database::addFilter('subQueryProjectKeys', +Database::addFilter('subQueryKeys', function($value) { return null; }, function($value, Document $document, Database $database) { return $database - ->find('projectKeys', [ + ->find('keys', [ new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) ], 5000, 0, []); } ); -Database::addFilter('subQueryProjectWebhooks', +Database::addFilter('subQueryWebhooks', function($value) { return null; }, function($value, Document $document, Database $database) { return $database - ->find('projectWebhooks', [ + ->find('webhooks', [ new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) ], 5000, 0, []); } ); -Database::addFilter('subQueryProjectServices', +Database::addFilter('subQueryServices', function($value) { return null; }, function($value, Document $document, Database $database) { $services = $database - ->find('projectsServices', [ + ->find('services', [ new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) ], 5000, 0, []); @@ -270,13 +270,13 @@ Database::addFilter('subQueryProjectServices', } ); -Database::addFilter('subQueryProjectProviders', +Database::addFilter('subQueryProviders', function($value) { return null; }, function($value, Document $document, Database $database) { $providers = $database - ->find('projectProviders', [ + ->find('providers', [ new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) ], 5000, 0, []); diff --git a/src/Appwrite/Database/Database.php b/src/Appwrite/Database/Database.php index e6cfa001d..37978ab65 100644 --- a/src/Appwrite/Database/Database.php +++ b/src/Appwrite/Database/Database.php @@ -577,7 +577,7 @@ class Database { if (!isset(self::$filters[$name])) { return $value; - throw new Exception('Filter not found'); + throw new Exception("Filter '{$name}' not found"); } try { @@ -599,7 +599,7 @@ class Database { if (!isset(self::$filters[$name])) { return $value; - throw new Exception('Filter not found'); + throw new Exception("Filter '{$name}' not found"); } try { From be47d162907dffb4e486e9d56bb23bbfd0d30bd4 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 1 Sep 2021 10:08:59 -0400 Subject: [PATCH 066/130] Use MariaDB max limit for index/attribute subqueries --- app/init.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/init.php b/app/init.php index 57f2dc713..d68f9f485 100644 --- a/app/init.php +++ b/app/init.php @@ -184,7 +184,7 @@ Database::addFilter('subQueryAttributes', return $database ->find('attributes', [ new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()]) - ], 100, 0, []); + ], 1017, 0, []); } ); @@ -196,7 +196,7 @@ Database::addFilter('subQueryIndexes', return $database ->find('indexes', [ new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()]) - ], 100, 0, []); + ], 64, 0, []); } ); From 624d099bf9b71aabb598b658f60a807268e8dc78 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 1 Sep 2021 10:36:01 -0400 Subject: [PATCH 067/130] Assert row width exception is thrown --- tests/e2e/Services/Database/DatabaseCustomServerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Database/DatabaseCustomServerTest.php b/tests/e2e/Services/Database/DatabaseCustomServerTest.php index 2be62f8fa..6c0a50075 100644 --- a/tests/e2e/Services/Database/DatabaseCustomServerTest.php +++ b/tests/e2e/Services/Database/DatabaseCustomServerTest.php @@ -398,8 +398,8 @@ class DatabaseCustomServerTest extends Scope 'required' => true, ]); - // $this->assertEquals(400, $tooWide['headers']['status-code']); - // $this->assertEquals('Attribute limit exceeded', $tooWide['body']['message']); + $this->assertEquals(400, $tooWide['headers']['status-code']); + $this->assertEquals('Attribute limit exceeded', $tooWide['body']['message']); } public function testIndexLimitException() From 6b7059c034a8096e6067e71ec5fc10f038bf094e Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 1 Sep 2021 10:57:20 -0400 Subject: [PATCH 068/130] Fix missing lines from conflict resolution --- tests/e2e/Services/Database/DatabaseBase.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index ba0da3d2e..96339b1fd 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -1159,6 +1159,9 @@ trait DatabaseBase sleep(2); $document1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ 'documentId' => 'unique()', 'data' => [ 'attribute' => 'one', From 524abc80d70fbec15820b92763247c6cf0097499 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Thu, 2 Sep 2021 11:38:24 +0200 Subject: [PATCH 069/130] Rollback of services and providers sub-collections --- app/config/collections2.php | 4 +- app/controllers/api/projects.php | 71 ++++---------------------------- app/init.php | 39 ------------------ 3 files changed, 11 insertions(+), 103 deletions(-) diff --git a/app/config/collections2.php b/app/config/collections2.php index df2f2dbab..29375bec5 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -478,7 +478,7 @@ $collections = [ 'required' => false, 'default' => null, 'array' => false, - 'filters' => ['subQueryServices'], + 'filters' => ['json'], ], [ '$id' => 'auths', @@ -500,7 +500,7 @@ $collections = [ 'required' => false, 'default' => null, 'array' => false, - 'filters' => ['subQueryProviders'], + 'filters' => ['json'], ], [ '$id' => 'platforms', diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 42cb3ebc1..973df927d 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -93,9 +93,9 @@ App::post('/v1/projects') 'legalCity' => $legalCity, 'legalAddress' => $legalAddress, 'legalTaxId' => $legalTaxId, - 'services' => null, + 'services' => new stdClass(), 'platforms' => null, - 'providers' => null, + 'providers' => [], 'webhooks' => null, 'keys' => null, 'domains' => null, @@ -484,37 +484,10 @@ App::patch('/v1/projects/:projectId/service') throw new Exception('Project not found', 404); } - $document = $dbForConsole->findOne('services', [ - new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), - new Query('key', Query::TYPE_EQUAL, [$service]), - ]); + $services = $project->getAttribute('services', []); + $services[$service] = $status; - if($document == false || $document->isEmpty()) { - $document = new Document([ - '$id' => $dbForConsole->getId(), - '$read' => ['role:all'], - '$write' => ['role:all'], - 'projectId' => $project->getId(), - 'key' => $service, - 'status' => $status, - ]); - - $dbForConsole->createDocument('services', $document); - - $project - ->setAttribute('services', $document, Document::SET_TYPE_APPEND); - - $dbForConsole->purgeDocument('projects', $project->getId()); - } else { - if($document->getAttribute('status') != $status) { - $document->setAttribute('status', $status); - $dbForConsole->updateDocument('services', $document->getId(), $document); - - $project->findAndReplace('$id', $document->getId(), $document, 'services'); - - $dbForConsole->purgeDocument('projects', $project->getId()); - } - } + $project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('services', $services)); $response->dynamic($project, Response::MODEL_PROJECT); }); @@ -550,37 +523,11 @@ App::patch('/v1/projects/:projectId/oauth2') new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), ]); - if($provider && !$provider->isEmpty()) { - // Provider exists + $providers = $project->getAttribute('providers', []); + $providers[$provider . 'Appid'] = $appId; + $providers[$provider . 'Secret'] = $secret; - $provider->setAttribute('appId', $appId) - ->setAttribute('appSecret', $secret); - - $dbForConsole->updateDocument('providers', $provider->getId(), $provider); - - $project->findAndReplace('$id', $provider->getId(), $provider, 'providers'); - - $dbForConsole->purgeDocument('projects', $project->getId()); - } else { - // Provider does not exist yet - - $provider = new Document([ - '$id' => $dbForConsole->getId(), - '$read' => ['role:all'], - '$write' => ['role:all'], - 'projectId' => $project->getId(), - 'key' => $providerKey, - 'appId' => $appId, - 'appSecret' => $secret - ]); - - $dbForConsole->createDocument('providers', $provider); - - $project - ->setAttribute('providers', $provider, Document::SET_TYPE_APPEND); - - $dbForConsole->purgeDocument('projects', $project->getId()); - } + $project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('providers', $providers)); $response->dynamic($project, Response::MODEL_PROJECT); }); diff --git a/app/init.php b/app/init.php index e30b7f696..86c69ae59 100644 --- a/app/init.php +++ b/app/init.php @@ -251,45 +251,6 @@ Database::addFilter('subQueryWebhooks', } ); -Database::addFilter('subQueryServices', - function($value) { - return null; - }, - function($value, Document $document, Database $database) { - $services = $database - ->find('services', [ - new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) - ], 5000, 0, []); - - $responseJson = []; - foreach($services as $service) { - $responseJson[$service->getAttribute('key')] = $service->getAttribute('status', true); - } - - return $responseJson; - } -); - -Database::addFilter('subQueryProviders', - function($value) { - return null; - }, - function($value, Document $document, Database $database) { - $providers = $database - ->find('providers', [ - new Query('projectId', Query::TYPE_EQUAL, [$document->getId()]) - ], 5000, 0, []); - - $responseJson = []; - foreach($providers as $provider) { - $responseJson[$provider->getAttribute('key') . 'Appid'] = $provider->getAttribute('appId'); - $responseJson[$provider->getAttribute('key') . 'Secret'] = $provider->getAttribute('appSecret'); - } - - return $responseJson; - } -); - Database::addFilter('encrypt', function($value) { $key = App::getEnv('_APP_OPENSSL_KEY_V1'); From 0fa6c764132cb4efc34aaa128818436a62448958 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Thu, 2 Sep 2021 13:12:10 +0200 Subject: [PATCH 070/130] Bug fix, tests are now green --- app/controllers/api/projects.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 973df927d..0c8d254c2 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -508,7 +508,7 @@ App::patch('/v1/projects/:projectId/oauth2') ->param('secret', '', new text(512), 'Provider secret key. Max length: 512 chars.', true) ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $providerKey, $appId, $secret, $response, $dbForConsole) { + ->action(function ($projectId, $provider, $appId, $secret, $response, $dbForConsole) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForConsole */ @@ -518,11 +518,6 @@ App::patch('/v1/projects/:projectId/oauth2') throw new Exception('Project not found', 404); } - $provider = $dbForConsole->findOne('providers', [ - new Query('key', Query::TYPE_EQUAL, [$providerKey]), - new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]), - ]); - $providers = $project->getAttribute('providers', []); $providers[$provider . 'Appid'] = $appId; $providers[$provider . 'Secret'] = $secret; From 31977e462c27d1788de0714e20751701c418646b Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Thu, 2 Sep 2021 17:38:33 -0400 Subject: [PATCH 071/130] Clean up indexes when attribute is deleted --- app/workers/database.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/workers/database.php b/app/workers/database.php index fd3824615..ecd617cc5 100644 --- a/app/workers/database.php +++ b/app/workers/database.php @@ -120,6 +120,28 @@ class DatabaseV1 extends Worker $dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed')); } + // the underlying database removes/rebuilds indexes when attribute is removed + // update indexes table with changes + /** @var Document[] $indexes */ + $indexes = $collection->getAttribute('indexes', []); + + foreach ($indexes as $index) { + /** @var string[] $attributes */ + $attributes = $index->getAttribute('attributes'); + $found = array_search($key, $attributes); + + if ($found !== false) { + $remove = [$attributes[$found]]; + $attributes = array_diff($attributes, $remove); // remove attribute from array + + if (empty($attributes)) { + $dbForInternal->deleteDocument('indexes', $index->getId()); + } else { + $dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN)); + } + } + } + $dbForInternal->purgeDocument('collections', $collectionId); } From 1410101db80cd4219ccf5f2c6d42edbf46202570 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Thu, 2 Sep 2021 17:38:46 -0400 Subject: [PATCH 072/130] Test index cleanup when attribute is deleted --- .../Database/DatabaseCustomServerTest.php | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/tests/e2e/Services/Database/DatabaseCustomServerTest.php b/tests/e2e/Services/Database/DatabaseCustomServerTest.php index e5a75fa68..b642a5767 100644 --- a/tests/e2e/Services/Database/DatabaseCustomServerTest.php +++ b/tests/e2e/Services/Database/DatabaseCustomServerTest.php @@ -241,6 +241,103 @@ class DatabaseCustomServerTest extends Scope /** * @depends testDeleteIndex */ + public function testDeleteIndexOnDeleteAttribute($data) + { + $attribute1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'attribute1', + 'size' => 16, + 'required' => true, + ]); + + $attribute2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'attribute2', + 'size' => 16, + 'required' => true, + ]); + + $this->assertEquals(201, $attribute1['headers']['status-code']); + $this->assertEquals(201, $attribute2['headers']['status-code']); + $this->assertEquals('attribute1', $attribute1['body']['key']); + $this->assertEquals('attribute2', $attribute2['body']['key']); + + sleep(2); + + $index1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'indexId' => 'index1', + 'type' => 'key', + 'attributes' => ['attribute1', 'attribute2'], + 'orders' => ['ASC', 'ASC'], + ]); + + $index2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'indexId' => 'index2', + 'type' => 'key', + 'attributes' => ['attribute2'], + ]); + + $this->assertEquals(201, $index1['headers']['status-code']); + $this->assertEquals(201, $index2['headers']['status-code']); + $this->assertEquals('index1', $index1['body']['key']); + $this->assertEquals('index2', $index2['body']['key']); + + sleep(2); + + $deleted = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/attributes/'. $attribute2['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals($deleted['headers']['status-code'], 204); + + // wait for database worker to complete + sleep(2); + + $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['collectionId'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $collection['headers']['status-code']); + $this->assertIsArray($collection['body']['indexes']); + $this->assertCount(1, $collection['body']['indexes']); + $this->assertEquals($index1['body']['key'], $collection['body']['indexes'][0]['key']); + $this->assertIsArray($collection['body']['indexes'][0]['attributes']); + $this->assertCount(1, $collection['body']['indexes'][0]['attributes']); + $this->assertEquals($attribute1['body']['key'], $collection['body']['indexes'][0]['attributes'][0]); + + // Delete attribute + $deleted = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/attributes/' . $attribute1['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals($deleted['headers']['status-code'], 204); + + return $data; + } + + /** + * @depends testDeleteIndexOnDeleteAttribute + */ public function testDeleteCollection($data) { $collectionId = $data['collectionId']; From e1c7e4908b60a93daf8d6f9e8046d92ef640968b Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Thu, 9 Sep 2021 12:52:56 -0400 Subject: [PATCH 073/130] Remove unneeded var --- app/controllers/api/database.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 1d2b7c997..633e6f232 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -1230,7 +1230,6 @@ App::get('/v1/database/collections/:collectionId/documents') throw new Exception($validator->getDescription(), 400); } - $afterDocument = null; if (!empty($after)) { $afterDocument = $dbForExternal->getDocument($collectionId, $after); From f5d69b4b0aaaba03480323d798097412179f2811 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Thu, 9 Sep 2021 13:15:30 -0400 Subject: [PATCH 074/130] Catch exceptions in one block --- app/controllers/api/database.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 7cda57662..5560d931d 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -88,16 +88,13 @@ $attributesCallback = function ($collectionId, $attribute, $response, $dbForInte try { $dbForInternal->checkAttribute($collection, $attribute); - } catch (LimitException $exception) { - throw new Exception('Attribute limit exceeded', 400); - } - - try { $attribute = $dbForInternal->createDocument('attributes', $attribute); - } catch (DuplicateException $th) { + } + catch (DuplicateException $exception) { throw new Exception('Attribute already exists', 409); - } catch (LimitException $e) { - throw new Exception($e->getMessage(), 400); + } + catch (LimitException $exception) { + throw new Exception('Attribute limit exceeded', 400); } $dbForInternal->purgeDocument('collections', $collectionId); From e9124f28c722603102d1dd73d0987966e73a1a3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sat, 11 Sep 2021 19:29:17 +0200 Subject: [PATCH 075/130] Removed unnecessary array_merge import Co-authored-by: Eldad A. Fux --- tests/e2e/Services/Projects/ProjectsConsoleClientTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index f090158cd..2f91663c9 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -7,7 +7,6 @@ use Tests\E2E\Scopes\ProjectConsole; use Tests\E2E\Scopes\SideClient; use Tests\E2E\Services\Projects\ProjectsBase; use Tests\E2E\Client; -use function array_merge; class ProjectsConsoleClientTest extends Scope { From d3e02db31f670aef41ac8e16ad66a023cdf69b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 14 Sep 2021 08:57:55 +0200 Subject: [PATCH 076/130] Apply suggestions from code review Co-authored-by: kodumbeats --- app/controllers/api/projects.php | 36 +++++++++++++++++--------------- app/init.php | 4 ++-- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 0c8d254c2..f72637192 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -760,7 +760,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId') new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); - if ($webhook == false || $webhook->isEmpty()) { + if ($webhook === false || $webhook->isEmpty()) { throw new Exception('Webhook not found', 404); } @@ -804,7 +804,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); - if ($webhook == false || $webhook->isEmpty()) { + if ($webhook === false || $webhook->isEmpty()) { throw new Exception('Webhook not found', 404); } @@ -814,7 +814,8 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') ->setAttribute('url', $url) ->setAttribute('security', $security) ->setAttribute('httpUser', $httpUser) - ->setAttribute('httpPass', $httpPass); + ->setAttribute('httpPass', $httpPass) + ; $dbForConsole->updateDocument('webhooks', $webhook->getId(), $webhook); @@ -851,7 +852,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); - if($webhook == false || $webhook->isEmpty()) { + if($webhook === false || $webhook->isEmpty()) { throw new Exception('Webhook not found', 404); } @@ -969,7 +970,7 @@ App::get('/v1/projects/:projectId/keys/:keyId') new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); - if ($key == false || $key->isEmpty()) { + if ($key === false || $key->isEmpty()) { throw new Exception('Key not found', 404); } @@ -1007,12 +1008,14 @@ App::put('/v1/projects/:projectId/keys/:keyId') new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); - if ($key == false || $key->isEmpty()) { + if ($key === false || $key->isEmpty()) { throw new Exception('Key not found', 404); } - $key->setAttribute('name', $name) - ->setAttribute('scopes', $scopes); + $key + ->setAttribute('name', $name) + ->setAttribute('scopes', $scopes) + ; $dbForConsole->updateDocument('keys', $key->getId(), $key); @@ -1049,7 +1052,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId') new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); - if($key == false || $key->isEmpty()) { + if($key === false || $key->isEmpty()) { throw new Exception('Key not found', 404); } @@ -1174,7 +1177,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId') new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); - if ($platform == false || $platform->isEmpty()) { + if ($platform === false || $platform->isEmpty()) { throw new Exception('Platform not found', 404); } @@ -1214,7 +1217,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); - if ($platform == false || $platform->isEmpty()) { + if ($platform === false || $platform->isEmpty()) { throw new Exception('Platform not found', 404); } @@ -1261,7 +1264,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); - if ($platform == false || $platform->isEmpty()) { + if ($platform === false || $platform->isEmpty()) { throw new Exception('Platform not found', 404); } @@ -1398,7 +1401,7 @@ App::get('/v1/projects/:projectId/domains/:domainId') new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); - if ($domain == false || $domain->isEmpty()) { + if ($domain === false || $domain->isEmpty()) { throw new Exception('Domain not found', 404); } @@ -1434,7 +1437,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); - if ($domain == false || $domain->isEmpty()) { + if ($domain === false || $domain->isEmpty()) { throw new Exception('Domain not found', 404); } @@ -1454,9 +1457,8 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') throw new Exception('Failed to verify domain', 401); } - $domain->setAttribute('verification', true); - $dbForConsole->updateDocument('domains', $domain->getId(), $domain); + $dbForConsole->updateDocument('domains', $domain->getId(), $domain->setAttribute('verification', true)); $dbForConsole->purgeDocument('projects', $project->getId()); // Issue a TLS certificate when domain is verified @@ -1497,7 +1499,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId') new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]) ]); - if ($domain == false || $domain->isEmpty()) { + if ($domain === false || $domain->isEmpty()) { throw new Exception('Domain not found', 404); } diff --git a/app/init.php b/app/init.php index 86c69ae59..7892ac8a1 100644 --- a/app/init.php +++ b/app/init.php @@ -187,7 +187,7 @@ Database::addFilter('subQueryAttributes', return $database ->find('attributes', [ new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()]) - ], 5000, 0, []); + ], 1017, 0, []); } ); @@ -199,7 +199,7 @@ Database::addFilter('subQueryIndexes', return $database ->find('indexes', [ new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()]) - ], 5000, 0, []); + ], 64, 0, []); } ); From ba6c52861711e98ad84ac9a2ee70a11fbdfb5785 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Tue, 14 Sep 2021 08:58:19 +0200 Subject: [PATCH 077/130] Apply suggestions from code review 2 --- app/config/collections2.php | 111 ------------------------------- app/controllers/api/projects.php | 2 +- 2 files changed, 1 insertion(+), 112 deletions(-) diff --git a/app/config/collections2.php b/app/config/collections2.php index 29375bec5..97d6db253 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -663,117 +663,6 @@ $collections = [ ], ], - 'services' => [ - '$collection' => Database::METADATA, - '$id' => 'services', - 'name' => 'services', - 'attributes' => [ - [ - '$id' => 'projectId', - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => 'key', - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => 'status', - 'type' => Database::VAR_BOOLEAN, - 'format' => '', - 'size' => 0, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ] - ], - 'indexes' => [ - [ - '$id' => '_key_project', - 'type' => Database::INDEX_KEY, - 'attributes' => ['projectId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - ], - ], - - 'providers' => [ - '$collection' => Database::METADATA, - '$id' => 'providers', - 'name' => 'providers', - 'attributes' => [ - [ - '$id' => 'projectId', - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => 'key', - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => 'appId', - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - [ - '$id' => 'appSecret', - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], - ], - 'indexes' => [ - [ - '$id' => '_key_project', - 'type' => Database::INDEX_KEY, - 'attributes' => ['projectId'], - 'lengths' => [Database::LENGTH_KEY], - 'orders' => [Database::ORDER_ASC], - ], - ], - ], - 'domains' => [ '$collection' => Database::METADATA, '$id' => 'domains', diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index f72637192..8e99f200a 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -470,7 +470,7 @@ App::patch('/v1/projects/:projectId/service') ->label('sdk.response.model', Response::MODEL_PROJECT) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('service', '', new WhiteList(array_keys(array_filter(Config::getParam('services'), function($element) {return $element['optional'];})), true), 'Service name.') - ->param('status', true, new Boolean(), 'Service status.') + ->param('status', null, new Boolean(), 'Service status.') ->inject('response') ->inject('dbForConsole') ->action(function ($projectId, $service, $status, $response, $dbForConsole) { From 0992576e525394c6dbbf5f8b22eaa381cb0bb756 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 14 Sep 2021 10:26:16 +0200 Subject: [PATCH 078/130] introduce response type array --- src/Appwrite/Utopia/Response.php | 21 +++++++++++++-- src/Appwrite/Utopia/Response/Model.php | 7 ++--- .../Utopia/Response/Model/Attribute.php | 2 ++ .../Response/Model/AttributeBoolean.php | 4 +++ .../Utopia/Response/Model/AttributeEmail.php | 5 ++++ .../Utopia/Response/Model/AttributeFloat.php | 4 +++ .../Utopia/Response/Model/AttributeIP.php | 5 ++++ .../Response/Model/AttributeInteger.php | 4 +++ .../Utopia/Response/Model/AttributeList.php | 26 +++++++------------ .../Utopia/Response/Model/AttributeString.php | 4 +++ .../Utopia/Response/Model/AttributeURL.php | 5 ++++ .../Utopia/Response/Model/Collection.php | 24 +++++++---------- 12 files changed, 73 insertions(+), 38 deletions(-) diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index a653ddbb0..00e4194c8 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -341,7 +341,24 @@ class Response extends SwooleResponse foreach ($data[$key] as &$item) { if ($item instanceof Document) { - $ruleType = (!\is_null($rule['getNestedType'])) ? $rule['getNestedType']($item) : $rule['type']; + if (\is_array($rule['type'])) { + foreach ($rule['type'] as $type) { + $condition = false; + foreach ($this->getModel($type)->conditions as $attribute => $val) { + $condition = $item->getAttribute($attribute) === $val; + if(!$condition) { + break; + } + } + if ($condition) { + $ruleType = $type; + break; + } + } + } else { + $ruleType = $rule['type']; + } + if (!array_key_exists($ruleType, $this->models)) { throw new Exception('Missing model for rule: '. $ruleType); } @@ -350,7 +367,7 @@ class Response extends SwooleResponse } } } - + $output[$key] = $data[$key]; } diff --git a/src/Appwrite/Utopia/Response/Model.php b/src/Appwrite/Utopia/Response/Model.php index a763692c7..4c3143ca9 100644 --- a/src/Appwrite/Utopia/Response/Model.php +++ b/src/Appwrite/Utopia/Response/Model.php @@ -69,13 +69,11 @@ abstract class Model /** * Add a New Rule * If rule is an array of documents with varying models - * Pass callable $getNestedType that accepts Document and returns the nested response type * * @param string $key * @param array $options - * @param callable $getNestedType function(Document $value): string */ - protected function addRule(string $key, array $options, callable $getNestedType = null): self + protected function addRule(string $key, array $options): self { $this->rules[$key] = array_merge([ 'require' => true, @@ -83,8 +81,7 @@ abstract class Model 'description' => '', 'default' => null, 'example' => '', - 'array' => false, - 'getNestedType' => $getNestedType + 'array' => false ], $options); return $this; diff --git a/src/Appwrite/Utopia/Response/Model/Attribute.php b/src/Appwrite/Utopia/Response/Model/Attribute.php index 2c9dbf7c2..281a37a73 100644 --- a/src/Appwrite/Utopia/Response/Model/Attribute.php +++ b/src/Appwrite/Utopia/Response/Model/Attribute.php @@ -44,6 +44,8 @@ class Attribute extends Model ; } + public array $conditions = []; + /** * Get Name * diff --git a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php index 4076bc9eb..66aab770d 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php @@ -23,6 +23,10 @@ class AttributeBoolean extends Attribute ; } + public array $conditions = [ + 'type' => self::TYPE_BOOLEAN + ]; + /** * Get Name * diff --git a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php index 15bc4674e..a048e2c41 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php @@ -31,6 +31,11 @@ class AttributeEmail extends Attribute ; } + public array $conditions = [ + 'type' => self::TYPE_STRING, + 'format' => \APP_DATABASE_ATTRIBUTE_EMAIL + ]; + /** * Get Name * diff --git a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php index 7c4ead9e7..56ed9c68d 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php @@ -39,6 +39,10 @@ class AttributeFloat extends Attribute ; } + public array $conditions = [ + 'type' => self::TYPE_FLOAT, + ]; + /** * Get Name * diff --git a/src/Appwrite/Utopia/Response/Model/AttributeIP.php b/src/Appwrite/Utopia/Response/Model/AttributeIP.php index ec3a9cfed..6e3a8d7ba 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeIP.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeIP.php @@ -31,6 +31,11 @@ class AttributeIP extends Attribute ; } + public array $conditions = [ + 'type' => self::TYPE_STRING, + 'format' => \APP_DATABASE_ATTRIBUTE_IP + ]; + /** * Get Name * diff --git a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php index f11344d79..b2e86c79b 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php @@ -39,6 +39,10 @@ class AttributeInteger extends Attribute ; } + public array $conditions = [ + 'type' => self::TYPE_INTEGER, + ]; + /** * Get Name * * @return string diff --git a/src/Appwrite/Utopia/Response/Model/AttributeList.php b/src/Appwrite/Utopia/Response/Model/AttributeList.php index 26ea146ec..cad333bdf 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeList.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeList.php @@ -18,24 +18,18 @@ class AttributeList extends Model 'example' => 5, ]) ->addRule('attributes', [ - 'type' => Response::MODEL_ATTRIBUTE, + 'type' => [ + Response::MODEL_ATTRIBUTE_BOOLEAN, + Response::MODEL_ATTRIBUTE_INTEGER, + Response::MODEL_ATTRIBUTE_FLOAT, + Response::MODEL_ATTRIBUTE_EMAIL, + Response::MODEL_ATTRIBUTE_URL, + Response::MODEL_ATTRIBUTE_IP, + Response::MODEL_ATTRIBUTE_STRING // needs to be last, since its condition would dominate any other string attribute + ], 'description' => 'List of attributes.', 'default' => [], - 'array' => true, - 'getNestedType' => function(Document $attribute) { - return match($attribute->getAttribute('type')) { - self::TYPE_BOOLEAN => Response::MODEL_ATTRIBUTE_BOOLEAN, - self::TYPE_INTEGER => Response::MODEL_ATTRIBUTE_INTEGER, - self::TYPE_FLOAT => Response::MODEL_ATTRIBUTE_FLOAT, - self::TYPE_STRING => match($attribute->getAttribute('format')) { - APP_DATABASE_ATTRIBUTE_EMAIL => Response::MODEL_ATTRIBUTE_EMAIL, - APP_DATABASE_ATTRIBUTE_IP => Response::MODEL_ATTRIBUTE_IP, - APP_DATABASE_ATTRIBUTE_URL => Response::MODEL_ATTRIBUTE_URL, - default => Response::MODEL_ATTRIBUTE_STRING, - }, - default => Response::MODEL_ATTRIBUTE, - }; - }, + 'array' => true ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeString.php b/src/Appwrite/Utopia/Response/Model/AttributeString.php index 22f7e52a3..9feaf6b7b 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeString.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeString.php @@ -29,6 +29,10 @@ class AttributeString extends Attribute ; } + public array $conditions = [ + 'type' => self::TYPE_STRING, + ]; + /** * Get Name * diff --git a/src/Appwrite/Utopia/Response/Model/AttributeURL.php b/src/Appwrite/Utopia/Response/Model/AttributeURL.php index 3caddbc10..476d9bba0 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeURL.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeURL.php @@ -31,6 +31,11 @@ class AttributeURL extends Attribute ; } + public array $conditions = [ + 'type' => self::TYPE_STRING, + 'format' => \APP_DATABASE_ATTRIBUTE_URL + ]; + /** * Get Name * diff --git a/src/Appwrite/Utopia/Response/Model/Collection.php b/src/Appwrite/Utopia/Response/Model/Collection.php index f023be261..e52539691 100644 --- a/src/Appwrite/Utopia/Response/Model/Collection.php +++ b/src/Appwrite/Utopia/Response/Model/Collection.php @@ -45,25 +45,19 @@ class Collection extends Model 'example' => 'document', ]) ->addRule('attributes', [ - 'type' => Response::MODEL_ATTRIBUTE, + 'type' => [ + Response::MODEL_ATTRIBUTE_BOOLEAN, + Response::MODEL_ATTRIBUTE_INTEGER, + Response::MODEL_ATTRIBUTE_FLOAT, + Response::MODEL_ATTRIBUTE_EMAIL, + Response::MODEL_ATTRIBUTE_URL, + Response::MODEL_ATTRIBUTE_IP, + Response::MODEL_ATTRIBUTE_STRING, // needs to be last, since its condition would dominate any other string attribute + ], 'description' => 'Collection attributes.', 'default' => [], 'example' => new stdClass, 'array' => true, - 'getNestedType' => function(Document $attribute) { - return match($attribute->getAttribute('type')) { - self::TYPE_BOOLEAN => Response::MODEL_ATTRIBUTE_BOOLEAN, - self::TYPE_INTEGER => Response::MODEL_ATTRIBUTE_INTEGER, - self::TYPE_FLOAT => Response::MODEL_ATTRIBUTE_FLOAT, - self::TYPE_STRING => match($attribute->getAttribute('format')) { - APP_DATABASE_ATTRIBUTE_EMAIL => Response::MODEL_ATTRIBUTE_EMAIL, - APP_DATABASE_ATTRIBUTE_IP => Response::MODEL_ATTRIBUTE_IP, - APP_DATABASE_ATTRIBUTE_URL => Response::MODEL_ATTRIBUTE_URL, - default => Response::MODEL_ATTRIBUTE_STRING, - }, - default => Response::MODEL_ATTRIBUTE, - }; - }, ]) ->addRule('indexes', [ 'type' => Response::MODEL_INDEX, From 4cf16c88edf04ae97724762bdafdfb56f2075499 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 15 Sep 2021 11:17:09 -0400 Subject: [PATCH 079/130] Update docblocks for getType --- src/Appwrite/Utopia/Response/Model/AttributeBoolean.php | 2 +- src/Appwrite/Utopia/Response/Model/AttributeEmail.php | 2 +- src/Appwrite/Utopia/Response/Model/AttributeFloat.php | 2 +- src/Appwrite/Utopia/Response/Model/AttributeIP.php | 2 +- src/Appwrite/Utopia/Response/Model/AttributeInteger.php | 2 +- src/Appwrite/Utopia/Response/Model/AttributeList.php | 2 +- src/Appwrite/Utopia/Response/Model/AttributeString.php | 2 +- src/Appwrite/Utopia/Response/Model/AttributeURL.php | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php index 66aab770d..de7ae5813 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php @@ -38,7 +38,7 @@ class AttributeBoolean extends Attribute } /** - * Get Collection + * Get Type * * @return string */ diff --git a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php index a048e2c41..268412a55 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php @@ -47,7 +47,7 @@ class AttributeEmail extends Attribute } /** - * Get Collection + * Get Type * * @return string */ diff --git a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php index 56ed9c68d..93033f5b0 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php @@ -54,7 +54,7 @@ class AttributeFloat extends Attribute } /** - * Get Collection + * Get Type * * @return string */ diff --git a/src/Appwrite/Utopia/Response/Model/AttributeIP.php b/src/Appwrite/Utopia/Response/Model/AttributeIP.php index 6e3a8d7ba..2b8f64ded 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeIP.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeIP.php @@ -47,7 +47,7 @@ class AttributeIP extends Attribute } /** - * Get Collection + * Get Type * * @return string */ diff --git a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php index b2e86c79b..9ff94c8f5 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php @@ -53,7 +53,7 @@ class AttributeInteger extends Attribute } /** - * Get Collection + * Get Type * * @return string */ diff --git a/src/Appwrite/Utopia/Response/Model/AttributeList.php b/src/Appwrite/Utopia/Response/Model/AttributeList.php index cad333bdf..0cf67b3bd 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeList.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeList.php @@ -45,7 +45,7 @@ class AttributeList extends Model } /** - * Get Collection + * Get Type * * @return string */ diff --git a/src/Appwrite/Utopia/Response/Model/AttributeString.php b/src/Appwrite/Utopia/Response/Model/AttributeString.php index 9feaf6b7b..e7ea85326 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeString.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeString.php @@ -44,7 +44,7 @@ class AttributeString extends Attribute } /** - * Get Collection + * Get Type * * @return string */ diff --git a/src/Appwrite/Utopia/Response/Model/AttributeURL.php b/src/Appwrite/Utopia/Response/Model/AttributeURL.php index 476d9bba0..489e03885 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeURL.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeURL.php @@ -47,7 +47,7 @@ class AttributeURL extends Attribute } /** - * Get Collection + * Get Type * * @return string */ From 38a6e147c2cc05438074cff84aa92b6b7ab461f3 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Thu, 16 Sep 2021 21:52:35 -0400 Subject: [PATCH 080/130] Use limits defined by database library --- app/init.php | 4 ++-- composer.json | 2 +- composer.lock | 50 +++++++++++++++++++++++++------------------------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/app/init.php b/app/init.php index d68f9f485..c5a15cc73 100644 --- a/app/init.php +++ b/app/init.php @@ -184,7 +184,7 @@ Database::addFilter('subQueryAttributes', return $database ->find('attributes', [ new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()]) - ], 1017, 0, []); + ], $database->getAttributeLimit(), 0, []); } ); @@ -196,7 +196,7 @@ Database::addFilter('subQueryIndexes', return $database ->find('indexes', [ new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()]) - ], 64, 0, []); + ], $database->getIndexLimit(), 0, []); } ); diff --git a/composer.json b/composer.json index 9bd152fb9..c51501f71 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "utopia-php/cache": "0.4.*", "utopia-php/cli": "0.11.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "dev-feat-check-attribute-method as 0.10.0", + "utopia-php/database": "dev-feat-get-limit-methods-on-database as 0.10.0", "utopia-php/locale": "0.4.*", "utopia-php/registry": "0.5.*", "utopia-php/preloader": "0.2.*", diff --git a/composer.lock b/composer.lock index 829e34820..b6418d148 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9a7739aabd503572b8010be91192b144", + "content-hash": "ffd14884e3b377752bdb699c5693cb0d", "packages": [ { "name": "adhocore/jwt", @@ -248,16 +248,16 @@ }, { "name": "chillerlan/php-settings-container", - "version": "2.1.1", + "version": "2.1.2", "source": { "type": "git", "url": "https://github.com/chillerlan/php-settings-container.git", - "reference": "98ccc1b31b31a53bcb563465c4961879b2b93096" + "reference": "ec834493a88682dd69652a1eeaf462789ed0c5f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/98ccc1b31b31a53bcb563465c4961879b2b93096", - "reference": "98ccc1b31b31a53bcb563465c4961879b2b93096", + "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/ec834493a88682dd69652a1eeaf462789ed0c5f5", + "reference": "ec834493a88682dd69652a1eeaf462789ed0c5f5", "shasum": "" }, "require": { @@ -307,7 +307,7 @@ "type": "ko_fi" } ], - "time": "2021-01-06T15:57:03+00:00" + "time": "2021-09-06T15:17:01+00:00" }, { "name": "colinmollenhour/credis", @@ -355,16 +355,16 @@ }, { "name": "composer/package-versions-deprecated", - "version": "1.11.99.3", + "version": "1.11.99.4", "source": { "type": "git", "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "fff576ac850c045158a250e7e27666e146e78d18" + "reference": "b174585d1fe49ceed21928a945138948cb394600" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/fff576ac850c045158a250e7e27666e146e78d18", - "reference": "fff576ac850c045158a250e7e27666e146e78d18", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600", + "reference": "b174585d1fe49ceed21928a945138948cb394600", "shasum": "" }, "require": { @@ -408,7 +408,7 @@ "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", "support": { "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.3" + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4" }, "funding": [ { @@ -424,7 +424,7 @@ "type": "tidelift" } ], - "time": "2021-08-17T13:49:14+00:00" + "time": "2021-09-13T08:41:34+00:00" }, { "name": "dragonmantank/cron-expression", @@ -1984,11 +1984,11 @@ }, { "name": "utopia-php/database", - "version": "dev-feat-check-attribute-method", + "version": "dev-feat-get-limit-methods-on-database", "source": { "type": "git", "url": "https://github.com/utopia-php/database", - "reference": "2d1f48345f1284876f6847599c66eee145b7233e" + "reference": "4cc9d5eb9c6be1a9116a4d9dc2e6bd9db4e8e9c0" }, "require": { "ext-mongodb": "*", @@ -2037,7 +2037,7 @@ "upf", "utopia" ], - "time": "2021-09-01T00:50:12+00:00" + "time": "2021-09-17T01:44:11+00:00" }, { "name": "utopia-php/domains", @@ -3761,33 +3761,33 @@ }, { "name": "phpspec/prophecy", - "version": "1.13.0", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea" + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea", - "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", "shasum": "" }, "require": { "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.1", + "php": "^7.2 || ~8.0, <8.2", "phpdocumentor/reflection-docblock": "^5.2", "sebastian/comparator": "^3.0 || ^4.0", "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^6.0", + "phpspec/phpspec": "^6.0 || ^7.0", "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -3822,9 +3822,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.13.0" + "source": "https://github.com/phpspec/prophecy/tree/1.14.0" }, - "time": "2021-03-17T13:42:18+00:00" + "time": "2021-09-10T09:02:12+00:00" }, { "name": "phpunit/php-code-coverage", @@ -6251,7 +6251,7 @@ "aliases": [ { "package": "utopia-php/database", - "version": "dev-feat-check-attribute-method", + "version": "dev-feat-get-limit-methods-on-database", "alias": "0.10.0", "alias_normalized": "0.10.0.0" } From c9601160a6089aaa25ac7438fb142d1e1d48cda3 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 20 Sep 2021 21:22:08 -0400 Subject: [PATCH 081/130] Handle case of duplicate indexes on deleteAttribute --- app/workers/database.php | 45 +++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/app/workers/database.php b/app/workers/database.php index 6cdcead7b..0ca7aa45f 100644 --- a/app/workers/database.php +++ b/app/workers/database.php @@ -120,24 +120,53 @@ class DatabaseV1 extends Worker $dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed')); } - // the underlying database removes/rebuilds indexes when attribute is removed - // update indexes table with changes + // The underlying database removes/rebuilds indexes when attribute is removed + // Update indexes table with changes /** @var Document[] $indexes */ $indexes = $collection->getAttribute('indexes', []); foreach ($indexes as $index) { /** @var string[] $attributes */ - $attributes = $index->getAttribute('attributes'); - $found = array_search($key, $attributes); + $attributes = $index->getAttribute('attributes'); + $lengths = $index->getAttribute('lengths'); + $orders = $index->getAttribute('orders'); + + $found = \array_search($key, $attributes); if ($found !== false) { - $remove = [$attributes[$found]]; - $attributes = array_diff($attributes, $remove); // remove attribute from array + // If found, remove entry from attributes, lengths, and orders + // array_values wraps array_diff to reindex array keys + // when found attribute is removed from array + $attributes = \array_values(\array_diff($attributes, [$attributes[$found]])); + $lengths = \array_values(\array_diff($lengths, [$lengths[$found]])); + $orders = \array_values(\array_diff($orders, [$orders[$found]])); if (empty($attributes)) { $dbForInternal->deleteDocument('indexes', $index->getId()); } else { - $dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN)); + $index + ->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN) + ->setAttribute('lengths', $lengths, Document::SET_TYPE_ASSIGN) + ->setAttribute('orders', $orders, Document::SET_TYPE_ASSIGN) + ; + + // Check if an index exists with the same attributes and orders + $exists = false; + foreach ($indexes as $existing) { + if ($existing->getAttribute('key') !== $index->getAttribute('key') // Ignore itself + && $existing->getAttribute('attributes') === $index->getAttribute('attributes') + && $existing->getAttribute('orders') === $index->getAttribute('orders') + ) { + $exists = true; + break; + } + } + + if ($exists) { // Delete the duplicate if created, else update in db + $this->deleteIndex($collection, $index, $projectId); + } else { + $dbForInternal->updateDocument('indexes', $index->getId(), $index); + } } } } @@ -190,7 +219,7 @@ class DatabaseV1 extends Worker try { if(!$dbForExternal->deleteIndex($collectionId, $key)) { - throw new Exception('Failed to delete Attribute'); + throw new Exception('Failed to delete index'); } $dbForInternal->deleteDocument('indexes', $index->getId()); From fdd43837c16bfe50a5f9e28ec75a23b001960262 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 20 Sep 2021 21:23:41 -0400 Subject: [PATCH 082/130] Test duplicate index is removed in edge case of deleteAttribute --- .../Database/DatabaseCustomServerTest.php | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/tests/e2e/Services/Database/DatabaseCustomServerTest.php b/tests/e2e/Services/Database/DatabaseCustomServerTest.php index 2fe0f9731..fd4a12b5a 100644 --- a/tests/e2e/Services/Database/DatabaseCustomServerTest.php +++ b/tests/e2e/Services/Database/DatabaseCustomServerTest.php @@ -298,6 +298,7 @@ class DatabaseCustomServerTest extends Scope sleep(2); + // Expected behavior: deleting attribute2 will cause index2 to be dropped, and index1 rebuilt with a single key $deleted = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/attributes/'. $attribute2['body']['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -335,6 +336,104 @@ class DatabaseCustomServerTest extends Scope return $data; } + /** + * @depends testDeleteIndex + */ + public function testCleanupDuplicateIndexOnDeleteAttribute($data) + { + $attribute1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'attribute1', + 'size' => 16, + 'required' => true, + ]); + + $attribute2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'attributeId' => 'attribute2', + 'size' => 16, + 'required' => true, + ]); + + $this->assertEquals(201, $attribute1['headers']['status-code']); + $this->assertEquals(201, $attribute2['headers']['status-code']); + $this->assertEquals('attribute1', $attribute1['body']['key']); + $this->assertEquals('attribute2', $attribute2['body']['key']); + + sleep(2); + + $index1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'indexId' => 'index1', + 'type' => 'key', + 'attributes' => ['attribute1', 'attribute2'], + 'orders' => ['ASC', 'ASC'], + ]); + + $index2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'indexId' => 'index2', + 'type' => 'key', + 'attributes' => ['attribute2'], + ]); + + $this->assertEquals(201, $index1['headers']['status-code']); + $this->assertEquals(201, $index2['headers']['status-code']); + $this->assertEquals('index1', $index1['body']['key']); + $this->assertEquals('index2', $index2['body']['key']); + + sleep(2); + + // Expected behavior: deleting attribute1 would cause index1 to be a duplicate of index2 and automatically removed + $deleted = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/attributes/'. $attribute1['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals($deleted['headers']['status-code'], 204); + + // wait for database worker to complete + sleep(2); + + $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['collectionId'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $collection['headers']['status-code']); + $this->assertIsArray($collection['body']['indexes']); + $this->assertCount(1, $collection['body']['indexes']); + $this->assertEquals($index2['body']['key'], $collection['body']['indexes'][0]['key']); + $this->assertIsArray($collection['body']['indexes'][0]['attributes']); + $this->assertCount(1, $collection['body']['indexes'][0]['attributes']); + $this->assertEquals($attribute2['body']['key'], $collection['body']['indexes'][0]['attributes'][0]); + + // Delete attribute + $deleted = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/attributes/' . $attribute2['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals($deleted['headers']['status-code'], 204); + + return $data; + } + /** * @depends testDeleteIndexOnDeleteAttribute */ From 732c59a03de82ccebe5f4cb1b3a785cd8345abc7 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Tue, 21 Sep 2021 10:22:13 +0200 Subject: [PATCH 083/130] Implemented search tests --- app/controllers/api/users.php | 4 +- composer.lock | 147 +++++++++--------- .../Functions/FunctionsCustomClientTest.php | 1 + .../Functions/FunctionsCustomServerTest.php | 74 +++++++++ .../Projects/ProjectsConsoleClientTest.php | 23 +++ tests/e2e/Services/Storage/StorageBase.php | 38 ++++- tests/e2e/Services/Teams/TeamsBase.php | 14 ++ tests/e2e/Services/Users/UsersBase.php | 80 +++++++++- 8 files changed, 294 insertions(+), 87 deletions(-) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 4ca682b92..e4008e545 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -97,7 +97,7 @@ App::get('/v1/users') $afterUser = $dbForInternal->getDocument('users', $after); if ($afterUser->isEmpty()) { - throw new Exception('User for after not found', 400); + throw new Exception("User '{$after}' for the 'after' value not found.", 400); } } @@ -107,7 +107,7 @@ App::get('/v1/users') $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); } - $results = $dbForInternal->find('users', $queries, $limit, $offset, ['_id'], [$orderType]); + $results = $dbForInternal->find('users', $queries, $limit, $offset, [], [$orderType], $afterUser ?? null); $sum = $dbForInternal->count('users', $queries, APP_LIMIT_COUNT); $response->dynamic(new Document([ diff --git a/composer.lock b/composer.lock index db07f5df2..f1709a381 100644 --- a/composer.lock +++ b/composer.lock @@ -248,16 +248,16 @@ }, { "name": "chillerlan/php-settings-container", - "version": "2.1.1", + "version": "2.1.2", "source": { "type": "git", "url": "https://github.com/chillerlan/php-settings-container.git", - "reference": "98ccc1b31b31a53bcb563465c4961879b2b93096" + "reference": "ec834493a88682dd69652a1eeaf462789ed0c5f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/98ccc1b31b31a53bcb563465c4961879b2b93096", - "reference": "98ccc1b31b31a53bcb563465c4961879b2b93096", + "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/ec834493a88682dd69652a1eeaf462789ed0c5f5", + "reference": "ec834493a88682dd69652a1eeaf462789ed0c5f5", "shasum": "" }, "require": { @@ -307,7 +307,7 @@ "type": "ko_fi" } ], - "time": "2021-01-06T15:57:03+00:00" + "time": "2021-09-06T15:17:01+00:00" }, { "name": "colinmollenhour/credis", @@ -355,16 +355,16 @@ }, { "name": "composer/package-versions-deprecated", - "version": "1.11.99.2", + "version": "1.11.99.4", "source": { "type": "git", "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "c6522afe5540d5fc46675043d3ed5a45a740b27c" + "reference": "b174585d1fe49ceed21928a945138948cb394600" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/c6522afe5540d5fc46675043d3ed5a45a740b27c", - "reference": "c6522afe5540d5fc46675043d3ed5a45a740b27c", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600", + "reference": "b174585d1fe49ceed21928a945138948cb394600", "shasum": "" }, "require": { @@ -408,7 +408,7 @@ "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", "support": { "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.2" + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4" }, "funding": [ { @@ -424,7 +424,7 @@ "type": "tidelift" } ], - "time": "2021-05-24T07:46:03+00:00" + "time": "2021-09-13T08:41:34+00:00" }, { "name": "dragonmantank/cron-expression", @@ -1666,22 +1666,22 @@ }, { "name": "utopia-php/abuse", - "version": "0.6.2", + "version": "0.6.3", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "4cd9c16610f7398d2e1737663ef682fa721ae736" + "reference": "d63e928c2c50b367495a499a85ba9806ee274c5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/4cd9c16610f7398d2e1737663ef682fa721ae736", - "reference": "4cd9c16610f7398d2e1737663ef682fa721ae736", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/d63e928c2c50b367495a499a85ba9806ee274c5e", + "reference": "d63e928c2c50b367495a499a85ba9806ee274c5e", "shasum": "" }, "require": { "ext-pdo": "*", "php": ">=7.4", - "utopia-php/database": "0.7.*" + "utopia-php/database": ">=0.6 <1.0" }, "require-dev": { "phpunit/phpunit": "^9.4", @@ -1713,9 +1713,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.6.2" + "source": "https://github.com/utopia-php/abuse/tree/0.6.3" }, - "time": "2021-08-13T07:52:34+00:00" + "time": "2021-08-16T18:38:31+00:00" }, { "name": "utopia-php/analytics", @@ -1774,22 +1774,22 @@ }, { "name": "utopia-php/audit", - "version": "0.6.2", + "version": "0.6.3", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "2ec39a53eb98a5f9d230550ad56c7c04de5d77df" + "reference": "d79b467fbc7d03e5e02f12cdeb08761507a60ca0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/2ec39a53eb98a5f9d230550ad56c7c04de5d77df", - "reference": "2ec39a53eb98a5f9d230550ad56c7c04de5d77df", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/d79b467fbc7d03e5e02f12cdeb08761507a60ca0", + "reference": "d79b467fbc7d03e5e02f12cdeb08761507a60ca0", "shasum": "" }, "require": { "ext-pdo": "*", "php": ">=7.4", - "utopia-php/database": "0.7.*" + "utopia-php/database": ">=0.6 <1.0" }, "require-dev": { "phpunit/phpunit": "^9.3", @@ -1821,9 +1821,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.6.2" + "source": "https://github.com/utopia-php/audit/tree/0.6.3" }, - "time": "2021-08-13T08:05:20+00:00" + "time": "2021-08-16T18:49:55+00:00" }, { "name": "utopia-php/cache", @@ -3389,16 +3389,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.12.0", + "version": "v4.13.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6608f01670c3cc5079e18c1dab1104e002579143" + "reference": "50953a2691a922aa1769461637869a0a2faa3f53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6608f01670c3cc5079e18c1dab1104e002579143", - "reference": "6608f01670c3cc5079e18c1dab1104e002579143", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/50953a2691a922aa1769461637869a0a2faa3f53", + "reference": "50953a2691a922aa1769461637869a0a2faa3f53", "shasum": "" }, "require": { @@ -3439,9 +3439,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.12.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.0" }, - "time": "2021-07-21T10:44:31+00:00" + "time": "2021-09-20T12:20:58+00:00" }, { "name": "openlss/lib-array2xml", @@ -3718,16 +3718,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.4.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" + "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/30f38bffc6f24293dadd1823936372dfa9e86e2f", + "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f", "shasum": "" }, "require": { @@ -3735,7 +3735,8 @@ "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "*" + "ext-tokenizer": "*", + "psalm/phar": "^4.8" }, "type": "library", "extra": { @@ -3761,39 +3762,39 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.0" }, - "time": "2020-09-17T18:55:26+00:00" + "time": "2021-09-17T15:28:14+00:00" }, { "name": "phpspec/prophecy", - "version": "1.13.0", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea" + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea", - "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", "shasum": "" }, "require": { "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.1", + "php": "^7.2 || ~8.0, <8.2", "phpdocumentor/reflection-docblock": "^5.2", "sebastian/comparator": "^3.0 || ^4.0", "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^6.0", + "phpspec/phpspec": "^6.0 || ^7.0", "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -3828,29 +3829,29 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.13.0" + "source": "https://github.com/phpspec/prophecy/tree/1.14.0" }, - "time": "2021-03-17T13:42:18+00:00" + "time": "2021-09-10T09:02:12+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.6", + "version": "9.2.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "f6293e1b30a2354e8428e004689671b83871edde" + "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde", - "reference": "f6293e1b30a2354e8428e004689671b83871edde", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218", + "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.10.2", + "nikic/php-parser": "^4.12.0", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -3899,7 +3900,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7" }, "funding": [ { @@ -3907,7 +3908,7 @@ "type": "github" } ], - "time": "2021-03-28T07:26:59+00:00" + "time": "2021-09-17T05:39:03+00:00" }, { "name": "phpunit/php-file-iterator", @@ -5319,16 +5320,16 @@ }, { "name": "symfony/console", - "version": "v5.3.6", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "51b71afd6d2dc8f5063199357b9880cea8d8bfe2" + "reference": "8b1008344647462ae6ec57559da166c2bfa5e16a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/51b71afd6d2dc8f5063199357b9880cea8d8bfe2", - "reference": "51b71afd6d2dc8f5063199357b9880cea8d8bfe2", + "url": "https://api.github.com/repos/symfony/console/zipball/8b1008344647462ae6ec57559da166c2bfa5e16a", + "reference": "8b1008344647462ae6ec57559da166c2bfa5e16a", "shasum": "" }, "require": { @@ -5398,7 +5399,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.3.6" + "source": "https://github.com/symfony/console/tree/v5.3.7" }, "funding": [ { @@ -5414,7 +5415,7 @@ "type": "tidelift" } ], - "time": "2021-07-27T19:10:22+00:00" + "time": "2021-08-25T20:02:16+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5888,16 +5889,16 @@ }, { "name": "symfony/string", - "version": "v5.3.3", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1" + "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1", - "reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1", + "url": "https://api.github.com/repos/symfony/string/zipball/8d224396e28d30f81969f083a58763b8b9ceb0a5", + "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5", "shasum": "" }, "require": { @@ -5951,7 +5952,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.3.3" + "source": "https://github.com/symfony/string/tree/v5.3.7" }, "funding": [ { @@ -5967,7 +5968,7 @@ "type": "tidelift" } ], - "time": "2021-06-27T11:44:38+00:00" + "time": "2021-08-26T08:00:08+00:00" }, { "name": "theseer/tokenizer", @@ -6021,16 +6022,16 @@ }, { "name": "twig/twig", - "version": "v2.14.6", + "version": "v2.14.7", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "27e5cf2b05e3744accf39d4c68a3235d9966d260" + "reference": "8e202327ee1ed863629de9b18a5ec70ac614d88f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/27e5cf2b05e3744accf39d4c68a3235d9966d260", - "reference": "27e5cf2b05e3744accf39d4c68a3235d9966d260", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/8e202327ee1ed863629de9b18a5ec70ac614d88f", + "reference": "8e202327ee1ed863629de9b18a5ec70ac614d88f", "shasum": "" }, "require": { @@ -6040,7 +6041,7 @@ }, "require-dev": { "psr/container": "^1.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9" + "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" }, "type": "library", "extra": { @@ -6084,7 +6085,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v2.14.6" + "source": "https://github.com/twigphp/Twig/tree/v2.14.7" }, "funding": [ { @@ -6096,7 +6097,7 @@ "type": "tidelift" } ], - "time": "2021-05-16T12:12:47+00:00" + "time": "2021-09-17T08:39:54+00:00" }, { "name": "vimeo/psalm", diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 86f6fde48..0d229aafc 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -7,6 +7,7 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideClient; +use function var_dump; class FunctionsCustomClientTest extends Scope { diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index cb820b45c..e118df8a5 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -7,6 +7,8 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; +use function array_merge; +use function var_dump; class FunctionsCustomServerTest extends Scope { @@ -77,6 +79,39 @@ class FunctionsCustomServerTest extends Scope /** * Test for SUCCESS */ + $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $data['functionId'] + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertCount(1, $response['body']['functions']); + $this->assertEquals($response['body']['functions'][0]['name'], 'Test'); + + $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'Test' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertCount(1, $response['body']['functions']); + $this->assertEquals($response['body']['functions'][0]['$id'], $data['functionId']); + + $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'php-8.0' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertCount(1, $response['body']['functions']); + $this->assertEquals($response['body']['functions'][0]['$id'], $data['functionId']); + $response = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ 'content-type' => 'application/json', @@ -283,6 +318,45 @@ class FunctionsCustomServerTest extends Scope $this->assertIsArray($function['body']['tags']); $this->assertCount(1, $function['body']['tags']); + $function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/tags', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders(), [ + 'search' => $data['functionId'] + ])); + + $this->assertEquals($function['headers']['status-code'], 200); + $this->assertEquals($function['body']['sum'], 1); + $this->assertIsArray($function['body']['tags']); + $this->assertCount(1, $function['body']['tags']); + $this->assertEquals($function['body']['tags'][0]['$id'], $data['tagId']); + + $function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/tags', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders(), [ + 'search' => 'Test' + ])); + + $this->assertEquals($function['headers']['status-code'], 200); + $this->assertEquals($function['body']['sum'], 1); + $this->assertIsArray($function['body']['tags']); + $this->assertCount(1, $function['body']['tags']); + $this->assertEquals($function['body']['tags'][0]['$id'], $data['tagId']); + + $function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/tags', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders(), [ + 'search' => 'php-8.0' + ])); + + $this->assertEquals($function['headers']['status-code'], 200); + $this->assertEquals($function['body']['sum'], 1); + $this->assertIsArray($function['body']['tags']); + $this->assertCount(1, $function['body']['tags']); + $this->assertEquals($function['body']['tags'][0]['$id'], $data['tagId']); + return $data; } diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 4d83efb41..6c0813cc2 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -7,6 +7,7 @@ use Tests\E2E\Scopes\ProjectConsole; use Tests\E2E\Scopes\SideClient; use Tests\E2E\Services\Projects\ProjectsBase; use Tests\E2E\Client; +use function array_merge; class ProjectsConsoleClientTest extends Scope { @@ -97,6 +98,28 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals($id, $response['body']['projects'][0]['$id']); $this->assertEquals('Project Test', $response['body']['projects'][0]['name']); + $response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders(), [ + 'search' => $id + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']); + $this->assertEquals('Project Test', $response['body']['projects'][0]['name']); + + $response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders(), [ + 'search' => 'Project Test' + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']); + $this->assertEquals($id, $response['body']['projects'][0]['$id']); + /** * Test after pagination */ diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index 57a5ee697..fde1fc9fb 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -5,6 +5,9 @@ namespace Tests\E2E\Services\Storage; use CURLFile; use Tests\E2E\Client; use Utopia\Image\Image; +use function array_merge; +use function realpath; +use function var_dump; trait StorageBase { @@ -163,7 +166,7 @@ trait StorageBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'fileId' => 'unique()', - 'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'), + 'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/file.png'), 'image/png', 'file.png'), 'read' => ['role:all'], 'write' => ['role:all'], ]); @@ -171,9 +174,9 @@ trait StorageBase $this->assertEquals($file['headers']['status-code'], 201); $this->assertNotEmpty($file['body']['$id']); $this->assertIsInt($file['body']['dateCreated']); - $this->assertEquals('logo.png', $file['body']['name']); - $this->assertEquals('image/png', $file['body']['mimeType']); - $this->assertEquals(47218, $file['body']['sizeOriginal']); + $this->assertEquals('file.png', $file['body']['name']); + $this->assertEquals('image/jpeg', $file['body']['mimeType']); + $this->assertEquals(16804, $file['body']['sizeOriginal']); $files = $this->client->call(Client::METHOD_GET, '/storage/files', array_merge([ 'content-type' => 'application/json', @@ -197,6 +200,33 @@ trait StorageBase $this->assertEquals($files['body']['files'][1]['$id'], $response['body']['files'][0]['$id']); $this->assertCount(1, $response['body']['files']); + $response = $this->client->call(Client::METHOD_GET, '/storage/files', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $data['fileId'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertGreaterThan(0, $response['body']['sum']); + $this->assertIsInt($response['body']['sum']); + $this->assertCount(1, $response['body']['files']); + $this->assertEquals('logo.png', $response['body']['files'][0]['name']); + + + $response = $this->client->call(Client::METHOD_GET, '/storage/files', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'logo', + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertGreaterThan(0, $response['body']['sum']); + $this->assertIsInt($response['body']['sum']); + $this->assertGreaterThan(0, $response['body']['files']); + $this->assertEquals($data['fileId'], $response['body']['files'][0]['$id']); + /** * Test for FAILURE */ diff --git a/tests/e2e/Services/Teams/TeamsBase.php b/tests/e2e/Services/Teams/TeamsBase.php index 6c1ebbe36..fd1b064a7 100644 --- a/tests/e2e/Services/Teams/TeamsBase.php +++ b/tests/e2e/Services/Teams/TeamsBase.php @@ -3,6 +3,7 @@ namespace Tests\E2E\Services\Teams; use Tests\E2E\Client; +use function array_merge; trait TeamsBase { @@ -172,6 +173,19 @@ trait TeamsBase $this->assertCount(1, $response['body']['teams']); $this->assertEquals('Manchester United', $response['body']['teams'][0]['name']); + $response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $data['teamUid'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertGreaterThan(0, $response['body']['sum']); + $this->assertIsInt($response['body']['sum']); + $this->assertCount(1, $response['body']['teams']); + $this->assertEquals('Arsenal', $response['body']['teams'][0]['name']); + $teams = $this->client->call(Client::METHOD_GET, '/teams', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php index b35f355c9..e85e2a44a 100644 --- a/tests/e2e/Services/Users/UsersBase.php +++ b/tests/e2e/Services/Users/UsersBase.php @@ -3,6 +3,8 @@ namespace Tests\E2E\Services\Users; use Tests\E2E\Client; +use function array_merge; +use function var_dump; trait UsersBase { @@ -16,14 +18,14 @@ trait UsersBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'userId' => 'unique()', - 'email' => 'users.service@example.com', + 'email' => 'first.user@example.com', 'password' => 'password', - 'name' => 'Project User', + 'name' => 'First Name', ]); $this->assertEquals($user['headers']['status-code'], 201); - $this->assertEquals($user['body']['name'], 'Project User'); - $this->assertEquals($user['body']['email'], 'users.service@example.com'); + $this->assertEquals($user['body']['name'], 'First Name'); + $this->assertEquals($user['body']['email'], 'first.user@example.com'); $this->assertEquals($user['body']['status'], true); $this->assertGreaterThan(0, $user['body']['registration']); @@ -56,7 +58,7 @@ trait UsersBase public function testListUsers(array $data): void { /** - * Test for SUCCESS + * Test for SUCCESS listUsers */ $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ 'content-type' => 'application/json', @@ -82,8 +84,70 @@ trait UsersBase $this->assertNotEmpty($response['body']); $this->assertNotEmpty($response['body']['users']); $this->assertCount(1, $response['body']['users']); - $this->assertEquals($response['body']['users'][0]['$id'], 'user1'); + + /** + * Test for SUCCESS searchUsers + */ + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'First Name' + ]); + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertGreaterThan(0, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); + + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'first.user' + ]); + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertGreaterThan(0, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); + + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'first user name' + ]); + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertGreaterThan(0, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); + + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $data['userId'] + ]); + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertGreaterThan(0, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); + + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'first user - first.user@example.com ' . $data['userId'] + ]); + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertGreaterThan(0, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); } /** @@ -100,8 +164,8 @@ trait UsersBase ], $this->getHeaders())); $this->assertEquals($user['headers']['status-code'], 200); - $this->assertEquals($user['body']['name'], 'Project User'); - $this->assertEquals($user['body']['email'], 'users.service@example.com'); + $this->assertEquals($user['body']['name'], 'First Name'); + $this->assertEquals($user['body']['email'], 'first.user@example.com'); $this->assertEquals($user['body']['status'], true); $this->assertGreaterThan(0, $user['body']['registration']); From 9d1b1a17d951a4ee050dbf99ce740622f50f6050 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Tue, 21 Sep 2021 10:26:28 +0200 Subject: [PATCH 084/130] Removed PHPStorm-generated stuff --- tests/e2e/Services/Functions/FunctionsCustomClientTest.php | 1 - tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 2 -- tests/e2e/Services/Storage/StorageBase.php | 1 - tests/e2e/Services/Users/UsersBase.php | 2 -- 4 files changed, 6 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 0d229aafc..86f6fde48 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -7,7 +7,6 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideClient; -use function var_dump; class FunctionsCustomClientTest extends Scope { diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index e118df8a5..62198293a 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -7,8 +7,6 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; -use function array_merge; -use function var_dump; class FunctionsCustomServerTest extends Scope { diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index fde1fc9fb..47a81444c 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -7,7 +7,6 @@ use Tests\E2E\Client; use Utopia\Image\Image; use function array_merge; use function realpath; -use function var_dump; trait StorageBase { diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php index e85e2a44a..4bf344d95 100644 --- a/tests/e2e/Services/Users/UsersBase.php +++ b/tests/e2e/Services/Users/UsersBase.php @@ -3,8 +3,6 @@ namespace Tests\E2E\Services\Users; use Tests\E2E\Client; -use function array_merge; -use function var_dump; trait UsersBase { From fce5e3e96c54488a2250c8c801ba35cd1e1cf7ed Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Tue, 21 Sep 2021 10:27:31 +0200 Subject: [PATCH 085/130] Removed PHPStorm-generated stuff 2 --- tests/e2e/Services/Projects/ProjectsConsoleClientTest.php | 1 - tests/e2e/Services/Storage/StorageBase.php | 2 -- tests/e2e/Services/Teams/TeamsBase.php | 1 - 3 files changed, 4 deletions(-) diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 6c0813cc2..9edee076a 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -7,7 +7,6 @@ use Tests\E2E\Scopes\ProjectConsole; use Tests\E2E\Scopes\SideClient; use Tests\E2E\Services\Projects\ProjectsBase; use Tests\E2E\Client; -use function array_merge; class ProjectsConsoleClientTest extends Scope { diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index 47a81444c..8d9b2d9c8 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -5,8 +5,6 @@ namespace Tests\E2E\Services\Storage; use CURLFile; use Tests\E2E\Client; use Utopia\Image\Image; -use function array_merge; -use function realpath; trait StorageBase { diff --git a/tests/e2e/Services/Teams/TeamsBase.php b/tests/e2e/Services/Teams/TeamsBase.php index fd1b064a7..b594bbfa6 100644 --- a/tests/e2e/Services/Teams/TeamsBase.php +++ b/tests/e2e/Services/Teams/TeamsBase.php @@ -3,7 +3,6 @@ namespace Tests\E2E\Services\Teams; use Tests\E2E\Client; -use function array_merge; trait TeamsBase { From 77793b4e01160922e0154a46d49b2260ea0ff020 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Tue, 21 Sep 2021 13:18:54 +0200 Subject: [PATCH 086/130] update instead of delete, adjust code to new attribute --- app/config/collections2.php | 11 +++ app/controllers/api/account.php | 23 ++++-- app/controllers/api/users.php | 47 ++++++++----- composer.lock | 119 ++++++++++++++++---------------- 4 files changed, 115 insertions(+), 85 deletions(-) diff --git a/app/config/collections2.php b/app/config/collections2.php index 9ba963545..866d1cb1b 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -695,6 +695,17 @@ $collections = [ 'array' => true, 'filters' => ['json'], ], + [ + '$id' => 'deleted', + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], ], 'indexes' => [ [ diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index bc34f81dd..3ad04efe8 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -78,7 +78,9 @@ App::post('/v1/account') $limit = $project->getAttribute('auths', [])['limit'] ?? 0; if ($limit !== 0) { - $sum = $dbForInternal->count('users', [], APP_LIMIT_USERS); + $sum = $dbForInternal->count('users', [ + new Query('deleted', Query::TYPE_EQUAL, [false]), + ], APP_LIMIT_USERS); if ($sum >= $limit) { throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501); @@ -105,6 +107,7 @@ App::post('/v1/account') 'sessions' => [], 'tokens' => [], 'memberships' => [], + 'deleted' => false ])); } catch (Duplicate $th) { throw new Exception('Account already exists', 409); @@ -165,7 +168,7 @@ App::post('/v1/account/sessions') $email = \strtolower($email); $protocol = $request->getProtocol(); - $profile = $dbForInternal->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address + $profile = $dbForInternal->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address if (!$profile || !Auth::passwordVerify($password, $profile->getAttribute('password'))) { $audits @@ -462,13 +465,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $name = $oauth2->getUserName($accessToken); $email = $oauth2->getUserEmail($accessToken); - $user = $dbForInternal->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address + $user = $dbForInternal->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address if ($user === false || $user->isEmpty()) { // Last option -> create the user, generate random password $limit = $project->getAttribute('auths', [])['limit'] ?? 0; if ($limit !== 0) { - $sum = $dbForInternal->count('users', [], APP_LIMIT_COUNT); + $sum = $dbForInternal->count('users', [ new Query('deleted', Query::TYPE_EQUAL, [false]),], APP_LIMIT_COUNT); if ($sum >= $limit) { throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501); @@ -495,6 +498,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'sessions' => [], 'tokens' => [], 'memberships' => [], + 'deleted' => false ])); } catch (Duplicate $th) { throw new Exception('Account already exists', 409); @@ -639,7 +643,9 @@ App::post('/v1/account/sessions/anonymous') $limit = $project->getAttribute('auths', [])['limit'] ?? 0; if ($limit !== 0) { - $sum = $dbForInternal->count('users', [], APP_LIMIT_COUNT); + $sum = $dbForInternal->count('users', [ + new Query('deleted', Query::TYPE_EQUAL, [false]), + ], APP_LIMIT_COUNT); if ($sum >= $limit) { throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501); @@ -665,6 +671,7 @@ App::post('/v1/account/sessions/anonymous') 'sessions' => [], 'tokens' => [], 'memberships' => [], + 'deleted' => false ])); Authorization::reset(); @@ -1221,6 +1228,8 @@ App::delete('/v1/account') $protocol = $request->getProtocol(); $user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('status', false)); + // TODO Seems to be related to users.php/App::delete('/v1/users/:userId'). Can we share code between these two? Do todos below apply to users.php? + // TODO delete all tokens or only current session? // TODO delete all user data according to GDPR. Make sure everything is backed up and backups are deleted later /* @@ -1463,7 +1472,7 @@ App::post('/v1/account/recovery') $isAppUser = Auth::isAppUser(Authorization::$roles); $email = \strtolower($email); - $profile = $dbForInternal->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address + $profile = $dbForInternal->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address if (!$profile) { throw new Exception('User not found', 404); @@ -1566,7 +1575,7 @@ App::put('/v1/account/recovery') $profile = $dbForInternal->getDocument('users', $userId); - if ($profile->isEmpty()) { + if ($profile->isEmpty() || $profile->getAttribute('deleted')) { throw new Exception('User not found', 404); } diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index c6c8207e2..20266d1fd 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -66,6 +66,7 @@ App::post('/v1/users') 'sessions' => [], 'tokens' => [], 'memberships' => [], + 'deleted' => false ])); } catch (Duplicate $th) { throw new Exception('Account already exists', 409); @@ -106,13 +107,17 @@ App::get('/v1/users') if (!empty($after)) { $afterUser = $dbForInternal->getDocument('users', $after); - if ($afterUser->isEmpty()) { + if ($afterUser->isEmpty() || $afterUser->getAttribute('deleted')) { throw new Exception('User for after not found', 400); } } - $results = $dbForInternal->find('users', [], $limit, $offset, [], [$orderType], $afterUser ?? null); - $sum = $dbForInternal->count('users', [], APP_LIMIT_COUNT); + $results = $dbForInternal->find('users', [ + new Query('deleted', Query::TYPE_EQUAL, [false]), + ], $limit, $offset, [], [$orderType], $afterUser ?? null); + $sum = $dbForInternal->count('users', [ + new Query('deleted', Query::TYPE_EQUAL, [false]), + ], APP_LIMIT_COUNT); $usage ->setParam('users.read', 1) @@ -146,7 +151,7 @@ App::get('/v1/users/:userId') $user = $dbForInternal->getDocument('users', $userId); - if ($user->isEmpty()) { + if ($user->isEmpty() || $user->getAttribute('deleted')) { throw new Exception('User not found', 404); } @@ -178,7 +183,7 @@ App::get('/v1/users/:userId/prefs') $user = $dbForInternal->getDocument('users', $userId); - if ($user->isEmpty()) { + if ($user->isEmpty() || $user->getAttribute('deleted')) { throw new Exception('User not found', 404); } @@ -214,7 +219,7 @@ App::get('/v1/users/:userId/sessions') $user = $dbForInternal->getDocument('users', $userId); - if ($user->isEmpty()) { + if ($user->isEmpty() || $user->getAttribute('deleted')) { throw new Exception('User not found', 404); } @@ -266,7 +271,7 @@ App::get('/v1/users/:userId/logs') $user = $dbForInternal->getDocument('users', $userId); - if ($user->isEmpty()) { + if ($user->isEmpty() || $user->getAttribute('deleted')) { throw new Exception('User not found', 404); } @@ -374,7 +379,7 @@ App::patch('/v1/users/:userId/status') $user = $dbForInternal->getDocument('users', $userId); - if ($user->isEmpty()) { + if ($user->isEmpty() || $user->getAttribute('deleted')) { throw new Exception('User not found', 404); } @@ -410,7 +415,7 @@ App::patch('/v1/users/:userId/verification') $user = $dbForInternal->getDocument('users', $userId); - if ($user->isEmpty()) { + if ($user->isEmpty() || $user->getAttribute('deleted')) { throw new Exception('User not found', 404); } @@ -446,7 +451,7 @@ App::patch('/v1/users/:userId/name') $user = $dbForInternal->getDocument('users', $userId); - if ($user->isEmpty()) { + if ($user->isEmpty() || $user->getAttribute('deleted')) { throw new Exception('User not found', 404); } @@ -485,7 +490,7 @@ App::patch('/v1/users/:userId/password') $user = $dbForInternal->getDocument('users', $userId); - if ($user->isEmpty()) { + if ($user->isEmpty() || $user->getAttribute('deleted')) { throw new Exception('User not found', 404); } @@ -525,7 +530,7 @@ App::patch('/v1/users/:userId/email') $user = $dbForInternal->getDocument('users', $userId); - if ($user->isEmpty()) { + if ($user->isEmpty() || $user->getAttribute('deleted')) { throw new Exception('User not found', 404); } @@ -569,7 +574,7 @@ App::patch('/v1/users/:userId/prefs') $user = $dbForInternal->getDocument('users', $userId); - if ($user->isEmpty()) { + if ($user->isEmpty() || $user->getAttribute('deleted')) { throw new Exception('User not found', 404); } @@ -606,7 +611,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId') $user = $dbForInternal->getDocument('users', $userId); - if ($user->isEmpty()) { + if ($user->isEmpty() || $user->getAttribute('deleted')) { throw new Exception('User not found', 404); } @@ -661,7 +666,7 @@ App::delete('/v1/users/:userId/sessions') $user = $dbForInternal->getDocument('users', $userId); - if ($user->isEmpty()) { + if ($user->isEmpty() || $user->getAttribute('deleted')) { throw new Exception('User not found', 404); } @@ -710,13 +715,17 @@ App::delete('/v1/users/:userId') $user = $dbForInternal->getDocument('users', $userId); - if ($user->isEmpty()) { + if ($user->isEmpty() || $user->getAttribute('deleted')) { throw new Exception('User not found', 404); } - if (!$dbForInternal->deleteDocument('users', $userId)) { - throw new Exception('Failed to remove user from DB', 500); - } + $emptyUser = clone $user; + $emptyUser->setAttribute("name", null); + $emptyUser->setAttribute("email", null); + $emptyUser->setAttribute("password", null); + $emptyUser->setAttribute("deleted", true); + + $dbForInternal->updateDocument('users', $userId, $emptyUser); $deletes ->setParam('type', DELETE_TYPE_DOCUMENT) diff --git a/composer.lock b/composer.lock index 8dac7bcc6..c319daa5a 100644 --- a/composer.lock +++ b/composer.lock @@ -248,16 +248,16 @@ }, { "name": "chillerlan/php-settings-container", - "version": "2.1.1", + "version": "2.1.2", "source": { "type": "git", "url": "https://github.com/chillerlan/php-settings-container.git", - "reference": "98ccc1b31b31a53bcb563465c4961879b2b93096" + "reference": "ec834493a88682dd69652a1eeaf462789ed0c5f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/98ccc1b31b31a53bcb563465c4961879b2b93096", - "reference": "98ccc1b31b31a53bcb563465c4961879b2b93096", + "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/ec834493a88682dd69652a1eeaf462789ed0c5f5", + "reference": "ec834493a88682dd69652a1eeaf462789ed0c5f5", "shasum": "" }, "require": { @@ -307,7 +307,7 @@ "type": "ko_fi" } ], - "time": "2021-01-06T15:57:03+00:00" + "time": "2021-09-06T15:17:01+00:00" }, { "name": "colinmollenhour/credis", @@ -355,16 +355,16 @@ }, { "name": "composer/package-versions-deprecated", - "version": "1.11.99.3", + "version": "1.11.99.4", "source": { "type": "git", "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "fff576ac850c045158a250e7e27666e146e78d18" + "reference": "b174585d1fe49ceed21928a945138948cb394600" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/fff576ac850c045158a250e7e27666e146e78d18", - "reference": "fff576ac850c045158a250e7e27666e146e78d18", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600", + "reference": "b174585d1fe49ceed21928a945138948cb394600", "shasum": "" }, "require": { @@ -408,7 +408,7 @@ "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", "support": { "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.3" + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4" }, "funding": [ { @@ -424,7 +424,7 @@ "type": "tidelift" } ], - "time": "2021-08-17T13:49:14+00:00" + "time": "2021-09-13T08:41:34+00:00" }, { "name": "dragonmantank/cron-expression", @@ -3383,16 +3383,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.12.0", + "version": "v4.13.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6608f01670c3cc5079e18c1dab1104e002579143" + "reference": "50953a2691a922aa1769461637869a0a2faa3f53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6608f01670c3cc5079e18c1dab1104e002579143", - "reference": "6608f01670c3cc5079e18c1dab1104e002579143", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/50953a2691a922aa1769461637869a0a2faa3f53", + "reference": "50953a2691a922aa1769461637869a0a2faa3f53", "shasum": "" }, "require": { @@ -3433,9 +3433,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.12.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.0" }, - "time": "2021-07-21T10:44:31+00:00" + "time": "2021-09-20T12:20:58+00:00" }, { "name": "openlss/lib-array2xml", @@ -3712,16 +3712,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.4.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" + "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/30f38bffc6f24293dadd1823936372dfa9e86e2f", + "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f", "shasum": "" }, "require": { @@ -3729,7 +3729,8 @@ "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "*" + "ext-tokenizer": "*", + "psalm/phar": "^4.8" }, "type": "library", "extra": { @@ -3755,39 +3756,39 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.0" }, - "time": "2020-09-17T18:55:26+00:00" + "time": "2021-09-17T15:28:14+00:00" }, { "name": "phpspec/prophecy", - "version": "1.13.0", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea" + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea", - "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", "shasum": "" }, "require": { "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.1", + "php": "^7.2 || ~8.0, <8.2", "phpdocumentor/reflection-docblock": "^5.2", "sebastian/comparator": "^3.0 || ^4.0", "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^6.0", + "phpspec/phpspec": "^6.0 || ^7.0", "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -3822,29 +3823,29 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.13.0" + "source": "https://github.com/phpspec/prophecy/tree/1.14.0" }, - "time": "2021-03-17T13:42:18+00:00" + "time": "2021-09-10T09:02:12+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.6", + "version": "9.2.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "f6293e1b30a2354e8428e004689671b83871edde" + "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde", - "reference": "f6293e1b30a2354e8428e004689671b83871edde", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218", + "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.10.2", + "nikic/php-parser": "^4.12.0", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -3893,7 +3894,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7" }, "funding": [ { @@ -3901,7 +3902,7 @@ "type": "github" } ], - "time": "2021-03-28T07:26:59+00:00" + "time": "2021-09-17T05:39:03+00:00" }, { "name": "phpunit/php-file-iterator", @@ -5313,16 +5314,16 @@ }, { "name": "symfony/console", - "version": "v5.3.6", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "51b71afd6d2dc8f5063199357b9880cea8d8bfe2" + "reference": "8b1008344647462ae6ec57559da166c2bfa5e16a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/51b71afd6d2dc8f5063199357b9880cea8d8bfe2", - "reference": "51b71afd6d2dc8f5063199357b9880cea8d8bfe2", + "url": "https://api.github.com/repos/symfony/console/zipball/8b1008344647462ae6ec57559da166c2bfa5e16a", + "reference": "8b1008344647462ae6ec57559da166c2bfa5e16a", "shasum": "" }, "require": { @@ -5392,7 +5393,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.3.6" + "source": "https://github.com/symfony/console/tree/v5.3.7" }, "funding": [ { @@ -5408,7 +5409,7 @@ "type": "tidelift" } ], - "time": "2021-07-27T19:10:22+00:00" + "time": "2021-08-25T20:02:16+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5882,16 +5883,16 @@ }, { "name": "symfony/string", - "version": "v5.3.3", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1" + "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1", - "reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1", + "url": "https://api.github.com/repos/symfony/string/zipball/8d224396e28d30f81969f083a58763b8b9ceb0a5", + "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5", "shasum": "" }, "require": { @@ -5945,7 +5946,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.3.3" + "source": "https://github.com/symfony/string/tree/v5.3.7" }, "funding": [ { @@ -5961,7 +5962,7 @@ "type": "tidelift" } ], - "time": "2021-06-27T11:44:38+00:00" + "time": "2021-08-26T08:00:08+00:00" }, { "name": "theseer/tokenizer", @@ -6015,16 +6016,16 @@ }, { "name": "twig/twig", - "version": "v2.14.6", + "version": "v2.14.7", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "27e5cf2b05e3744accf39d4c68a3235d9966d260" + "reference": "8e202327ee1ed863629de9b18a5ec70ac614d88f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/27e5cf2b05e3744accf39d4c68a3235d9966d260", - "reference": "27e5cf2b05e3744accf39d4c68a3235d9966d260", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/8e202327ee1ed863629de9b18a5ec70ac614d88f", + "reference": "8e202327ee1ed863629de9b18a5ec70ac614d88f", "shasum": "" }, "require": { @@ -6034,7 +6035,7 @@ }, "require-dev": { "psr/container": "^1.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9" + "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" }, "type": "library", "extra": { @@ -6078,7 +6079,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v2.14.6" + "source": "https://github.com/twigphp/Twig/tree/v2.14.7" }, "funding": [ { @@ -6090,7 +6091,7 @@ "type": "tidelift" } ], - "time": "2021-05-16T12:12:47+00:00" + "time": "2021-09-17T08:39:54+00:00" }, { "name": "vimeo/psalm", From e8f77ae3633873f922fd7ae922f4c48c6d6897a8 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Tue, 21 Sep 2021 14:01:18 +0200 Subject: [PATCH 087/130] OpenAPI3 support for anyOf --- composer.lock | 119 +++++++++--------- .../Specification/Format/OpenAPI3.php | 28 ++++- 2 files changed, 84 insertions(+), 63 deletions(-) diff --git a/composer.lock b/composer.lock index c867efc33..27c2af756 100644 --- a/composer.lock +++ b/composer.lock @@ -248,16 +248,16 @@ }, { "name": "chillerlan/php-settings-container", - "version": "2.1.1", + "version": "2.1.2", "source": { "type": "git", "url": "https://github.com/chillerlan/php-settings-container.git", - "reference": "98ccc1b31b31a53bcb563465c4961879b2b93096" + "reference": "ec834493a88682dd69652a1eeaf462789ed0c5f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/98ccc1b31b31a53bcb563465c4961879b2b93096", - "reference": "98ccc1b31b31a53bcb563465c4961879b2b93096", + "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/ec834493a88682dd69652a1eeaf462789ed0c5f5", + "reference": "ec834493a88682dd69652a1eeaf462789ed0c5f5", "shasum": "" }, "require": { @@ -307,7 +307,7 @@ "type": "ko_fi" } ], - "time": "2021-01-06T15:57:03+00:00" + "time": "2021-09-06T15:17:01+00:00" }, { "name": "colinmollenhour/credis", @@ -355,16 +355,16 @@ }, { "name": "composer/package-versions-deprecated", - "version": "1.11.99.3", + "version": "1.11.99.4", "source": { "type": "git", "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "fff576ac850c045158a250e7e27666e146e78d18" + "reference": "b174585d1fe49ceed21928a945138948cb394600" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/fff576ac850c045158a250e7e27666e146e78d18", - "reference": "fff576ac850c045158a250e7e27666e146e78d18", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600", + "reference": "b174585d1fe49ceed21928a945138948cb394600", "shasum": "" }, "require": { @@ -408,7 +408,7 @@ "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", "support": { "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.3" + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4" }, "funding": [ { @@ -424,7 +424,7 @@ "type": "tidelift" } ], - "time": "2021-08-17T13:49:14+00:00" + "time": "2021-09-13T08:41:34+00:00" }, { "name": "dragonmantank/cron-expression", @@ -3383,16 +3383,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.12.0", + "version": "v4.13.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6608f01670c3cc5079e18c1dab1104e002579143" + "reference": "50953a2691a922aa1769461637869a0a2faa3f53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6608f01670c3cc5079e18c1dab1104e002579143", - "reference": "6608f01670c3cc5079e18c1dab1104e002579143", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/50953a2691a922aa1769461637869a0a2faa3f53", + "reference": "50953a2691a922aa1769461637869a0a2faa3f53", "shasum": "" }, "require": { @@ -3433,9 +3433,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.12.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.0" }, - "time": "2021-07-21T10:44:31+00:00" + "time": "2021-09-20T12:20:58+00:00" }, { "name": "openlss/lib-array2xml", @@ -3712,16 +3712,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.4.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" + "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", - "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/30f38bffc6f24293dadd1823936372dfa9e86e2f", + "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f", "shasum": "" }, "require": { @@ -3729,7 +3729,8 @@ "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "ext-tokenizer": "*" + "ext-tokenizer": "*", + "psalm/phar": "^4.8" }, "type": "library", "extra": { @@ -3755,39 +3756,39 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.0" }, - "time": "2020-09-17T18:55:26+00:00" + "time": "2021-09-17T15:28:14+00:00" }, { "name": "phpspec/prophecy", - "version": "1.13.0", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea" + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea", - "reference": "be1996ed8adc35c3fd795488a653f4b518be70ea", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", "shasum": "" }, "require": { "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.1", + "php": "^7.2 || ~8.0, <8.2", "phpdocumentor/reflection-docblock": "^5.2", "sebastian/comparator": "^3.0 || ^4.0", "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^6.0", + "phpspec/phpspec": "^6.0 || ^7.0", "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -3822,29 +3823,29 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.13.0" + "source": "https://github.com/phpspec/prophecy/tree/1.14.0" }, - "time": "2021-03-17T13:42:18+00:00" + "time": "2021-09-10T09:02:12+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.6", + "version": "9.2.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "f6293e1b30a2354e8428e004689671b83871edde" + "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde", - "reference": "f6293e1b30a2354e8428e004689671b83871edde", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218", + "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.10.2", + "nikic/php-parser": "^4.12.0", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -3893,7 +3894,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7" }, "funding": [ { @@ -3901,7 +3902,7 @@ "type": "github" } ], - "time": "2021-03-28T07:26:59+00:00" + "time": "2021-09-17T05:39:03+00:00" }, { "name": "phpunit/php-file-iterator", @@ -5313,16 +5314,16 @@ }, { "name": "symfony/console", - "version": "v5.3.6", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "51b71afd6d2dc8f5063199357b9880cea8d8bfe2" + "reference": "8b1008344647462ae6ec57559da166c2bfa5e16a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/51b71afd6d2dc8f5063199357b9880cea8d8bfe2", - "reference": "51b71afd6d2dc8f5063199357b9880cea8d8bfe2", + "url": "https://api.github.com/repos/symfony/console/zipball/8b1008344647462ae6ec57559da166c2bfa5e16a", + "reference": "8b1008344647462ae6ec57559da166c2bfa5e16a", "shasum": "" }, "require": { @@ -5392,7 +5393,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.3.6" + "source": "https://github.com/symfony/console/tree/v5.3.7" }, "funding": [ { @@ -5408,7 +5409,7 @@ "type": "tidelift" } ], - "time": "2021-07-27T19:10:22+00:00" + "time": "2021-08-25T20:02:16+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5882,16 +5883,16 @@ }, { "name": "symfony/string", - "version": "v5.3.3", + "version": "v5.3.7", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1" + "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1", - "reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1", + "url": "https://api.github.com/repos/symfony/string/zipball/8d224396e28d30f81969f083a58763b8b9ceb0a5", + "reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5", "shasum": "" }, "require": { @@ -5945,7 +5946,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.3.3" + "source": "https://github.com/symfony/string/tree/v5.3.7" }, "funding": [ { @@ -5961,7 +5962,7 @@ "type": "tidelift" } ], - "time": "2021-06-27T11:44:38+00:00" + "time": "2021-08-26T08:00:08+00:00" }, { "name": "theseer/tokenizer", @@ -6015,16 +6016,16 @@ }, { "name": "twig/twig", - "version": "v2.14.6", + "version": "v2.14.7", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "27e5cf2b05e3744accf39d4c68a3235d9966d260" + "reference": "8e202327ee1ed863629de9b18a5ec70ac614d88f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/27e5cf2b05e3744accf39d4c68a3235d9966d260", - "reference": "27e5cf2b05e3744accf39d4c68a3235d9966d260", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/8e202327ee1ed863629de9b18a5ec70ac614d88f", + "reference": "8e202327ee1ed863629de9b18a5ec70ac614d88f", "shasum": "" }, "require": { @@ -6034,7 +6035,7 @@ }, "require-dev": { "psr/container": "^1.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9" + "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" }, "type": "library", "extra": { @@ -6078,7 +6079,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v2.14.6" + "source": "https://github.com/twigphp/Twig/tree/v2.14.7" }, "funding": [ { @@ -6090,7 +6091,7 @@ "type": "tidelift" } ], - "time": "2021-05-16T12:12:47+00:00" + "time": "2021-09-17T08:39:54+00:00" }, { "name": "vimeo/psalm", diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 5095c5395..f032e6652 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -6,6 +6,8 @@ use Appwrite\Specification\Format; use Appwrite\Template\Template; use stdClass; use Utopia\Validator; +use function array_map; +use function var_dump; class OpenAPI3 extends Format { @@ -39,7 +41,13 @@ class OpenAPI3 extends Format } if (!is_object($model)) return; foreach ($model->getRules() as $rule) { - $this->getUsedModels($rule['type'], $usedModels); + if(\is_array($rule['type'])) { + foreach ($rule['type'] as $type) { + $this->getUsedModels($type, $usedModels); + } + } else { + $this->getUsedModels($rule['type'], $usedModels); + } } } @@ -430,12 +438,24 @@ class OpenAPI3 extends Format $type = 'object'; $rule['type'] = ($rule['type']) ? $rule['type'] : 'none'; - $items = [ - '$ref' => '#/components/schemas/'.$rule['type'], - ]; + if(\is_array($rule['type'])) { + $items = [ + 'oneOf' => \array_map(function($type) { + return ['$ref' => '#/components/schemas/'.$type]; + }, $rule['type']) + ]; + } else { + $items = [ + '$ref' => '#/components/schemas/'.$rule['type'], + ]; + } + + break; } + + if($rule['array']) { $output['components']['schemas'][$model->getType()]['properties'][$name] = [ 'type' => 'array', From a87d482e495a41c625a2ab4d7fc3ac2a89e7d4dc Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Tue, 21 Sep 2021 14:12:43 +0200 Subject: [PATCH 088/130] Swagger2 support for arrays (work-around) --- .../Specification/Format/OpenAPI3.php | 6 --- .../Specification/Format/Swagger2.php | 46 +++++++++++++------ 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index f032e6652..54e668fbe 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -6,8 +6,6 @@ use Appwrite\Specification\Format; use Appwrite\Template\Template; use stdClass; use Utopia\Validator; -use function array_map; -use function var_dump; class OpenAPI3 extends Format { @@ -449,13 +447,9 @@ class OpenAPI3 extends Format '$ref' => '#/components/schemas/'.$rule['type'], ]; } - - break; } - - if($rule['array']) { $output['components']['schemas'][$model->getType()]['properties'][$name] = [ 'type' => 'array', diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 2a3be45d1..5e14bd1f3 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -39,7 +39,13 @@ class Swagger2 extends Format } if (!is_object($model)) return; foreach ($model->getRules() as $rule) { - $this->getUsedModels($rule['type'], $usedModels); + if(\is_array($rule['type'])) { + foreach ($rule['type'] as $type) { + $this->getUsedModels($type, $usedModels); + } + } else { + $this->getUsedModels($rule['type'], $usedModels); + } } } @@ -91,15 +97,15 @@ class Swagger2 extends Format if (isset($output['securityDefinitions']['Project'])) { $output['securityDefinitions']['Project']['x-appwrite'] = ['demo' => '5df5acd0d48c2']; } - + if (isset($output['securityDefinitions']['Key'])) { $output['securityDefinitions']['Key']['x-appwrite'] = ['demo' => '919c2d18fb5d4...a2ae413da83346ad2']; } - + if (isset($output['securityDefinitions']['JWT'])) { $output['securityDefinitions']['JWT']['x-appwrite'] = ['demo' => 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...']; } - + if (isset($output['securityDefinitions']['Locale'])) { $output['securityDefinitions']['Locale']['x-appwrite'] = ['demo' => 'en']; } @@ -147,7 +153,7 @@ class Swagger2 extends Format if(empty($routeSecurity)) { $sdkPlatofrms[] = APP_PLATFORM_CLIENT; } - + $temp = [ 'summary' => $route->getDesc(), 'operationId' => $route->getLabel('sdk.namespace', 'default').ucfirst($id), @@ -216,7 +222,7 @@ class Swagger2 extends Format if ((!empty($scope))) { // && 'public' != $scope $securities = ['Project' => []]; - + foreach($route->getLabel('sdk.auth', []) as $security) { if(array_key_exists($security, $this->keys)) { $securities[$security] = []; @@ -226,7 +232,7 @@ class Swagger2 extends Format $temp['x-appwrite']['auth'] = array_slice($securities, 0, $this->authCount); $temp['security'][] = $securities; } - + $body = [ 'name' => 'payload', 'in' => 'body', @@ -399,7 +405,7 @@ class Swagger2 extends Format if($model->isAny()) { $output['definitions'][$model->getType()]['additionalProperties'] = true; } - + if(!empty($required)) { $output['definitions'][$model->getType()]['required'] = $required; } @@ -414,7 +420,7 @@ class Swagger2 extends Format case 'json': $type = 'string'; break; - + case 'integer': $type = 'integer'; $format = 'int32'; @@ -424,19 +430,29 @@ class Swagger2 extends Format $type = 'number'; $format = 'float'; break; - + case 'boolean': $type = 'boolean'; break; - + default: $type = 'object'; $rule['type'] = ($rule['type']) ? $rule['type'] : 'none'; - $items = [ - 'type' => $type, - '$ref' => '#/definitions/'.$rule['type'], - ]; + if(\is_array($rule['type'])) { + // THIS IS NOT SUPPORTED IN 2.0!!! +// $items = [ +// 'oneOf' => \array_map(function($type) { +// return ['$ref' => '#/definitions/'.$type]; +// }, $rule['type']) +// ]; + + $items = []; + } else { + $items = [ + '$ref' => '#/definitions/'.$rule['type'], + ]; + } break; } From b97542e581910e452da8a6d01a1be77282e7b5d3 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Tue, 21 Sep 2021 14:16:24 +0200 Subject: [PATCH 089/130] Swagger2 array fix --- src/Appwrite/Specification/Format/Swagger2.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 5e14bd1f3..493dac0bb 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -440,14 +440,11 @@ class Swagger2 extends Format $rule['type'] = ($rule['type']) ? $rule['type'] : 'none'; if(\is_array($rule['type'])) { - // THIS IS NOT SUPPORTED IN 2.0!!! -// $items = [ -// 'oneOf' => \array_map(function($type) { -// return ['$ref' => '#/definitions/'.$type]; -// }, $rule['type']) -// ]; - - $items = []; + $items = [ + 'oneOf' => \array_map(function($type) { + return ['$ref' => '#/definitions/'.$type]; + }, $rule['type']) + ]; } else { $items = [ '$ref' => '#/definitions/'.$rule['type'], From 1eeddc80151fb3bc2bf8ea393955134b4dd4566c Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Tue, 21 Sep 2021 14:19:25 +0200 Subject: [PATCH 090/130] This should not be removed --- src/Appwrite/Specification/Format/Swagger2.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 493dac0bb..70811536c 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -447,6 +447,7 @@ class Swagger2 extends Format ]; } else { $items = [ + 'type' => $type, '$ref' => '#/definitions/'.$rule['type'], ]; } From d0e777edd5e27e8ca8c7ea5b7668865c61090a50 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Tue, 21 Sep 2021 14:25:41 +0200 Subject: [PATCH 091/130] Swagger Double type fix --- src/Appwrite/Specification/Format/OpenAPI3.php | 7 ++++++- src/Appwrite/Specification/Format/Swagger2.php | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 54e668fbe..82eb69533 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -33,7 +33,7 @@ class OpenAPI3 extends Format */ protected function getUsedModels($model, array &$usedModels) { - if (is_string($model) && !in_array($model, ['string', 'integer', 'boolean', 'json', 'float'])) { + if (is_string($model) && !in_array($model, ['string', 'integer', 'boolean', 'json', 'float', 'double'])) { $usedModels[] = $model; return; } @@ -427,6 +427,11 @@ class OpenAPI3 extends Format $type = 'number'; $format = 'float'; break; + + case 'double': + $type = 'number'; + $format = 'double'; + break; case 'boolean': $type = 'boolean'; diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 70811536c..397b97f96 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -33,7 +33,7 @@ class Swagger2 extends Format */ protected function getUsedModels($model, array &$usedModels) { - if (is_string($model) && !in_array($model, ['string', 'integer', 'boolean', 'json', 'float'])) { + if (is_string($model) && !in_array($model, ['string', 'integer', 'boolean', 'json', 'float', 'double'])) { $usedModels[] = $model; return; } @@ -431,6 +431,11 @@ class Swagger2 extends Format $format = 'float'; break; + case 'double': + $type = 'number'; + $format = 'double'; + break; + case 'boolean': $type = 'boolean'; break; From 9c9a17a2a44ac06113e9ec54b1e9248425b16091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 21 Sep 2021 20:33:11 +0200 Subject: [PATCH 092/130] Apply suggestions from code review Co-authored-by: Torsten Dittmann --- src/Appwrite/Specification/Format/OpenAPI3.php | 2 +- src/Appwrite/Specification/Format/Swagger2.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 82eb69533..0c055fe70 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -443,7 +443,7 @@ class OpenAPI3 extends Format if(\is_array($rule['type'])) { $items = [ - 'oneOf' => \array_map(function($type) { + 'anyOf' => \array_map(function($type) { return ['$ref' => '#/components/schemas/'.$type]; }, $rule['type']) ]; diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 397b97f96..88cec0b2a 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -446,7 +446,7 @@ class Swagger2 extends Format if(\is_array($rule['type'])) { $items = [ - 'oneOf' => \array_map(function($type) { + 'anyOf' => \array_map(function($type) { return ['$ref' => '#/definitions/'.$type]; }, $rule['type']) ]; From c87e686ca2224b202dcf4da5b6cc25dfa43c72d8 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 22 Sep 2021 21:29:56 -0400 Subject: [PATCH 093/130] Fix createUrlAttribute description --- app/controllers/api/database.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index a031888ce..0451287a2 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -785,7 +785,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip') }); App::post('/v1/database/collections/:collectionId/attributes/url') - ->desc('Create IP Address Attribute') + ->desc('Create URL Attribute') ->groups(['api', 'database']) ->label('event', 'database.attributes.create') ->label('scope', 'collections.write') From 4bbf0e93037e03edd072c29f0432a68f45485cf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 23 Sep 2021 08:44:57 +0200 Subject: [PATCH 094/130] Remove unnecessary whitespace Co-authored-by: kodumbeats --- tests/e2e/Services/Storage/StorageBase.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index 8d9b2d9c8..c2f2483a5 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -210,7 +210,6 @@ trait StorageBase $this->assertCount(1, $response['body']['files']); $this->assertEquals('logo.png', $response['body']['files'][0]['name']); - $response = $this->client->call(Client::METHOD_GET, '/storage/files', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], From d43987fd7a569eb3470900be8f3ed26cce248758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 23 Sep 2021 08:49:45 +0200 Subject: [PATCH 095/130] Fix test assert bug Co-authored-by: kodumbeats --- tests/e2e/Services/Storage/StorageBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index c2f2483a5..2bd69ec1a 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -205,7 +205,7 @@ trait StorageBase ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertGreaterThan(0, $response['body']['sum']); + $this->assertEquals(1, $response['body']['sum']); $this->assertIsInt($response['body']['sum']); $this->assertCount(1, $response['body']['files']); $this->assertEquals('logo.png', $response['body']['files'][0]['name']); From 8f1847f87109c617dadd41f1bbe6a109199f329a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 23 Sep 2021 08:50:47 +0200 Subject: [PATCH 096/130] Test assert bug fix Co-authored-by: kodumbeats --- tests/e2e/Services/Storage/StorageBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index 2bd69ec1a..ecfc55d1a 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -218,7 +218,7 @@ trait StorageBase ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertGreaterThan(0, $response['body']['sum']); + $this->assertEquals(1, $response['body']['sum']); $this->assertIsInt($response['body']['sum']); $this->assertGreaterThan(0, $response['body']['files']); $this->assertEquals($data['fileId'], $response['body']['files'][0]['$id']); From 1f2f0c2443618366256522566d0645e4d5c98e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 23 Sep 2021 08:57:16 +0200 Subject: [PATCH 097/130] Apply suggestions from code review Co-authored-by: kodumbeats --- tests/e2e/Services/Storage/StorageBase.php | 2 +- tests/e2e/Services/Users/UsersBase.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index ecfc55d1a..ab1a08c68 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -220,7 +220,7 @@ trait StorageBase $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals(1, $response['body']['sum']); $this->assertIsInt($response['body']['sum']); - $this->assertGreaterThan(0, $response['body']['files']); + $this->assertCount(1, $response['body']['files']); $this->assertEquals($data['fileId'], $response['body']['files'][0]['$id']); /** diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php index 1fd4444b3..564b3e786 100644 --- a/tests/e2e/Services/Users/UsersBase.php +++ b/tests/e2e/Services/Users/UsersBase.php @@ -96,7 +96,7 @@ trait UsersBase $this->assertEquals($response['headers']['status-code'], 200); $this->assertNotEmpty($response['body']); $this->assertNotEmpty($response['body']['users']); - $this->assertGreaterThan(0, $response['body']['users']); + $this->assertCount(1, $response['body']['users']); $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ @@ -108,7 +108,7 @@ trait UsersBase $this->assertEquals($response['headers']['status-code'], 200); $this->assertNotEmpty($response['body']); $this->assertNotEmpty($response['body']['users']); - $this->assertGreaterThan(0, $response['body']['users']); + $this->assertCount(1, $response['body']['users']); $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ @@ -120,7 +120,7 @@ trait UsersBase $this->assertEquals($response['headers']['status-code'], 200); $this->assertNotEmpty($response['body']); $this->assertNotEmpty($response['body']['users']); - $this->assertGreaterThan(0, $response['body']['users']); + $this->assertCount(1, $response['body']['users']); $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ From c61b00995c643bc95a1fc968adcc40dbc21858cf Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Thu, 23 Sep 2021 09:01:10 +0200 Subject: [PATCH 098/130] Manual code review suggestion changes --- .../Functions/FunctionsCustomServerTest.php | 12 +++++++++-- .../Projects/ProjectsConsoleClientTest.php | 20 +++++++++++++------ tests/e2e/Services/Users/UsersBase.php | 4 ++-- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 62198293a..a0b0754b0 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -77,6 +77,10 @@ class FunctionsCustomServerTest extends Scope /** * Test for SUCCESS */ + + /** + * Test search queries + */ $response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -110,7 +114,9 @@ class FunctionsCustomServerTest extends Scope $this->assertCount(1, $response['body']['functions']); $this->assertEquals($response['body']['functions'][0]['$id'], $data['functionId']); - + /** + * Test pagination + */ $response = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -155,7 +161,6 @@ class FunctionsCustomServerTest extends Scope $this->assertCount(1, $response['body']['functions']); $this->assertEquals($response['body']['functions'][0]['name'], 'Test 2'); - return $data; } @@ -316,6 +321,9 @@ class FunctionsCustomServerTest extends Scope $this->assertIsArray($function['body']['tags']); $this->assertCount(1, $function['body']['tags']); + /** + * Test search queries + */ $function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/tags', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 5de323cc4..56daab688 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -87,6 +87,7 @@ class ProjectsConsoleClientTest extends Scope /** * Test for SUCCESS */ + $response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -97,6 +98,9 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals($id, $response['body']['projects'][0]['$id']); $this->assertEquals('Project Test', $response['body']['projects'][0]['name']); + /** + * Test search queries + */ $response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -104,9 +108,11 @@ class ProjectsConsoleClientTest extends Scope 'search' => $id ])); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertNotEmpty($response['body']); - $this->assertEquals('Project Test', $response['body']['projects'][0]['name']); + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals($response['body']['sum'], 1); + $this->assertIsArray($response['body']['projects']); + $this->assertCount(1, $response['body']['projects']); + $this->assertEquals($response['body']['projects'][0]['name'], 'Project Test'); $response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([ 'content-type' => 'application/json', @@ -115,9 +121,11 @@ class ProjectsConsoleClientTest extends Scope 'search' => 'Project Test' ])); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertNotEmpty($response['body']); - $this->assertEquals($id, $response['body']['projects'][0]['$id']); + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertEquals($response['body']['sum'], 1); + $this->assertIsArray($response['body']['projects']); + $this->assertCount(1, $response['body']['projects']); + $this->assertEquals($response['body']['projects'][0]['$id'], $data['projectId']); /** * Test after pagination diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php index 564b3e786..7db500ddc 100644 --- a/tests/e2e/Services/Users/UsersBase.php +++ b/tests/e2e/Services/Users/UsersBase.php @@ -132,7 +132,7 @@ trait UsersBase $this->assertEquals($response['headers']['status-code'], 200); $this->assertNotEmpty($response['body']); $this->assertNotEmpty($response['body']['users']); - $this->assertGreaterThan(0, $response['body']['users']); + $this->assertCount(1, $response['body']['users']); $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ @@ -144,7 +144,7 @@ trait UsersBase $this->assertEquals($response['headers']['status-code'], 200); $this->assertNotEmpty($response['body']); $this->assertNotEmpty($response['body']['users']); - $this->assertGreaterThan(0, $response['body']['users']); + $this->assertCount(1, $response['body']['users']); $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); } From c97b15325507b6eca319fca1fa38ab741be4e43a Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Thu, 23 Sep 2021 13:12:50 +0200 Subject: [PATCH 099/130] Implemented oneOf for non-array results with multiple types --- .../Specification/Format/OpenAPI3.php | 19 ++++++++++++++----- .../Specification/Format/Swagger2.php | 18 +++++++++++++----- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 0c055fe70..7bcfdd00a 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -6,6 +6,7 @@ use Appwrite\Specification\Format; use Appwrite\Template\Template; use stdClass; use Utopia\Validator; +use function var_dump; class OpenAPI3 extends Format { @@ -442,11 +443,19 @@ class OpenAPI3 extends Format $rule['type'] = ($rule['type']) ? $rule['type'] : 'none'; if(\is_array($rule['type'])) { - $items = [ - 'anyOf' => \array_map(function($type) { - return ['$ref' => '#/components/schemas/'.$type]; - }, $rule['type']) - ]; + if($rule['array']) { + $items = [ + 'anyOf' => \array_map(function($type) { + return ['$ref' => '#/components/schemas/'.$type]; + }, $rule['type']) + ]; + } else { + $items = [ + 'oneOf' => \array_map(function($type) { + return ['$ref' => '#/components/schemas/'.$type]; + }, $rule['type']) + ]; + } } else { $items = [ '$ref' => '#/components/schemas/'.$rule['type'], diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 88cec0b2a..66324567b 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -445,11 +445,19 @@ class Swagger2 extends Format $rule['type'] = ($rule['type']) ? $rule['type'] : 'none'; if(\is_array($rule['type'])) { - $items = [ - 'anyOf' => \array_map(function($type) { - return ['$ref' => '#/definitions/'.$type]; - }, $rule['type']) - ]; + if($rule['array']) { + $items = [ + 'anyOf' => \array_map(function($type) { + return ['$ref' => '#/definitions/'.$type]; + }, $rule['type']) + ]; + } else { + $items = [ + 'oneOf' => \array_map(function($type) { + return ['$ref' => '#/definitions/'.$type]; + }, $rule['type']) + ]; + } } else { $items = [ 'type' => $type, From 36f55c8726364fdda25c629647046ff6fd3f8acc Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Thu, 23 Sep 2021 13:13:22 +0200 Subject: [PATCH 100/130] Removed var dump --- src/Appwrite/Specification/Format/OpenAPI3.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 7bcfdd00a..1d462669c 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -6,7 +6,6 @@ use Appwrite\Specification\Format; use Appwrite\Template\Template; use stdClass; use Utopia\Validator; -use function var_dump; class OpenAPI3 extends Format { From add7a01fac568ab7e78220ef0659a1f6cbc6b44e Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Thu, 23 Sep 2021 15:40:05 +0200 Subject: [PATCH 101/130] Added database key for performence --- app/config/collections2.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/config/collections2.php b/app/config/collections2.php index 866d1cb1b..71dc7e9f6 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -715,6 +715,13 @@ $collections = [ 'lengths' => [1024], 'orders' => [Database::ORDER_ASC], ], + [ + '$id' => '_key_deleted_email', + 'type' => Database::INDEX_KEY, + 'attributes' => ['deleted', 'email'], + 'lengths' => [0, 1024], + 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC], + ], ], ], From d0b70a9dee75d1a1f6026f325e1cce6374a2ab5e Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Mon, 27 Sep 2021 12:12:42 +0200 Subject: [PATCH 102/130] Re-added function execution search + added missing tests --- app/config/collections2.php | 18 +++++++++++ app/controllers/api/functions.php | 24 +++++++++----- .../Functions/FunctionsCustomServerTest.php | 32 +++++++++++++++++++ 3 files changed, 66 insertions(+), 8 deletions(-) diff --git a/app/config/collections2.php b/app/config/collections2.php index ea99ae47d..da79e3684 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -1711,6 +1711,17 @@ $collections = [ 'array' => false, 'filters' => [], ], + [ + '$id' => 'search', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], ], 'indexes' => [ [ @@ -1720,6 +1731,13 @@ $collections = [ 'lengths' => [Database::LENGTH_KEY], 'orders' => [Database::ORDER_ASC], ], + [ + '$id' => '_fulltext_search', + 'type' => Database::INDEX_FULLTEXT, + 'attributes' => ['search'], + 'lengths' => [1024], + 'orders' => [Database::ORDER_ASC], + ], ], ], diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 28ff911c1..00ed18123 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -680,8 +680,10 @@ App::post('/v1/functions/:functionId/executions') Authorization::disable(); + $executionId = $dbForInternal->getId(); + $execution = $dbForInternal->createDocument('executions', new Document([ - '$id' => $dbForInternal->getId(), + '$id' => $executionId, '$read' => (!$user->isEmpty()) ? ['user:' . $user->getId()] : [], '$write' => [], 'dateCreated' => time(), @@ -693,6 +695,7 @@ App::post('/v1/functions/:functionId/executions') 'stdout' => '', 'stderr' => '', 'time' => 0.0, + 'search' => implode(' ', [$functionId, $executionId]), ])); Authorization::reset(); @@ -747,10 +750,11 @@ App::get('/v1/functions/:functionId/executions') ->param('functionId', '', new UID(), 'Function unique ID.') ->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true) ->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true) + ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->param('after', '', new UID(), 'ID of the execution used as the starting point for the query, excluding the execution itself. Should be used for efficient pagination when working with large sets of data.', true) ->inject('response') ->inject('dbForInternal') - ->action(function ($functionId, $limit, $offset, $after, $response, $dbForInternal) { + ->action(function ($functionId, $limit, $offset, $search, $after, $response, $dbForInternal) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ @@ -770,13 +774,17 @@ App::get('/v1/functions/:functionId/executions') } } - $results = $dbForInternal->find('executions', [ - new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]), - ], $limit, $offset, [], [Database::ORDER_DESC], $afterExecution ?? null); + $queries = [ + new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]) + ]; - $sum = $dbForInternal->count('executions', [ - new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]), - ], APP_LIMIT_COUNT); + if (!empty($search)) { + $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); + } + + $results = $dbForInternal->find('executions', $queries, $limit, $offset, [], [Database::ORDER_DESC], $afterExecution ?? null); + + $sum = $dbForInternal->count('executions', $queries, APP_LIMIT_COUNT); $response->dynamic(new Document([ 'executions' => $results, diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index a0b0754b0..aa6413388 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -7,6 +7,8 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; +use function array_merge; +use function var_dump; class FunctionsCustomServerTest extends Scope { @@ -474,6 +476,36 @@ class FunctionsCustomServerTest extends Scope $this->assertCount(1, $function['body']['executions']); $this->assertEquals($function['body']['executions'][0]['$id'], $data['executionId']); + /** + * Test search queries + */ + + $response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $data['executionId'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(1, $response['body']['sum']); + $this->assertIsInt($response['body']['sum']); + $this->assertCount(1, $response['body']['executions']); + $this->assertEquals($data['functionId'], $response['body']['executions'][0]['functionId']); + + $response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/executions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $data['functionId'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(1, $response['body']['sum']); + $this->assertIsInt($response['body']['sum']); + $this->assertCount(1, $response['body']['executions']); + $this->assertEquals($data['executionId'], $response['body']['executions'][0]['$id']); + return $data; } From 24f9c2518bf54f68c15329abb46f58f15fd31054 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Mon, 27 Sep 2021 12:20:32 +0200 Subject: [PATCH 103/130] Removed imports --- tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index aa6413388..57b8ad921 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -7,8 +7,6 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; -use function array_merge; -use function var_dump; class FunctionsCustomServerTest extends Scope { From 429da848c6ccb1ee41f15dbe82c2b023f42b6989 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Mon, 27 Sep 2021 14:11:06 +0200 Subject: [PATCH 104/130] Added missing tests for user deprecation feature --- composer.lock | 12 ++--- docker-compose.yml | 2 +- .../Services/Users/UsersCustomServerTest.php | 48 +++++++++++++++++++ 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/composer.lock b/composer.lock index c319daa5a..6e2046e5e 100644 --- a/composer.lock +++ b/composer.lock @@ -2576,16 +2576,16 @@ "packages-dev": [ { "name": "amphp/amp", - "version": "v2.6.0", + "version": "v2.6.1", "source": { "type": "git", "url": "https://github.com/amphp/amp.git", - "reference": "caa95edeb1ca1bf7532e9118ede4a3c3126408cc" + "reference": "c5fc66a78ee38d7ac9195a37bacaf940eb3f65ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/caa95edeb1ca1bf7532e9118ede4a3c3126408cc", - "reference": "caa95edeb1ca1bf7532e9118ede4a3c3126408cc", + "url": "https://api.github.com/repos/amphp/amp/zipball/c5fc66a78ee38d7ac9195a37bacaf940eb3f65ae", + "reference": "c5fc66a78ee38d7ac9195a37bacaf940eb3f65ae", "shasum": "" }, "require": { @@ -2653,7 +2653,7 @@ "support": { "irc": "irc://irc.freenode.org/amphp", "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.0" + "source": "https://github.com/amphp/amp/tree/v2.6.1" }, "funding": [ { @@ -2661,7 +2661,7 @@ "type": "github" } ], - "time": "2021-07-16T20:06:06+00:00" + "time": "2021-09-23T18:43:08+00:00" }, { "name": "amphp/byte-stream", diff --git a/docker-compose.yml b/docker-compose.yml index 539799aa9..27c2a125d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,7 +63,7 @@ services: - ./psalm.xml:/usr/src/code/psalm.xml - ./tests:/usr/src/code/tests - ./app:/usr/src/code/app - # - ./vendor/utopia-php/database:/usr/src/code/vendor/utopia-php/database + - ./vendor:/usr/src/code/vendor - ./docs:/usr/src/code/docs - ./public:/usr/src/code/public - ./src:/usr/src/code/src diff --git a/tests/e2e/Services/Users/UsersCustomServerTest.php b/tests/e2e/Services/Users/UsersCustomServerTest.php index c5e4ff8c1..3acd4330a 100644 --- a/tests/e2e/Services/Users/UsersCustomServerTest.php +++ b/tests/e2e/Services/Users/UsersCustomServerTest.php @@ -2,6 +2,7 @@ namespace Tests\E2E\Services\Users; +use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; @@ -11,4 +12,51 @@ class UsersCustomServerTest extends Scope use UsersBase; use ProjectCustom; use SideServer; + + public function testDeprecatedUsers():array + { + /** + * Test for FAILURE (don't allow recreating account with same custom ID) + */ + + // Create user with custom ID 'meldiron' + $response = $this->client->call(Client::METHOD_POST, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'userId' => 'meldiron', + 'email' => 'matej@appwrite.io', + 'password' => 'my-superstr0ng-password', + 'name' => 'Matej Bačo' + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // Delete user with custom ID 'meldiron' + $response = $this->client->call(Client::METHOD_DELETE, '/users/meldiron', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + + ]); + + $this->assertEquals(204, $response['headers']['status-code']); + + // Try to create user with custom ID 'meldiron' again, but now it should fail + $response1 = $this->client->call(Client::METHOD_POST, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'userId' => 'meldiron', + 'email' => 'matej@appwrite.io', + 'password' => 'my-superstr0ng-password', + 'name' => 'Matej Bačo' + ]); + + $this->assertEquals(409, $response1['headers']['status-code']); + $this->assertEquals('Account already exists', $response1['body']['message']); + + return []; + } + } \ No newline at end of file From f4d07e826b34129c0baf7123487d221a419e2310 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 27 Sep 2021 19:38:03 -0400 Subject: [PATCH 105/130] Fix tests by testing cleanup duplicate behavior on fresh collection --- .../Database/DatabaseCustomServerTest.php | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/tests/e2e/Services/Database/DatabaseCustomServerTest.php b/tests/e2e/Services/Database/DatabaseCustomServerTest.php index fd4a12b5a..50d8af29f 100644 --- a/tests/e2e/Services/Database/DatabaseCustomServerTest.php +++ b/tests/e2e/Services/Database/DatabaseCustomServerTest.php @@ -336,12 +336,26 @@ class DatabaseCustomServerTest extends Scope return $data; } - /** - * @depends testDeleteIndex - */ - public function testCleanupDuplicateIndexOnDeleteAttribute($data) + public function testCleanupDuplicateIndexOnDeleteAttribute() { - $attribute1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/attributes/string', array_merge([ + $collection = $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'] + ]), [ + 'collectionId' => 'unique()', + 'name' => 'TestCleanupDuplicateIndexOnDeleteAttribute', + 'read' => ['role:all'], + 'write' => ['role:all'], + 'permission' => 'document', + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + $this->assertNotEmpty($collection['body']['$id']); + + $collectionId = $collection['body']['$id']; + + $attribute1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -351,7 +365,7 @@ class DatabaseCustomServerTest extends Scope 'required' => true, ]); - $attribute2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/attributes/string', array_merge([ + $attribute2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -368,7 +382,7 @@ class DatabaseCustomServerTest extends Scope sleep(2); - $index1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/indexes', array_merge([ + $index1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/indexes', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -379,7 +393,7 @@ class DatabaseCustomServerTest extends Scope 'orders' => ['ASC', 'ASC'], ]); - $index2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/indexes', array_merge([ + $index2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/indexes', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -397,7 +411,7 @@ class DatabaseCustomServerTest extends Scope sleep(2); // Expected behavior: deleting attribute1 would cause index1 to be a duplicate of index2 and automatically removed - $deleted = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/attributes/'. $attribute1['body']['key'], array_merge([ + $deleted = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $collectionId . '/attributes/'. $attribute1['body']['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -408,7 +422,7 @@ class DatabaseCustomServerTest extends Scope // wait for database worker to complete sleep(2); - $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['collectionId'], array_merge([ + $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] @@ -423,15 +437,13 @@ class DatabaseCustomServerTest extends Scope $this->assertEquals($attribute2['body']['key'], $collection['body']['indexes'][0]['attributes'][0]); // Delete attribute - $deleted = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/attributes/' . $attribute2['body']['key'], array_merge([ + $deleted = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $collectionId . '/attributes/' . $attribute2['body']['key'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ])); $this->assertEquals($deleted['headers']['status-code'], 204); - - return $data; } /** From 8fb83d9605dcced707ef7af1df835c8d96616da1 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Tue, 28 Sep 2021 08:51:53 +0200 Subject: [PATCH 106/130] Fixed column width --- app/config/collections2.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/config/collections2.php b/app/config/collections2.php index 71dc7e9f6..72b8e0067 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -578,7 +578,7 @@ $collections = [ '$id' => 'email', 'type' => Database::VAR_STRING, 'format' => '', - 'size' => 1024, + 'size' => 320, 'signed' => true, 'required' => false, 'default' => null, @@ -712,14 +712,14 @@ $collections = [ '$id' => '_key_email', 'type' => Database::INDEX_UNIQUE, 'attributes' => ['email'], - 'lengths' => [1024], + 'lengths' => [320], 'orders' => [Database::ORDER_ASC], ], [ '$id' => '_key_deleted_email', 'type' => Database::INDEX_KEY, 'attributes' => ['deleted', 'email'], - 'lengths' => [0, 1024], + 'lengths' => [0, 320], 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC], ], ], From 73c0b23680c310cdde8ab8010d9599cf07db1210 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Thu, 30 Sep 2021 11:59:01 +0200 Subject: [PATCH 107/130] Swagger (2,3) support for multiple types of response --- app/controllers/web/home.php | 9 +- .../Specification/Format/OpenAPI3.php | 86 +++++++++++++------ .../Specification/Format/Swagger2.php | 59 ++++++++++--- 3 files changed, 114 insertions(+), 40 deletions(-) diff --git a/app/controllers/web/home.php b/app/controllers/web/home.php index 1d9e86d87..9f4d2412a 100644 --- a/app/controllers/web/home.php +++ b/app/controllers/web/home.php @@ -387,11 +387,10 @@ App::get('/specs/:format') } $routes[] = $route; - $model = $response->getModel($route->getLabel('sdk.response.model', 'none')); - - if($model) { - $models[$model->getType()] = $model; - } + $modelLabel = $route->getLabel('sdk.response.model', 'none'); + $model = \is_array($modelLabel) ? \array_map(function($m) use($response) { + return $response->getModel($m); + }, $modelLabel) : $response->getModel($modelLabel); } } diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 1d462669c..b0a61114c 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -6,6 +6,7 @@ use Appwrite\Specification\Format; use Appwrite\Template\Template; use stdClass; use Utopia\Validator; +use function gettype; class OpenAPI3 extends Format { @@ -25,14 +26,14 @@ class OpenAPI3 extends Format * Get Used Models * * Recursively get all used models - * + * * @param object $model * @param array $models * * @return void */ protected function getUsedModels($model, array &$usedModels) - { + { if (is_string($model) && !in_array($model, ['string', 'integer', 'boolean', 'json', 'float', 'double'])) { $usedModels[] = $model; return; @@ -99,7 +100,7 @@ class OpenAPI3 extends Format if (isset($output['components']['securitySchemes']['Project'])) { $output['components']['securitySchemes']['Project']['x-appwrite'] = ['demo' => '5df5acd0d48c2']; } - + if (isset($output['components']['securitySchemes']['Key'])) { $output['components']['securitySchemes']['Key']['x-appwrite'] = ['demo' => '919c2d18fb5d4...a2ae413da83346ad2']; } @@ -107,7 +108,7 @@ class OpenAPI3 extends Format if (isset($output['securityDefinitions']['JWT'])) { $output['securityDefinitions']['JWT']['x-appwrite'] = ['demo' => 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...']; } - + if (isset($output['components']['securitySchemes']['Locale'])) { $output['components']['securitySchemes']['Locale']['x-appwrite'] = ['demo' => 'en']; } @@ -131,7 +132,7 @@ class OpenAPI3 extends Format $id = $route->getLabel('sdk.method', \uniqid()); $desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath(__DIR__.'/../../../../'.$route->getLabel('sdk.description', '')) : null; $produces = $route->getLabel('sdk.response.type', null); - $model = $route->getLabel('sdk.response.model', 'none'); + $model = $route->getLabel('sdk.response.model', 'none'); $routeSecurity = $route->getLabel('sdk.auth', []); $sdkPlatofrms = []; @@ -155,7 +156,7 @@ class OpenAPI3 extends Format if(empty($routeSecurity)) { $sdkPlatofrms[] = APP_PLATFORM_CLIENT; } - + $temp = [ 'summary' => $route->getDesc(), 'operationId' => $route->getLabel('sdk.namespace', 'default').ucfirst($id), @@ -181,13 +182,24 @@ class OpenAPI3 extends Format ]; foreach ($this->models as $key => $value) { - if($value->getType() === $model) { - $model = $value; - break; + if(\is_array($model)) { + $model = \array_map(function($m) use($value) { + if($m === $value->getType()) { + return $value; + } + + return $m; + }, $model); + } else { + if($value->getType() === $model) { + $model = $value; + break; + } } + } - if($model->isNone()) { + if(!(\is_array($model)) && $model->isNone()) { $temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [ 'description' => (in_array($produces, [ 'image/*', @@ -204,17 +216,43 @@ class OpenAPI3 extends Format // ], ]; } else { - $usedModels[] = $model->getType(); - $temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [ - 'description' => $model->getName(), - 'content' => [ - $produces => [ - 'schema' => [ - '$ref' => '#/components/schemas/'.$model->getType(), + if(\is_array($model)) { + $modelDescription = \join(', or ', \array_map(function ($m) { + return $m->getName(); + }, $model)); + + // model has multiple possible responses, we will use oneOf + foreach ($model as $m) { + $usedModels[] = $m->getType(); + } + + $temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [ + 'description' => $modelDescription, + 'content' => [ + $produces => [ + 'schema' => [ + 'oneOf' => \array_map(function($m) { + return ['$ref' => '#/components/schemas/'.$m->getType()]; + }, $model) + ], ], ], - ], - ]; + ]; + } else { + // Response definition using one type + $usedModels[] = $model->getType(); + $temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [ + 'description' => $model->getName(), + 'content' => [ + $produces => [ + 'schema' => [ + '$ref' => '#/components/schemas/'.$model->getType(), + ], + ], + ], + ]; + } + } if($route->getLabel('sdk.response.code', 500) === 204) { @@ -224,7 +262,7 @@ class OpenAPI3 extends Format if ((!empty($scope))) { // && 'public' != $scope $securities = ['Project' => []]; - + foreach($route->getLabel('sdk.auth', []) as $security) { if(array_key_exists($security, $this->keys)) { $securities[$security] = []; @@ -402,7 +440,7 @@ class OpenAPI3 extends Format if($model->isAny()) { $output['components']['schemas'][$model->getType()]['additionalProperties'] = true; } - + if(!empty($required)) { $output['components']['schemas'][$model->getType()]['required'] = $required; } @@ -417,7 +455,7 @@ class OpenAPI3 extends Format case 'json': $type = 'string'; break; - + case 'integer': $type = 'integer'; $format = 'int32'; @@ -432,11 +470,11 @@ class OpenAPI3 extends Format $type = 'number'; $format = 'double'; break; - + case 'boolean': $type = 'boolean'; break; - + default: $type = 'object'; $rule['type'] = ($rule['type']) ? $rule['type'] : 'none'; diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 66324567b..a8dcffb1e 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -183,13 +183,22 @@ class Swagger2 extends Format } foreach ($this->models as $key => $value) { - if($value->getType() === $model) { - $model = $value; - break; + if(\is_array($model)) { + $model = \array_map(function($m) use($value) { + if($m === $value->getType()) { + return $value; + } + return $m; + }, $model); + } else { + if($value->getType() === $model) { + $model = $value; + break; + } } } - if($model->isNone()) { + if(!(\is_array($model)) && $model->isNone()) { $temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [ 'description' => (in_array($produces, [ 'image/*', @@ -206,13 +215,41 @@ class Swagger2 extends Format ], ]; } else { - $usedModels[] = $model->getType(); - $temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [ - 'description' => $model->getName(), - 'schema' => [ - '$ref' => '#/definitions/'.$model->getType(), - ], - ]; + + if(\is_array($model)) { + $modelDescription = \join(', or ', \array_map(function ($m) { + return $m->getName(); + }, $model)); + // model has multiple possible responses, we will use oneOf + foreach ($model as $m) { + $usedModels[] = $m->getType(); + } + $temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [ + 'description' => $modelDescription, + 'content' => [ + $produces => [ + 'schema' => [ + 'oneOf' => \array_map(function($m) { + return ['$ref' => '#/definitions/'.$m->getType()]; + }, $model) + ], + ], + ], + ]; + } else { + // Response definition using one type + $usedModels[] = $model->getType(); + $temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [ + 'description' => $model->getName(), + 'content' => [ + $produces => [ + 'schema' => [ + '$ref' => '#/definitions/'.$model->getType(), + ], + ], + ], + ]; + } } if(in_array($route->getLabel('sdk.response.code', 500), [204, 301, 302, 308], true)) { From dd04158ae1baad6e940393484f33e8e1261623f2 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Thu, 30 Sep 2021 15:03:18 -0400 Subject: [PATCH 108/130] Return oneOf models for getAttribute --- app/controllers/api/database.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index a031888ce..2973adc40 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -1040,7 +1040,14 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') ->label('sdk.description', '/docs/references/database/get-attribute.md') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_ATTRIBUTE) + ->label('sdk.response.model', [ + Response::MODEL_ATTRIBUTE_BOOLEAN, + Response::MODEL_ATTRIBUTE_INTEGER, + Response::MODEL_ATTRIBUTE_FLOAT, + Response::MODEL_ATTRIBUTE_EMAIL, + Response::MODEL_ATTRIBUTE_URL, + Response::MODEL_ATTRIBUTE_IP, + Response::MODEL_ATTRIBUTE_STRING,])// needs to be last, since its condition would dominate any other string attribute ->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).') ->param('attributeId', '', new Key(), 'Attribute ID.') ->inject('response') From 7ca035960dc9019dcaca2b1fa95fe751ec59d155 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Thu, 30 Sep 2021 15:37:21 -0400 Subject: [PATCH 109/130] Fix attribute response model spec definitions --- app/controllers/api/database.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 0451287a2..950095b11 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -663,7 +663,7 @@ App::post('/v1/database/collections/:collectionId/attributes/string') ->label('sdk.description', '/docs/references/database/create-attribute-string.md') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_ATTRIBUTE) + ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_STRING) ->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).') ->param('attributeId', '', new Key(), 'Attribute ID.') ->param('size', null, new Integer(), 'Attribute size for text attributes, in number of characters.') @@ -711,7 +711,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email') ->label('sdk.description', '/docs/references/database/create-attribute-email.md') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_ATTRIBUTE) + ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_EMAIL) ->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).') ->param('attributeId', '', new Key(), 'Attribute ID.') ->param('required', null, new Boolean(), 'Is attribute required?') @@ -753,7 +753,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip') ->label('sdk.description', '/docs/references/database/create-attribute-ip.md') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_ATTRIBUTE) + ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_IP) ->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).') ->param('attributeId', '', new Key(), 'Attribute ID.') ->param('required', null, new Boolean(), 'Is attribute required?') @@ -795,7 +795,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url') ->label('sdk.description', '/docs/references/database/create-attribute-url.md') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_ATTRIBUTE) + ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_URL) ->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).') ->param('attributeId', '', new Key(), 'Attribute ID.') ->param('required', null, new Boolean(), 'Is attribute required?') @@ -837,7 +837,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer') ->label('sdk.description', '/docs/references/database/create-attribute-integer.md') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_ATTRIBUTE) + ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_INTEGER) ->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).') ->param('attributeId', '', new Key(), 'Attribute ID.') ->param('required', null, new Boolean(), 'Is attribute required?') @@ -901,7 +901,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float') ->label('sdk.description', '/docs/references/database/create-attribute-float.md') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_ATTRIBUTE) + ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_FLOAT) ->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).') ->param('attributeId', '', new Key(), 'Attribute ID.') ->param('required', null, new Boolean(), 'Is attribute required?') @@ -965,7 +965,7 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean') ->label('sdk.description', '/docs/references/database/create-attribute-boolean.md') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_ATTRIBUTE) + ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_BOOLEAN) ->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).') ->param('attributeId', '', new Key(), 'Attribute ID.') ->param('required', null, new Boolean(), 'Is attribute required?') From 6ada84cb53f91eb733cc729ed669c551075d95d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 4 Oct 2021 12:58:33 +0200 Subject: [PATCH 110/130] Update tests/e2e/Services/Users/UsersCustomServerTest.php Co-authored-by: kodumbeats --- tests/e2e/Services/Users/UsersCustomServerTest.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/e2e/Services/Users/UsersCustomServerTest.php b/tests/e2e/Services/Users/UsersCustomServerTest.php index 3acd4330a..9d7e60a73 100644 --- a/tests/e2e/Services/Users/UsersCustomServerTest.php +++ b/tests/e2e/Services/Users/UsersCustomServerTest.php @@ -36,9 +36,7 @@ class UsersCustomServerTest extends Scope $response = $this->client->call(Client::METHOD_DELETE, '/users/meldiron', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - - ]); + ], $this->getHeaders())); $this->assertEquals(204, $response['headers']['status-code']); From b068f4969cde06c8e5a7a095238f4642626bbc1d Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Mon, 4 Oct 2021 13:01:08 +0200 Subject: [PATCH 111/130] Review changes --- docker-compose.yml | 2 +- tests/e2e/Services/Users/UsersCustomServerTest.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 27c2a125d..ade61dc1f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,7 +63,7 @@ services: - ./psalm.xml:/usr/src/code/psalm.xml - ./tests:/usr/src/code/tests - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor + # - ./vendor:/usr/src/code/vendor - ./docs:/usr/src/code/docs - ./public:/usr/src/code/public - ./src:/usr/src/code/src diff --git a/tests/e2e/Services/Users/UsersCustomServerTest.php b/tests/e2e/Services/Users/UsersCustomServerTest.php index 9d7e60a73..cee654e6c 100644 --- a/tests/e2e/Services/Users/UsersCustomServerTest.php +++ b/tests/e2e/Services/Users/UsersCustomServerTest.php @@ -46,9 +46,9 @@ class UsersCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'userId' => 'meldiron', - 'email' => 'matej@appwrite.io', - 'password' => 'my-superstr0ng-password', - 'name' => 'Matej Bačo' + 'email' => 'matej2@appwrite.io', + 'password' => 'someones-superstr0ng-password', + 'name' => 'Matej Bačo Second' ]); $this->assertEquals(409, $response1['headers']['status-code']); From 7497540db0c9c7a48dbb618b2679e9d371e7067a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 4 Oct 2021 13:02:54 +0200 Subject: [PATCH 112/130] Update app/config/collections2.php Co-authored-by: kodumbeats --- app/config/collections2.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/config/collections2.php b/app/config/collections2.php index da79e3684..bab2414ae 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -1735,7 +1735,7 @@ $collections = [ '$id' => '_fulltext_search', 'type' => Database::INDEX_FULLTEXT, 'attributes' => ['search'], - 'lengths' => [1024], + 'lengths' => [16384], 'orders' => [Database::ORDER_ASC], ], ], From 22132ef42004cc56b7a846444dcd82f6636c6199 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 5 Oct 2021 09:05:19 -0400 Subject: [PATCH 113/130] Update to utopia-php/database:0.10.0 --- composer.json | 11 +++------ composer.lock | 63 ++++++++++++++++++++++++--------------------------- 2 files changed, 33 insertions(+), 41 deletions(-) diff --git a/composer.json b/composer.json index b251fb13a..b3d21a4b0 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "utopia-php/cache": "0.4.*", "utopia-php/cli": "0.11.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "dev-feat-adjust-encodeAttribute as 0.10.0", + "utopia-php/database": "0.10.0", "utopia-php/locale": "0.4.*", "utopia-php/registry": "0.5.*", "utopia-php/preloader": "0.2.*", @@ -63,12 +63,7 @@ "adhocore/jwt": "1.1.2", "slickdeals/statsd": "3.1.0" }, - "repositories": [ - { - "type": "git", - "url": "https://github.com/utopia-php/database" - } - ], + "repositories": [], "require-dev": { "appwrite/sdk-generator": "0.13.0", "swoole/ide-helper": "4.6.7", @@ -83,4 +78,4 @@ "php": "8.0" } } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 27c2af756..7eee04c86 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "31670d6cc60a22007b4ed59a7dc9448f", + "content-hash": "dfb8fa19daa736b3687617c98f309983", "packages": [ { "name": "adhocore/jwt", @@ -1984,11 +1984,17 @@ }, { "name": "utopia-php/database", - "version": "dev-feat-adjust-encodeAttribute", + "version": "0.10.0", "source": { "type": "git", - "url": "https://github.com/utopia-php/database", - "reference": "5ef32ec85143daf78796e7826453244d24f8a86a" + "url": "https://github.com/utopia-php/database.git", + "reference": "b7c60b0ec769a9050dd2b939b78ff1f5d4fa27e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/database/zipball/b7c60b0ec769a9050dd2b939b78ff1f5d4fa27e8", + "reference": "b7c60b0ec769a9050dd2b939b78ff1f5d4fa27e8", + "shasum": "" }, "require": { "ext-mongodb": "*", @@ -2011,11 +2017,7 @@ "Utopia\\Database\\": "src/Database" } }, - "autoload-dev": { - "psr-4": { - "Utopia\\Tests\\": "tests/Database" - } - }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2037,7 +2039,11 @@ "upf", "utopia" ], - "time": "2021-08-27T20:39:51+00:00" + "support": { + "issues": "https://github.com/utopia-php/database/issues", + "source": "https://github.com/utopia-php/database/tree/0.10.0" + }, + "time": "2021-10-04T17:23:25+00:00" }, { "name": "utopia-php/domains", @@ -2576,16 +2582,16 @@ "packages-dev": [ { "name": "amphp/amp", - "version": "v2.6.0", + "version": "v2.6.1", "source": { "type": "git", "url": "https://github.com/amphp/amp.git", - "reference": "caa95edeb1ca1bf7532e9118ede4a3c3126408cc" + "reference": "c5fc66a78ee38d7ac9195a37bacaf940eb3f65ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/caa95edeb1ca1bf7532e9118ede4a3c3126408cc", - "reference": "caa95edeb1ca1bf7532e9118ede4a3c3126408cc", + "url": "https://api.github.com/repos/amphp/amp/zipball/c5fc66a78ee38d7ac9195a37bacaf940eb3f65ae", + "reference": "c5fc66a78ee38d7ac9195a37bacaf940eb3f65ae", "shasum": "" }, "require": { @@ -2653,7 +2659,7 @@ "support": { "irc": "irc://irc.freenode.org/amphp", "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.0" + "source": "https://github.com/amphp/amp/tree/v2.6.1" }, "funding": [ { @@ -2661,7 +2667,7 @@ "type": "github" } ], - "time": "2021-07-16T20:06:06+00:00" + "time": "2021-09-23T18:43:08+00:00" }, { "name": "amphp/byte-stream", @@ -3712,16 +3718,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f" + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/30f38bffc6f24293dadd1823936372dfa9e86e2f", - "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae", + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae", "shasum": "" }, "require": { @@ -3756,9 +3762,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1" }, - "time": "2021-09-17T15:28:14+00:00" + "time": "2021-10-02T14:08:47+00:00" }, { "name": "phpspec/prophecy", @@ -6249,18 +6255,9 @@ "time": "2015-12-17T08:42:14+00:00" } ], - "aliases": [ - { - "package": "utopia-php/database", - "version": "dev-feat-adjust-encodeAttribute", - "alias": "0.10.0", - "alias_normalized": "0.10.0.0" - } - ], + "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "utopia-php/database": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { From 481ff1e2f969eecc306cbe5d360595a0ffe459c0 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 5 Oct 2021 09:05:40 -0400 Subject: [PATCH 114/130] Refactor purgeDocument calls to deleteCachedDocument --- app/controllers/api/database.php | 8 ++++---- app/workers/database.php | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 6d7268c24..0b288a9ab 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -96,7 +96,7 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $ throw new Exception('Attribute already exists', 409); } - $dbForInternal->purgeDocument('collections', $collectionId); + $dbForInternal->deleteCachedDocument('collections', $collectionId); // Pass clone of $attribute object to workers // so we can later modify Document to fit response model @@ -1131,7 +1131,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId') } $attribute = $dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'deleting')); - $dbForInternal->purgeDocument('collections', $collectionId); + $dbForInternal->deleteCachedDocument('collections', $collectionId); $database ->setParam('type', DATABASE_TYPE_DELETE_ATTRIBUTE) @@ -1238,7 +1238,7 @@ App::post('/v1/database/collections/:collectionId/indexes') throw new Exception('Index already exists', 409); } - $dbForInternal->purgeDocument('collections', $collectionId); + $dbForInternal->deleteCachedDocument('collections', $collectionId); $database ->setParam('type', DATABASE_TYPE_CREATE_INDEX) @@ -1383,7 +1383,7 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId') } $index = $dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'deleting')); - $dbForInternal->purgeDocument('collections', $collectionId); + $dbForInternal->deleteCachedDocument('collections', $collectionId); $database ->setParam('type', DATABASE_TYPE_DELETE_INDEX) diff --git a/app/workers/database.php b/app/workers/database.php index 2366b3799..fb841c644 100644 --- a/app/workers/database.php +++ b/app/workers/database.php @@ -94,7 +94,7 @@ class DatabaseV1 extends Worker $dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed')); } - $dbForInternal->purgeDocument('collections', $collectionId); + $dbForInternal->deleteCachedDocument('collections', $collectionId); } /** @@ -120,7 +120,7 @@ class DatabaseV1 extends Worker $dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed')); } - $dbForInternal->purgeDocument('collections', $collectionId); + $dbForInternal->deleteCachedDocument('collections', $collectionId); } /** @@ -150,7 +150,7 @@ class DatabaseV1 extends Worker $dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'failed')); } - $dbForInternal->purgeDocument('collections', $collectionId); + $dbForInternal->deleteCachedDocument('collections', $collectionId); } /** @@ -177,6 +177,6 @@ class DatabaseV1 extends Worker $dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'failed')); } - $dbForInternal->purgeDocument('collections', $collectionId); + $dbForInternal->deleteCachedDocument('collections', $collectionId); } } From dc06e42348e4f0ae5f7ad9355ab13caf9d47a2dc Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 5 Oct 2021 09:16:27 -0400 Subject: [PATCH 115/130] Remove standard lib imports --- src/Appwrite/Specification/Format/OpenAPI3.php | 2 -- src/Appwrite/Utopia/Response/Model/Collection.php | 1 - 2 files changed, 3 deletions(-) diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index b0a61114c..8d375d2f9 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -4,9 +4,7 @@ namespace Appwrite\Specification\Format; use Appwrite\Specification\Format; use Appwrite\Template\Template; -use stdClass; use Utopia\Validator; -use function gettype; class OpenAPI3 extends Format { diff --git a/src/Appwrite/Utopia/Response/Model/Collection.php b/src/Appwrite/Utopia/Response/Model/Collection.php index e52539691..e46603fd9 100644 --- a/src/Appwrite/Utopia/Response/Model/Collection.php +++ b/src/Appwrite/Utopia/Response/Model/Collection.php @@ -5,7 +5,6 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; -use stdClass; class Collection extends Model { From 0f5931555aa3024e2d1b6e68b4d9610a5e2a24d2 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 5 Oct 2021 09:57:57 -0400 Subject: [PATCH 116/130] Fix issues from merge --- app/controllers/api/database.php | 5 ++--- src/Appwrite/Utopia/Response/Model/Collection.php | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index ecbc896d0..255293c04 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -77,7 +77,7 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $ } try { - $attribute = $dbForInternal->createDocument('attributes', new Document([ + $attribute = new Document([ '$id' => $collectionId.'_'.$attributeId, 'key' => $attributeId, 'collectionId' => $collectionId, @@ -90,9 +90,8 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $ 'array' => $array, 'format' => $format, 'formatOptions' => $formatOptions, - ]); + ]); - try { $dbForInternal->checkAttribute($collection, $attribute); $attribute = $dbForInternal->createDocument('attributes', $attribute); } diff --git a/src/Appwrite/Utopia/Response/Model/Collection.php b/src/Appwrite/Utopia/Response/Model/Collection.php index e46603fd9..331dd48b0 100644 --- a/src/Appwrite/Utopia/Response/Model/Collection.php +++ b/src/Appwrite/Utopia/Response/Model/Collection.php @@ -55,14 +55,14 @@ class Collection extends Model ], 'description' => 'Collection attributes.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true, ]) ->addRule('indexes', [ 'type' => Response::MODEL_INDEX, 'description' => 'Collection indexes.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ; From 0960a54169364e4f219f6d1db57716c612996405 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 5 Oct 2021 10:10:40 -0400 Subject: [PATCH 117/130] Clean up unneeded sleeps in db tests --- tests/e2e/Services/Database/DatabaseBase.php | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index 96339b1fd..4111aca11 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -654,8 +654,6 @@ trait DatabaseBase 'required' => false, ]); - sleep(2); - $ip = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/ip', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -665,8 +663,6 @@ trait DatabaseBase 'required' => false, ]); - sleep(2); - $url = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/url', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -677,8 +673,6 @@ trait DatabaseBase 'required' => false, ]); - sleep(2); - $range = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -690,8 +684,6 @@ trait DatabaseBase 'max' => 10, ]); - sleep(2); - // TODO@kodumbeats min and max are rounded in error message $floatRange = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/float', array_merge([ 'content-type' => 'application/json', @@ -704,8 +696,6 @@ trait DatabaseBase 'max' => 1.4, ]); - sleep(2); - // TODO@kodumbeats float validator rejects 0.0 and 1.0 as floats // $probability = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/float', array_merge([ // 'content-type' => 'application/json', @@ -728,8 +718,6 @@ trait DatabaseBase 'max' => 10, ]); - sleep(2); - $lowerBound = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -766,7 +754,7 @@ trait DatabaseBase // $this->assertEquals('Minimum value must be lesser than maximum value', $invalidRange['body']['message']); // wait for worker to add attributes - sleep(2); + sleep(3); $collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId, array_merge([ 'content-type' => 'application/json', From bc0a15e38bbd356275ee02c21a84cd9720a725ab Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 5 Oct 2021 16:27:06 +0200 Subject: [PATCH 118/130] fix usage worker --- app/tasks/usage.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/tasks/usage.php b/app/tasks/usage.php index 4ff5e8cad..701037fcc 100644 --- a/app/tasks/usage.php +++ b/app/tasks/usage.php @@ -342,7 +342,7 @@ $cli do { // list projects try { $attempts++; - $projects = $dbForConsole->find('projects', [], 100, orderAfter:$latestProject); + $projects = $dbForConsole->find('projects', [], 100, cursor:$latestProject); break; // leave the do-while if successful } catch (\Exception $e) { Console::warning("Console DB not ready yet. Retrying ({$attempts})..."); @@ -472,7 +472,7 @@ $cli do { // Loop over all the parent collection document for each sub collection $dbForProject->setNamespace("project_{$projectId}_{$options['namespace']}"); - $parents = $dbForProject->find($collection, [], 100, orderAfter:$latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections + $parents = $dbForProject->find($collection, [], 100, cursor:$latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections if (empty($parents)) { continue; From 88ef9dfd3c8ebe6f2a70bf3885f5a256a01d9203 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 5 Oct 2021 10:29:43 -0400 Subject: [PATCH 119/130] style - pass clone of user object to workers --- app/controllers/api/users.php | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 20266d1fd..5023ccd04 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -719,26 +719,31 @@ App::delete('/v1/users/:userId') throw new Exception('User not found', 404); } - $emptyUser = clone $user; - $emptyUser->setAttribute("name", null); - $emptyUser->setAttribute("email", null); - $emptyUser->setAttribute("password", null); - $emptyUser->setAttribute("deleted", true); + // clone user object to send to workers + $clone = clone $user; - $dbForInternal->updateDocument('users', $userId, $emptyUser); + $user + ->setAttribute("name", null) + ->setAttribute("email", null); + ->setAttribute("password", null); + ->setAttribute("deleted", true) + ; + + $dbForInternal->updateDocument('users', $userId, $user); $deletes ->setParam('type', DELETE_TYPE_DOCUMENT) - ->setParam('document', $user) + ->setParam('document', $clone) ; $events - ->setParam('eventData', $response->output($user, Response::MODEL_USER)) + ->setParam('eventData', $response->output($clone, Response::MODEL_USER)) ; $usage ->setParam('users.delete', 1) ; + $response->noContent(); }); From cdaec34c6a5427c0926dfd6b02dceb6f040bb541 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 5 Oct 2021 16:36:34 +0200 Subject: [PATCH 120/130] fix usage worker --- app/tasks/usage.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/tasks/usage.php b/app/tasks/usage.php index 4ff5e8cad..701037fcc 100644 --- a/app/tasks/usage.php +++ b/app/tasks/usage.php @@ -342,7 +342,7 @@ $cli do { // list projects try { $attempts++; - $projects = $dbForConsole->find('projects', [], 100, orderAfter:$latestProject); + $projects = $dbForConsole->find('projects', [], 100, cursor:$latestProject); break; // leave the do-while if successful } catch (\Exception $e) { Console::warning("Console DB not ready yet. Retrying ({$attempts})..."); @@ -472,7 +472,7 @@ $cli do { // Loop over all the parent collection document for each sub collection $dbForProject->setNamespace("project_{$projectId}_{$options['namespace']}"); - $parents = $dbForProject->find($collection, [], 100, orderAfter:$latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections + $parents = $dbForProject->find($collection, [], 100, cursor:$latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections if (empty($parents)) { continue; From 41857f0bda8c65df0817cec29308badee57bf2c7 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 5 Oct 2021 16:37:19 +0200 Subject: [PATCH 121/130] fix usage worker --- app/tasks/usage.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/tasks/usage.php b/app/tasks/usage.php index 4ff5e8cad..701037fcc 100644 --- a/app/tasks/usage.php +++ b/app/tasks/usage.php @@ -342,7 +342,7 @@ $cli do { // list projects try { $attempts++; - $projects = $dbForConsole->find('projects', [], 100, orderAfter:$latestProject); + $projects = $dbForConsole->find('projects', [], 100, cursor:$latestProject); break; // leave the do-while if successful } catch (\Exception $e) { Console::warning("Console DB not ready yet. Retrying ({$attempts})..."); @@ -472,7 +472,7 @@ $cli do { // Loop over all the parent collection document for each sub collection $dbForProject->setNamespace("project_{$projectId}_{$options['namespace']}"); - $parents = $dbForProject->find($collection, [], 100, orderAfter:$latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections + $parents = $dbForProject->find($collection, [], 100, cursor:$latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections if (empty($parents)) { continue; From c3d1dad82cdaaeb95f0fc42ed87de76c240bf3a4 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 5 Oct 2021 17:39:39 +0200 Subject: [PATCH 122/130] fix stdClass usage --- app/controllers/api/projects.php | 2 +- .../Specification/Format/OpenAPI3.php | 2 +- .../Specification/Format/Swagger2.php | 3 +-- src/Appwrite/Utopia/Response.php | 3 +-- .../Utopia/Response/Model/Collection.php | 5 ++--- .../Utopia/Response/Model/Project.php | 9 ++++---- .../Utopia/Response/Model/UsageBuckets.php | 11 +++++----- .../Utopia/Response/Model/UsageCollection.php | 11 +++++----- .../Utopia/Response/Model/UsageDatabase.php | 21 +++++++++---------- .../Utopia/Response/Model/UsageFunctions.php | 7 +++---- .../Utopia/Response/Model/UsageProject.php | 15 +++++++------ .../Utopia/Response/Model/UsageStorage.php | 5 ++--- .../Utopia/Response/Model/UsageUsers.php | 17 +++++++-------- 13 files changed, 50 insertions(+), 61 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 3c7dbb8e5..4baf99e08 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -94,7 +94,7 @@ App::post('/v1/projects') 'legalCity' => $legalCity, 'legalAddress' => $legalAddress, 'legalTaxId' => $legalTaxId, - 'services' => new stdClass(), + 'services' => new \stdClass(), 'platforms' => [], 'webhooks' => [], 'keys' => [], diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 8d375d2f9..863a7a962 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -319,7 +319,7 @@ class OpenAPI3 extends Format case 'Utopia\Validator\JSON': case 'Utopia\Validator\Mock': case 'Utopia\Validator\Assoc': - $param['default'] = (empty($param['default'])) ? new stdClass() : $param['default']; + $param['default'] = (empty($param['default'])) ? new \stdClass() : $param['default']; $node['schema']['type'] = 'object'; $node['schema']['x-example'] = '{}'; //$node['schema']['format'] = 'json'; diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index a8dcffb1e..3ab5f184a 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -4,7 +4,6 @@ namespace Appwrite\Specification\Format; use Appwrite\Specification\Format; use Appwrite\Template\Template; -use stdClass; use Utopia\Validator; class Swagger2 extends Format @@ -317,7 +316,7 @@ class Swagger2 extends Format case 'Utopia\Validator\Mock': case 'Utopia\Validator\Assoc': $node['type'] = 'object'; - $param['default'] = (empty($param['default'])) ? new stdClass() : $param['default']; + $param['default'] = (empty($param['default'])) ? new \stdClass() : $param['default']; $node['x-example'] = '{}'; //$node['format'] = 'json'; break; diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 825875835..f92023d8d 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -59,7 +59,6 @@ use Appwrite\Utopia\Response\Model\UsageFunctions; use Appwrite\Utopia\Response\Model\UsageProject; use Appwrite\Utopia\Response\Model\UsageStorage; use Appwrite\Utopia\Response\Model\UsageUsers; -use stdClass; /** * @method Response public function setStatusCode(int $code = 200) @@ -328,7 +327,7 @@ class Response extends SwooleResponse $output = self::getFilter()->parse($output, $model); } - $this->json(!empty($output) ? $output : new stdClass()); + $this->json(!empty($output) ? $output : new \stdClass()); } /** diff --git a/src/Appwrite/Utopia/Response/Model/Collection.php b/src/Appwrite/Utopia/Response/Model/Collection.php index e46603fd9..b2a05846c 100644 --- a/src/Appwrite/Utopia/Response/Model/Collection.php +++ b/src/Appwrite/Utopia/Response/Model/Collection.php @@ -4,7 +4,6 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -use Utopia\Database\Document; class Collection extends Model { @@ -55,14 +54,14 @@ class Collection extends Model ], 'description' => 'Collection attributes.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true, ]) ->addRule('indexes', [ 'type' => Response::MODEL_INDEX, 'description' => 'Collection indexes.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ; diff --git a/src/Appwrite/Utopia/Response/Model/Project.php b/src/Appwrite/Utopia/Response/Model/Project.php index adbd92266..ed2fd60f1 100644 --- a/src/Appwrite/Utopia/Response/Model/Project.php +++ b/src/Appwrite/Utopia/Response/Model/Project.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Response\Model; -use stdClass; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Config\Config; @@ -100,28 +99,28 @@ class Project extends Model 'type' => Response::MODEL_PLATFORM, 'description' => 'List of Platforms.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true, ]) ->addRule('webhooks', [ 'type' => Response::MODEL_WEBHOOK, 'description' => 'List of Webhooks.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true, ]) ->addRule('keys', [ 'type' => Response::MODEL_KEY, 'description' => 'List of API Keys.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true, ]) ->addRule('domains', [ 'type' => Response::MODEL_DOMAIN, 'description' => 'List of Domains.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true, ]) ; diff --git a/src/Appwrite/Utopia/Response/Model/UsageBuckets.php b/src/Appwrite/Utopia/Response/Model/UsageBuckets.php index 8c66ff47c..cafa679ab 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageBuckets.php +++ b/src/Appwrite/Utopia/Response/Model/UsageBuckets.php @@ -4,7 +4,6 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -use stdClass; class UsageBuckets extends Model { @@ -21,35 +20,35 @@ class UsageBuckets extends Model 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for total number of files in this bucket.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('files.create', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for files created.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('files.read', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for files read.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('files.update', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for files updated.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('files.delete', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for files deleted.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ; diff --git a/src/Appwrite/Utopia/Response/Model/UsageCollection.php b/src/Appwrite/Utopia/Response/Model/UsageCollection.php index f2815831a..cf9e3e454 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageCollection.php +++ b/src/Appwrite/Utopia/Response/Model/UsageCollection.php @@ -4,7 +4,6 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -use stdClass; class UsageCollection extends Model { @@ -21,35 +20,35 @@ class UsageCollection extends Model 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for total number of documents.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('documents.create', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for documents created.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('documents.read', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for documents read.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('documents.update', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for documents updated.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('documents.delete', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for documents deleted.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ; diff --git a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php index 2ebe0b64c..eab025ba1 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php +++ b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php @@ -4,7 +4,6 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -use stdClass; class UsageDatabase extends Model { @@ -21,70 +20,70 @@ class UsageDatabase extends Model 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for total number of documents.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('collections.count', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for total number of collections.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('documents.create', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for documents created.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('documents.read', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for documents read.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('documents.update', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for documents updated.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('documents.delete', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for documents deleted.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('collections.create', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for collections created.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('collections.read', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for collections read.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('collections.update', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for collections updated.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('collections.delete', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for collections delete.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ; diff --git a/src/Appwrite/Utopia/Response/Model/UsageFunctions.php b/src/Appwrite/Utopia/Response/Model/UsageFunctions.php index 625bdab71..6183b1aff 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageFunctions.php +++ b/src/Appwrite/Utopia/Response/Model/UsageFunctions.php @@ -4,7 +4,6 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -use stdClass; class UsageFunctions extends Model { @@ -21,21 +20,21 @@ class UsageFunctions extends Model 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for function executions.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('functions.failures', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for function execution failures.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('functions.compute', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for function execution duration.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ; diff --git a/src/Appwrite/Utopia/Response/Model/UsageProject.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php index c44af4cf6..5593def1d 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageProject.php +++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php @@ -4,7 +4,6 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -use stdClass; class UsageProject extends Model { @@ -21,49 +20,49 @@ class UsageProject extends Model 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for number of requests.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('network', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for consumed bandwidth.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('functions', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for function executions.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('documents', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for number of documents.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('collections', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for number of collections.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('users', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for number of users.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('storage', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for the occupied storage size (in bytes).', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ; diff --git a/src/Appwrite/Utopia/Response/Model/UsageStorage.php b/src/Appwrite/Utopia/Response/Model/UsageStorage.php index 4462a219a..db40ccf3b 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageStorage.php +++ b/src/Appwrite/Utopia/Response/Model/UsageStorage.php @@ -4,7 +4,6 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -use stdClass; class UsageStorage extends Model { @@ -21,14 +20,14 @@ class UsageStorage extends Model 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for the occupied storage size (in bytes).', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('files', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for total number of files.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ; diff --git a/src/Appwrite/Utopia/Response/Model/UsageUsers.php b/src/Appwrite/Utopia/Response/Model/UsageUsers.php index 881361506..3854fdec7 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageUsers.php +++ b/src/Appwrite/Utopia/Response/Model/UsageUsers.php @@ -4,7 +4,6 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -use stdClass; class UsageUsers extends Model { @@ -21,56 +20,56 @@ class UsageUsers extends Model 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for total number of users.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('users.create', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for users created.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('users.read', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for users read.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('users.update', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for users updated.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('users.delete', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for users deleted.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('sessions.create', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for sessions created.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('sessions.provider.create', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for sessions created for a provider ( email, anonymous or oauth2 ).', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ->addRule('sessions.delete', [ 'type' => Response::MODEL_METRIC_LIST, 'description' => 'Aggregated stats for sessions deleted.', 'default' => [], - 'example' => new stdClass, + 'example' => new \stdClass, 'array' => true ]) ; From 3d6de5da4774f60719526f6ebe2a8306bb435158 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 5 Oct 2021 12:02:54 -0400 Subject: [PATCH 123/130] Throw correct response code for duplicate user --- app/controllers/api/account.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index e01bb0731..ac7179972 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1140,7 +1140,7 @@ App::patch('/v1/account/email') $profile = $dbForInternal->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address if ($profile) { - throw new Exception('User already registered', 400); + throw new Exception('User already registered', 409); } try { From 895da9dbef9c65a244b1b899145f6d65beadae80 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 5 Oct 2021 22:37:17 +0200 Subject: [PATCH 124/130] fix semi-colons --- app/controllers/api/users.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 5023ccd04..86319ea5e 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -724,8 +724,8 @@ App::delete('/v1/users/:userId') $user ->setAttribute("name", null) - ->setAttribute("email", null); - ->setAttribute("password", null); + ->setAttribute("email", null) + ->setAttribute("password", null) ->setAttribute("deleted", true) ; From 4a054559a54ddc43601caeaf5a5af11e92646b5d Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 5 Oct 2021 23:15:43 +0200 Subject: [PATCH 125/130] fix search with cristiano --- app/controllers/api/users.php | 2 +- tests/e2e/Services/Users/UsersBase.php | 67 +++++++++++--------------- 2 files changed, 28 insertions(+), 41 deletions(-) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index a68857273..7adaa747c 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -111,7 +111,7 @@ App::get('/v1/users') throw new Exception("User '{$after}' for the 'after' value not found.", 400); } } - + $queries = []; if (!empty($search)) { diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php index 7db500ddc..ee35e66fc 100644 --- a/tests/e2e/Services/Users/UsersBase.php +++ b/tests/e2e/Services/Users/UsersBase.php @@ -16,14 +16,14 @@ trait UsersBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'userId' => 'unique()', - 'email' => 'first.user@example.com', + 'email' => 'cristiano.ronaldo@manchester-united.co.uk', 'password' => 'password', - 'name' => 'First Name', + 'name' => 'Cristiano Ronaldo', ]); $this->assertEquals($user['headers']['status-code'], 201); - $this->assertEquals($user['body']['name'], 'First Name'); - $this->assertEquals($user['body']['email'], 'first.user@example.com'); + $this->assertEquals($user['body']['name'], 'Cristiano Ronaldo'); + $this->assertEquals($user['body']['email'], 'cristiano.ronaldo@manchester-united.co.uk'); $this->assertEquals($user['body']['status'], true); $this->assertGreaterThan(0, $user['body']['registration']); @@ -35,15 +35,15 @@ trait UsersBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'userId' => 'user1', - 'email' => 'users.service1@example.com', + 'email' => 'lionel.messi@psg.fr', 'password' => 'password', - 'name' => 'Project User', + 'name' => 'Lionel Messi', ]); $this->assertEquals($res['headers']['status-code'], 201); $this->assertEquals($res['body']['$id'], 'user1'); - $this->assertEquals($res['body']['name'], 'Project User'); - $this->assertEquals($res['body']['email'], 'users.service1@example.com'); + $this->assertEquals($res['body']['name'], 'Lionel Messi'); + $this->assertEquals($res['body']['email'], 'lionel.messi@psg.fr'); $this->assertEquals(true, $res['body']['status']); $this->assertGreaterThan(0, $res['body']['registration']); @@ -91,7 +91,7 @@ trait UsersBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'search' => 'First Name' + 'search' => 'Ronaldo' ]); $this->assertEquals($response['headers']['status-code'], 200); $this->assertNotEmpty($response['body']); @@ -103,7 +103,7 @@ trait UsersBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'search' => 'first.user' + 'search' => 'cristiano.ronaldo' ]); $this->assertEquals($response['headers']['status-code'], 200); $this->assertNotEmpty($response['body']); @@ -115,7 +115,7 @@ trait UsersBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'search' => 'first user name' + 'search' => 'manchester' ]); $this->assertEquals($response['headers']['status-code'], 200); $this->assertNotEmpty($response['body']); @@ -123,6 +123,20 @@ trait UsersBase $this->assertCount(1, $response['body']['users']); $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'manchester-united.co.uk' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertIsArray($response['body']); + $this->assertIsArray($response['body']['users']); + $this->assertIsInt($response['body']['sum']); + $this->assertEquals(1, $response['body']['sum']); + $this->assertCount(1, $response['body']['users']); + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -134,18 +148,6 @@ trait UsersBase $this->assertNotEmpty($response['body']['users']); $this->assertCount(1, $response['body']['users']); $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); - - $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'search' => 'first user - first.user@example.com ' . $data['userId'] - ]); - $this->assertEquals($response['headers']['status-code'], 200); - $this->assertNotEmpty($response['body']); - $this->assertNotEmpty($response['body']['users']); - $this->assertCount(1, $response['body']['users']); - $this->assertEquals($response['body']['users'][0]['$id'], $data['userId']); } /** @@ -162,8 +164,8 @@ trait UsersBase ], $this->getHeaders())); $this->assertEquals($user['headers']['status-code'], 200); - $this->assertEquals($user['body']['name'], 'First Name'); - $this->assertEquals($user['body']['email'], 'first.user@example.com'); + $this->assertEquals($user['body']['name'], 'Cristiano Ronaldo'); + $this->assertEquals($user['body']['email'], 'cristiano.ronaldo@manchester-united.co.uk'); $this->assertEquals($user['body']['status'], true); $this->assertGreaterThan(0, $user['body']['registration']); @@ -194,21 +196,6 @@ trait UsersBase $this->assertIsInt($users['body']['sum']); $this->assertGreaterThan(0, $users['body']['sum']); - $users = $this->client->call(Client::METHOD_GET, '/users', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'search' => 'example.com' - ]); - - $this->assertEquals($users['headers']['status-code'], 200); - $this->assertIsArray($users['body']); - $this->assertIsArray($users['body']['users']); - $this->assertIsInt($users['body']['sum']); - $this->assertEquals(2, $users['body']['sum']); - $this->assertGreaterThan(0, $users['body']['sum']); - $this->assertCount(2, $users['body']['users']); - return $data; } From 8331842a23ec4687fea2f026e229909538a27b6b Mon Sep 17 00:00:00 2001 From: Krish Gupta Date: Wed, 6 Oct 2021 11:10:29 +0530 Subject: [PATCH 126/130] style: fix using php cs fixer --- src/Appwrite/URL/URL.php | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Appwrite/URL/URL.php b/src/Appwrite/URL/URL.php index 6e32f8dfb..99edf9cf8 100644 --- a/src/Appwrite/URL/URL.php +++ b/src/Appwrite/URL/URL.php @@ -13,7 +13,7 @@ class URL * * @return array */ - public static function parse(string $url):array + public static function parse(string $url): array { $default = [ 'scheme' => '', @@ -28,7 +28,7 @@ class URL return \array_merge($default, \parse_url($url)); } - + /** * Un-Parse URL * @@ -39,32 +39,32 @@ class URL * * @return string */ - public static function unparse(array $url, array $ommit = []):string + public static function unparse(array $url, array $ommit = []): string { if (isset($url['path']) && \mb_substr($url['path'], 0, 1) !== '/') { $url['path'] = '/'.$url['path']; } - + $parts = []; - + $parts['scheme'] = isset($url['scheme']) ? $url['scheme'].'://' : ''; - + $parts['host'] = isset($url['host']) ? $url['host'] : ''; - + $parts['port'] = isset($url['port']) ? ':'.$url['port'] : ''; - + $parts['user'] = isset($url['user']) ? $url['user'] : ''; - - $parts['pass'] = isset($url['pass']) ? ':'.$url['pass'] : ''; - + + $parts['pass'] = isset($url['pass']) ? ':'.$url['pass'] : ''; + $parts['pass'] = ($parts['user'] || $parts['pass']) ? $parts['pass'].'@' : ''; - + $parts['path'] = isset($url['path']) ? $url['path'] : ''; - + $parts['query'] = isset($url['query']) && !empty($url['query']) ? '?'.$url['query'] : ''; - + $parts['fragment'] = isset($url['fragment']) ? '#'.$url['fragment'] : ''; - + if ($ommit) { foreach ($ommit as $key) { if (isset($parts[ $key ])) { @@ -72,7 +72,7 @@ class URL } } } - + return $parts['scheme'].$parts['user'].$parts['pass'].$parts['host'].$parts['port'].$parts['path'].$parts['query'].$parts['fragment']; } @@ -85,7 +85,7 @@ class URL * * @return array */ - public static function parseQuery(string $query):array + public static function parseQuery(string $query): array { \parse_str($query, $result); @@ -101,7 +101,7 @@ class URL * * @return string */ - public static function unparseQuery(array $query):string + public static function unparseQuery(array $query): string { return \http_build_query($query); } From c9b8175a904be4e8ea325ec197860c08e947e9da Mon Sep 17 00:00:00 2001 From: Charithesh Puppireddy Date: Wed, 6 Oct 2021 11:38:54 +0530 Subject: [PATCH 127/130] Fixed PSR issues for Detector Library --- src/Appwrite/Detector/Detector.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Appwrite/Detector/Detector.php b/src/Appwrite/Detector/Detector.php index d2a029fe4..75c438724 100644 --- a/src/Appwrite/Detector/Detector.php +++ b/src/Appwrite/Detector/Detector.php @@ -26,7 +26,7 @@ class Detector /** * Get OS info - * + * * @return array */ public function getOS(): array @@ -42,7 +42,7 @@ class Detector /** * Get client info - * + * * @return array */ public function getClient(): array @@ -61,7 +61,7 @@ class Detector /** * Get device info - * + * * @return array */ public function getDevice(): array @@ -78,7 +78,7 @@ class Detector */ protected function getDetector(): DeviceDetector { - if(!$this->detctor) { + if (!$this->detctor) { $this->detctor = new DeviceDetector($this->userAgent); $this->detctor->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) $this->detctor->parse(); From acb4aebe4db26d4a76826aef76e3d93a9547b503 Mon Sep 17 00:00:00 2001 From: Steven Boixel Date: Wed, 6 Oct 2021 13:53:33 +0200 Subject: [PATCH 128/130] Fix - PSR issues in the Task library This fix the PSR issue for the Cron Class and also the CronTest Resolve #1981 --- src/Appwrite/Task/Validator/Cron.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Task/Validator/Cron.php b/src/Appwrite/Task/Validator/Cron.php index 544482048..03bd1c522 100644 --- a/src/Appwrite/Task/Validator/Cron.php +++ b/src/Appwrite/Task/Validator/Cron.php @@ -10,11 +10,11 @@ class Cron extends Validator /** * Get Description. * - * Returns validator description + * Returns validator description. * * @return string */ - public function getDescription() + public function getDescription(): string { return 'String must be a valid cron expression'; } @@ -28,7 +28,7 @@ class Cron extends Validator * * @return bool */ - public function isValid($value) + public function isValid($value): bool { if (empty($value)) { return true; @@ -42,7 +42,7 @@ class Cron extends Validator } /** - * Is array + * Is array. * * Function will return true if object is array. * @@ -54,7 +54,7 @@ class Cron extends Validator } /** - * Get Type + * Get Type. * * Returns validator type. * From 7ce2ca45b4405ba84c67ea53d698d018e64fefa6 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Wed, 6 Oct 2021 13:56:44 +0200 Subject: [PATCH 129/130] fix cache purge in projects api --- app/controllers/api/projects.php | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index f87176af0..2acb095a2 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -597,7 +597,7 @@ App::post('/v1/projects/:projectId/webhooks') $webhook = $dbForConsole->createDocument('webhooks', $webhook); - $dbForConsole->purgeDocument('projects', $project->getId()); + $dbForConsole->deleteCachedDocument('projects', $project->getId()); $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($webhook, Response::MODEL_WEBHOOK); @@ -724,7 +724,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') $dbForConsole->updateDocument('webhooks', $webhook->getId(), $webhook); - $dbForConsole->purgeDocument('projects', $project->getId()); + $dbForConsole->deleteCachedDocument('projects', $project->getId()); $response->dynamic($webhook, Response::MODEL_WEBHOOK); }); @@ -763,7 +763,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') $dbForConsole->deleteDocument('webhooks', $webhook->getId()); - $dbForConsole->purgeDocument('projects', $project->getId()); + $dbForConsole->deleteCachedDocument('projects', $project->getId()); $response->noContent(); }); @@ -807,7 +807,7 @@ App::post('/v1/projects/:projectId/keys') $key = $dbForConsole->createDocument('keys', $key); - $dbForConsole->purgeDocument('projects', $project->getId()); + $dbForConsole->deleteCachedDocument('projects', $project->getId()); $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($key, Response::MODEL_KEY); @@ -924,7 +924,7 @@ App::put('/v1/projects/:projectId/keys/:keyId') $dbForConsole->updateDocument('keys', $key->getId(), $key); - $dbForConsole->purgeDocument('projects', $project->getId()); + $dbForConsole->deleteCachedDocument('projects', $project->getId()); $response->dynamic($key, Response::MODEL_KEY); }); @@ -963,7 +963,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId') $dbForConsole->deleteDocument('keys', $key->getId()); - $dbForConsole->purgeDocument('projects', $project->getId()); + $dbForConsole->deleteCachedDocument('projects', $project->getId()); $response->noContent(); }); @@ -1014,7 +1014,7 @@ App::post('/v1/projects/:projectId/platforms') $platform = $dbForConsole->createDocument('platforms', $platform); - $dbForConsole->purgeDocument('projects', $project->getId()); + $dbForConsole->deleteCachedDocument('projects', $project->getId()); $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($platform, Response::MODEL_PLATFORM); @@ -1136,7 +1136,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') $dbForConsole->updateDocument('platforms', $platform->getId(), $platform); - $dbForConsole->purgeDocument('projects', $project->getId()); + $dbForConsole->deleteCachedDocument('projects', $project->getId()); $response->dynamic($platform, Response::MODEL_PLATFORM); }); @@ -1175,7 +1175,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') $dbForConsole->deleteDocument('platforms', $platformId); - $dbForConsole->purgeDocument('projects', $project->getId()); + $dbForConsole->deleteCachedDocument('projects', $project->getId()); $response->noContent(); }); @@ -1238,7 +1238,7 @@ App::post('/v1/projects/:projectId/domains') $domain = $dbForConsole->createDocument('domains', $domain); - $dbForConsole->purgeDocument('projects', $project->getId()); + $dbForConsole->deleteCachedDocument('projects', $project->getId()); $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($domain, Response::MODEL_DOMAIN); @@ -1364,7 +1364,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') $dbForConsole->updateDocument('domains', $domain->getId(), $domain->setAttribute('verification', true)); - $dbForConsole->purgeDocument('projects', $project->getId()); + $dbForConsole->deleteCachedDocument('projects', $project->getId()); // Issue a TLS certificate when domain is verified Resque::enqueue('v1-certificates', 'CertificatesV1', [ @@ -1410,7 +1410,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId') $dbForConsole->deleteDocument('domains', $domain->getId()); - $dbForConsole->purgeDocument('projects', $project->getId()); + $dbForConsole->deleteCachedDocument('projects', $project->getId()); $deletes ->setParam('type', DELETE_TYPE_CERTIFICATES) From e0699932a03ad73b66b14e78459c5586d1313fe6 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Wed, 6 Oct 2021 14:56:44 +0200 Subject: [PATCH 130/130] fix(users): query to ignore deleted users --- app/controllers/api/users.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 28da6b0a9..99748d40d 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -113,7 +113,9 @@ App::get('/v1/users') } } - $queries = []; + $queries = [ + new Query('deleted', Query::TYPE_EQUAL, [false]) + ]; if (!empty($search)) { $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);