diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 75ea368fcd..f5d8f9ebd6 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -1676,54 +1676,77 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $queries = Query::parseQueries($queries); - foreach ($queries as $query) { + $queriesFromRequest = Query::parseQueries($queries); + + // Add type property in query if select query exists and type property doesn't exist as type is required for response model + $hasDetailsinQuery = [ + 'selectQuery' => false, + 'typeAttributeInSelectQuery' => false, + ]; + foreach ($queriesFromRequest as $query) { if ($query->getMethod() === Query::TYPE_SELECT) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, 'Select queries are not valid.'); + $hasDetailsinQuery['selectQuery'] = true; + } + if(\array_search('type', $query->getValues())){ + $hasDetailsinQuery['typeAttributeInSelectQuery'] = true; } } - \array_push($queries, Query::equal('collectionId', [$collectionId]), Query::equal('databaseId', [$databaseId])); + $transformedQueries = $queriesFromRequest; + if ($hasDetailsinQuery['selectQuery'] && !$hasDetailsinQuery['typeAttributeInSelectQuery']) { + \array_push($transformedQueries, Query::select(['type'])); + } + + \array_push($transformedQueries, Query::equal('collectionId', [$collectionId]), Query::equal('databaseId', [$databaseId])); // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); + $cursor = Query::getByType($transformedQueries, [Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE]); $cursor = reset($cursor); if ($cursor) { $attributeId = $cursor->getValue(); - $cursorDocument = Authorization::skip(fn() => $dbForProject->find('attributes', [ Query::equal('collectionId', [$collectionId]), Query::equal('databaseId', [$databaseId]), Query::equal('key', [$attributeId]), Query::limit(1), ])); - if (empty($cursorDocument) || $cursorDocument[0]->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Attribute '{$attributeId}' for the 'cursor' value not found."); } - $cursor->setValue($cursorDocument[0]); } - - $attributes = $dbForProject->find('attributes', $queries); + + $attributes = $dbForProject->find('attributes', $transformedQueries); + $filterQueries = Query::groupByType($transformedQueries)['filters']; + $total = $dbForProject->count('attributes', $filterQueries, APP_LIMIT_COUNT); //Add relationship data from options to attributes as it loses options during response setup foreach ($attributes as $attribute) { - if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { + if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP ) { $options = $attribute->getAttribute('options'); - $attribute->setAttribute('relatedCollection', $options['relatedCollection']); - $attribute->setAttribute('relationType', $options['relationType']); - $attribute->setAttribute('twoWay', $options['twoWay']); - $attribute->setAttribute('twoWayKey', $options['twoWayKey']); - $attribute->setAttribute('side', $options['side']); - $attribute->setAttribute('onDelete', $options['onDelete']); + if(!\is_null($options)){ + $attribute->setAttribute('relatedCollection', $options['relatedCollection']); + $attribute->setAttribute('relationType', $options['relationType']); + $attribute->setAttribute('twoWay', $options['twoWay']); + $attribute->setAttribute('twoWayKey', $options['twoWayKey']); + $attribute->setAttribute('side', $options['side']); + $attribute->setAttribute('onDelete', $options['onDelete']); + } } } - $filterQueries = Query::groupByType($queries)['filters']; - $response->dynamic(new Document([ - 'total' => $dbForProject->count('attributes', $filterQueries, APP_LIMIT_COUNT), + $output = $response->output(new Document([ + 'total' => $total, 'attributes' => $attributes, ]), Response::MODEL_ATTRIBUTE_LIST); + + // If type Attribute didn't exist in select query we need to remove type attribute from attribute list + if ($hasDetailsinQuery['selectQuery'] && !$hasDetailsinQuery['typeAttributeInSelectQuery']) { + foreach($output['attributes'] as &$attribute) { + unset($attribute['type']); + } + } + + $response->static($output); }); App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') @@ -2551,11 +2574,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') } $queries = Query::parseQueries($queries); - foreach ($queries as $query) { - if ($query->getMethod() === Query::TYPE_SEARCH) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, 'Select queries are not valid.'); - } - } \array_push($queries, Query::equal('collectionId', [$collectionId]), Query::equal('databaseId', [$databaseId])); // Get cursor document if there was a cursor query diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index c89bf0252e..566a800752 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -207,9 +207,11 @@ App::init() $deletes->setProject($project); $database->setProject($project); - $dbForProject->on(Database::EVENT_DOCUMENT_CREATE, fn ($event, Document $document) => $databaseListener($event, $document, $usage)); + $calculateUsage = fn ($event, Document $document) => $databaseListener($event, $document, $usage); - $dbForProject->on(Database::EVENT_DOCUMENT_DELETE, fn ($event, Document $document) => $databaseListener($event, $document, $usage)); + $dbForProject->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', $calculateUsage); + + $dbForProject->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', $calculateUsage); $useCache = $route->getLabel('cache', false); diff --git a/composer.json b/composer.json index 8070096898..6c31fa8873 100644 --- a/composer.json +++ b/composer.json @@ -43,13 +43,13 @@ "ext-sockets": "*", "appwrite/php-clamav": "2.0.*", "appwrite/php-runtimes": "0.11.*", - "utopia-php/abuse": "0.27.*", + "utopia-php/abuse": "0.29.*", "utopia-php/analytics": "0.2.*", - "utopia-php/audit": "0.29.*", + "utopia-php/audit": "0.31.*", "utopia-php/cache": "0.8.*", "utopia-php/cli": "0.13.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.38.*", + "utopia-php/database": "0.40.*", "utopia-php/domains": "1.1.*", "utopia-php/framework": "0.28.*", "utopia-php/image": "0.5.*", diff --git a/composer.lock b/composer.lock index 5a25c90dc4..c9d6d1295f 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": "c41be9b81d4ee69cf915be4c7ba37975", + "content-hash": "1dde9b87dc894a8ab765cad3567bfe7c", "packages": [ { "name": "adhocore/jwt", @@ -994,16 +994,16 @@ }, { "name": "matomo/device-detector", - "version": "6.1.3", + "version": "6.1.4", "source": { "type": "git", "url": "https://github.com/matomo-org/device-detector.git", - "reference": "3e0fac7e77f3faadc3858fea9f5fa7efeb9cf239" + "reference": "74f6c4f6732b3ad6cdf25560746841d522969112" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/3e0fac7e77f3faadc3858fea9f5fa7efeb9cf239", - "reference": "3e0fac7e77f3faadc3858fea9f5fa7efeb9cf239", + "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/74f6c4f6732b3ad6cdf25560746841d522969112", + "reference": "74f6c4f6732b3ad6cdf25560746841d522969112", "shasum": "" }, "require": { @@ -1059,7 +1059,7 @@ "source": "https://github.com/matomo-org/matomo", "wiki": "https://dev.matomo.org/" }, - "time": "2023-06-06T11:58:07+00:00" + "time": "2023-08-02T08:48:53+00:00" }, { "name": "mongodb/mongodb", @@ -1804,23 +1804,23 @@ }, { "name": "utopia-php/abuse", - "version": "0.27.0", + "version": "0.29.0", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "d1115f5843e903ffaba9c23e450b33c0fe265ae0" + "reference": "7589d0c7a6f685fcbb02d57875f034bd233ec262" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/d1115f5843e903ffaba9c23e450b33c0fe265ae0", - "reference": "d1115f5843e903ffaba9c23e450b33c0fe265ae0", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/7589d0c7a6f685fcbb02d57875f034bd233ec262", + "reference": "7589d0c7a6f685fcbb02d57875f034bd233ec262", "shasum": "" }, "require": { "ext-curl": "*", "ext-pdo": "*", "php": ">=8.0", - "utopia-php/database": "0.38.*" + "utopia-php/database": "0.40.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1847,9 +1847,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.27.0" + "source": "https://github.com/utopia-php/abuse/tree/0.29.0" }, - "time": "2023-07-15T00:53:50+00:00" + "time": "2023-08-03T08:22:12+00:00" }, { "name": "utopia-php/analytics", @@ -1908,21 +1908,21 @@ }, { "name": "utopia-php/audit", - "version": "0.29.0", + "version": "0.31.0", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "5318538f457bf73623629345c98ea06371ca5dd4" + "reference": "f1b0165fccb6ec665f7ae947bfed56a8f6b877cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/5318538f457bf73623629345c98ea06371ca5dd4", - "reference": "5318538f457bf73623629345c98ea06371ca5dd4", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/f1b0165fccb6ec665f7ae947bfed56a8f6b877cb", + "reference": "f1b0165fccb6ec665f7ae947bfed56a8f6b877cb", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "0.38.*" + "utopia-php/database": "0.40.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1949,9 +1949,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.29.0" + "source": "https://github.com/utopia-php/audit/tree/0.31.0" }, - "time": "2023-07-15T00:51:10+00:00" + "time": "2023-08-03T08:22:32+00:00" }, { "name": "utopia-php/cache", @@ -2108,16 +2108,16 @@ }, { "name": "utopia-php/database", - "version": "0.38.0", + "version": "0.40.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "59e4684cf87e03c12dab9240158c1dfc6888e534" + "reference": "ec586ba1c5fc83856df4feaa05464da72b72ee0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/59e4684cf87e03c12dab9240158c1dfc6888e534", - "reference": "59e4684cf87e03c12dab9240158c1dfc6888e534", + "url": "https://api.github.com/repos/utopia-php/database/zipball/ec586ba1c5fc83856df4feaa05464da72b72ee0d", + "reference": "ec586ba1c5fc83856df4feaa05464da72b72ee0d", "shasum": "" }, "require": { @@ -2158,9 +2158,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.38.0" + "source": "https://github.com/utopia-php/database/tree/0.40.0" }, - "time": "2023-07-14T07:49:38+00:00" + "time": "2023-08-03T08:01:37+00:00" }, { "name": "utopia-php/domains", @@ -3785,16 +3785,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.22.1", + "version": "1.23.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0" + "reference": "a2b24135c35852b348894320d47b3902a94bc494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/65c39594fbd8c67abfc68bb323f86447bab79cc0", - "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a2b24135c35852b348894320d47b3902a94bc494", + "reference": "a2b24135c35852b348894320d47b3902a94bc494", "shasum": "" }, "require": { @@ -3826,22 +3826,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.23.0" }, - "time": "2023-06-29T20:46:06+00:00" + "time": "2023-07-23T22:17:56+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.26", + "version": "9.2.27", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" + "reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/b0a88255cb70d52653d80c890bd7f38740ea50d1", + "reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1", "shasum": "" }, "require": { @@ -3897,7 +3897,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.27" }, "funding": [ { @@ -3905,7 +3906,7 @@ "type": "github" } ], - "time": "2023-03-06T12:58:08+00:00" + "time": "2023-07-26T13:44:30+00:00" }, { "name": "phpunit/php-file-iterator", @@ -4757,16 +4758,16 @@ }, { "name": "sebastian/global-state", - "version": "5.0.5", + "version": "5.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + "reference": "bde739e7565280bda77be70044ac1047bc007e34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", + "reference": "bde739e7565280bda77be70044ac1047bc007e34", "shasum": "" }, "require": { @@ -4809,7 +4810,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" }, "funding": [ { @@ -4817,7 +4818,7 @@ "type": "github" } ], - "time": "2022-02-14T08:28:10+00:00" + "time": "2023-08-02T09:26:13+00:00" }, { "name": "sebastian/lines-of-code", @@ -5580,16 +5581,16 @@ }, { "name": "twig/twig", - "version": "v3.6.1", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd" + "reference": "5cf942bbab3df42afa918caeba947f1b690af64b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd", - "reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/5cf942bbab3df42afa918caeba947f1b690af64b", + "reference": "5cf942bbab3df42afa918caeba947f1b690af64b", "shasum": "" }, "require": { @@ -5635,7 +5636,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.6.1" + "source": "https://github.com/twigphp/Twig/tree/v3.7.0" }, "funding": [ { @@ -5647,7 +5648,7 @@ "type": "tidelift" } ], - "time": "2023-06-08T12:52:13+00:00" + "time": "2023-07-26T07:16:09+00:00" } ], "aliases": [], diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Indexes.php b/src/Appwrite/Utopia/Database/Validator/Queries/Indexes.php index 56bc696706..d9290e5737 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Indexes.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Indexes.php @@ -8,7 +8,8 @@ class Indexes extends Base 'key', 'type', 'status', - 'error' + 'attributes', + 'error', ]; /** @@ -17,6 +18,6 @@ class Indexes extends Base */ public function __construct() { - parent::__construct('attributes', self::ALLOWED_ATTRIBUTES); + parent::__construct('indexes', self::ALLOWED_ATTRIBUTES); } } diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index e65f65cd52..51df3c2f37 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -458,6 +458,33 @@ class Response extends SwooleResponse } } + /** + * Sends the response based on content type + * + * @param array $ouput + * + * return void + * @throws Exception + */ + public function static(array $output): void + { + switch ($this->getContentType()) { + case self::CONTENT_TYPE_JSON: + $this->json(!empty($output) ? $output : new \stdClass()); + break; + + case self::CONTENT_TYPE_YAML: + $this->yaml(!empty($output) ? $output : new \stdClass()); + break; + + case self::CONTENT_TYPE_NULL: + break; + + default: + $this->json(!empty($output) ? $output : new \stdClass()); + } + } + /** * Generate valid response object from document data * @@ -491,45 +518,46 @@ class Response extends SwooleResponse } } - if ($rule['array']) { - if (!is_array($document[$key])) { - throw new Exception($key . ' must be an array of type ' . $rule['type']); - } - - foreach ($document[$key] as $index => $item) { - if ($item instanceof Document) { - 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) { + if ($rule['required'] && !(\is_null($document[$key]))) { + if ($rule['array']) { + if (!is_array($document[$key])) { + throw new Exception($key . ' must be an array of type ' . $rule['type']); + } + + foreach ($document[$key] as $index => $item) { + if ($item instanceof Document) { + 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; } } - if ($condition) { - $ruleType = $type; - break; - } + } else { + $ruleType = $rule['type']; } - } else { - $ruleType = $rule['type']; + + if (!array_key_exists($ruleType, $this->models)) { + throw new Exception('Missing model for rule: ' . $ruleType); + } + + $data[$key][$index] = $this->output($item, $ruleType); } - - if (!array_key_exists($ruleType, $this->models)) { - throw new Exception('Missing model for rule: ' . $ruleType); - } - - $data[$key][$index] = $this->output($item, $ruleType); + } + } else { + if ($document[$key] instanceof Document) { + $data[$key] = $this->output($data[$key], $rule['type']); } } - } else { - if ($document[$key] instanceof Document) { - $data[$key] = $this->output($data[$key], $rule['type']); - } + $output[$key] = $data[$key]; } - - $output[$key] = $data[$key]; } $this->payload = $output;