From 85de3d27d860d82bf195347ca780f37cc8038e11 Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Wed, 16 Apr 2025 15:22:38 +0530 Subject: [PATCH 01/11] updated database version --- composer.json | 2 +- composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 5b13227d2b..b3a1df7a59 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,7 @@ "utopia-php/cache": "0.12.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.65.*", + "utopia-php/database": "0.66.*", "utopia-php/domains": "0.5.*", "utopia-php/dsn": "0.2.1", "utopia-php/framework": "0.33.*", diff --git a/composer.lock b/composer.lock index c8c1e3c192..84e7ac9cb3 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": "51ff891ef6cee8a3f8c4e5187b7fd479", + "content-hash": "35fd85e8d566d20d8177266469c5ebcb", "packages": [ { "name": "adhocore/jwt", @@ -3497,16 +3497,16 @@ }, { "name": "utopia-php/database", - "version": "0.65.0", + "version": "0.66.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "e589efdc5da1216523a758e8af358866d4fb563f" + "reference": "67d2ab418efba31dc76b3564cf043e2b3f98d027" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/e589efdc5da1216523a758e8af358866d4fb563f", - "reference": "e589efdc5da1216523a758e8af358866d4fb563f", + "url": "https://api.github.com/repos/utopia-php/database/zipball/67d2ab418efba31dc76b3564cf043e2b3f98d027", + "reference": "67d2ab418efba31dc76b3564cf043e2b3f98d027", "shasum": "" }, "require": { @@ -3547,9 +3547,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.65.0" + "source": "https://github.com/utopia-php/database/tree/0.66.0" }, - "time": "2025-04-14T07:39:01+00:00" + "time": "2025-04-16T07:10:27+00:00" }, { "name": "utopia-php/domains", From b7c2220648fb356996f386f884080e52e0f9338e Mon Sep 17 00:00:00 2001 From: arnab Date: Wed, 16 Apr 2025 17:02:43 +0530 Subject: [PATCH 02/11] added exception and errors --- app/config/errors.php | 5 +++++ src/Appwrite/Extend/Exception.php | 1 + 2 files changed, 6 insertions(+) diff --git a/app/config/errors.php b/app/config/errors.php index 7c7f6dc9ec..4b2e58b08b 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -593,6 +593,11 @@ return [ 'description' => 'Database timed out. Try adjusting your queries or adding an index.', 'code' => 408 ], + Exception::DATABASE_QUERY_ORDER_NULL => [ + 'name' => Exception::DATABASE_QUERY_ORDER_NULL, + 'description' => 'The order attribute had a null value. Cursor pagination requires all documents order attribute values are non-null.', + 'code' => 400, + ], /** Collections */ Exception::COLLECTION_NOT_FOUND => [ diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index d4f47ca177..2db5a840bc 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -175,6 +175,7 @@ class Exception extends \Exception public const DATABASE_NOT_FOUND = 'database_not_found'; public const DATABASE_ALREADY_EXISTS = 'database_already_exists'; public const DATABASE_TIMEOUT = 'database_timeout'; + public const DATABASE_QUERY_ORDER_NULL = 'database_query_order_null'; /** Collections */ public const COLLECTION_NOT_FOUND = 'collection_not_found'; From 8e4bd363d2e720f9e1f113b06ad43b8734d5186d Mon Sep 17 00:00:00 2001 From: arnab Date: Wed, 16 Apr 2025 17:03:14 +0530 Subject: [PATCH 03/11] added catching of order exception in database controllers --- app/controllers/api/databases.php | 59 +++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 4b9d3d18f2..dfebfe2069 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -31,6 +31,7 @@ use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Index as IndexException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; +use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Exception\Structure as StructureException; @@ -596,10 +597,15 @@ App::get('/v1/databases') $filterQueries = Query::groupByType($queries)['filters']; - $response->dynamic(new Document([ - 'databases' => $dbForProject->find('databases', $queries), - 'total' => $dbForProject->count('databases', $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_DATABASE_LIST); + try { + $response->dynamic(new Document([ + 'databases' => $dbForProject->find('databases', $queries), + 'total' => $dbForProject->count('databases', $filterQueries, APP_LIMIT_COUNT), + ]), Response::MODEL_DATABASE_LIST); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } }); App::get('/v1/databases/:databaseId') @@ -970,11 +976,15 @@ App::get('/v1/databases/:databaseId/collections') } $filterQueries = Query::groupByType($queries)['filters']; - - $response->dynamic(new Document([ - 'collections' => $dbForProject->find('database_' . $database->getInternalId(), $queries), - 'total' => $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_COLLECTION_LIST); + try { + $response->dynamic(new Document([ + 'collections' => $dbForProject->find('database_' . $database->getInternalId(), $queries), + 'total' => $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT), + ]), Response::MODEL_COLLECTION_LIST); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } }); App::get('/v1/databases/:databaseId/collections/:collectionId') @@ -1982,8 +1992,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') } $filters = Query::groupByType($queries)['filters']; - - $attributes = $dbForProject->find('attributes', $queries); + try { + $attributes = $dbForProject->find('attributes', $queries); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } $total = $dbForProject->count('attributes', $filters, APP_LIMIT_COUNT); $response->dynamic(new Document([ @@ -2980,10 +2994,15 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') } $filterQueries = Query::groupByType($queries)['filters']; - $response->dynamic(new Document([ - 'total' => $dbForProject->count('indexes', $filterQueries, APP_LIMIT_COUNT), - 'indexes' => $dbForProject->find('indexes', $queries), - ]), Response::MODEL_INDEX_LIST); + try { + $response->dynamic(new Document([ + 'total' => $dbForProject->count('indexes', $filterQueries, APP_LIMIT_COUNT), + 'indexes' => $dbForProject->find('indexes', $queries), + ]), Response::MODEL_INDEX_LIST); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } }); App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') @@ -3445,9 +3464,13 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $cursor->setValue($cursorDocument); } - - $documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries); - $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT); + try { + $documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries); + $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } $operations = 0; From 1cacb87c655751ae62784cd753e5b6f80dbcd047 Mon Sep 17 00:00:00 2001 From: arnab Date: Wed, 16 Apr 2025 17:09:13 +0530 Subject: [PATCH 04/11] added order query exception handling in the storage --- app/controllers/api/storage.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index f13c9703c5..388a015cb5 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -24,6 +24,7 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\NotFound as NotFoundException; +use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; @@ -216,11 +217,15 @@ App::get('/v1/storage/buckets') } $filterQueries = Query::groupByType($queries)['filters']; - - $response->dynamic(new Document([ - 'buckets' => $dbForProject->find('buckets', $queries), - 'total' => $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_BUCKET_LIST); + try { + $response->dynamic(new Document([ + 'buckets' => $dbForProject->find('buckets', $queries), + 'total' => $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT), + ]), Response::MODEL_BUCKET_LIST); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } }); App::get('/v1/storage/buckets/:bucketId') @@ -837,6 +842,9 @@ App::get('/v1/storage/buckets/:bucketId/files') } } catch (NotFoundException) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); } $response->dynamic(new Document([ From 3c602e84e6d2a44d59db7d86dcdd7688d179cb90 Mon Sep 17 00:00:00 2001 From: arnab Date: Wed, 16 Apr 2025 17:09:53 +0530 Subject: [PATCH 05/11] added order exception in teams --- app/controllers/api/teams.php | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 3e0e366b6b..1a117999f2 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -33,6 +33,7 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Duplicate; +use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; @@ -204,8 +205,12 @@ App::get('/v1/teams') } $filterQueries = Query::groupByType($queries)['filters']; - - $results = $dbForProject->find('teams', $queries); + try { + $results = $dbForProject->find('teams', $queries); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL,$message); + } $total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT); $response->dynamic(new Document([ @@ -859,11 +864,15 @@ App::get('/v1/teams/:teamId/memberships') } $filterQueries = Query::groupByType($queries)['filters']; - - $memberships = $dbForProject->find( - collection: 'memberships', - queries: $queries, - ); + try { + $memberships = $dbForProject->find( + collection: 'memberships', + queries: $queries, + ); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL,$message); + } $total = $dbForProject->count( collection: 'memberships', From 6e422ebe3cd246fe8c136452ad7271f31bbb5d75 Mon Sep 17 00:00:00 2001 From: arnab Date: Wed, 16 Apr 2025 17:16:39 +0530 Subject: [PATCH 06/11] added teams and users order exception --- app/controllers/api/teams.php | 4 ++-- app/controllers/api/users.php | 43 +++++++++++++++++++++++------------ 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 1a117999f2..f9f663acb1 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -209,7 +209,7 @@ App::get('/v1/teams') $results = $dbForProject->find('teams', $queries); } catch (OrderException $e) { $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL,$message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); } $total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT); @@ -871,7 +871,7 @@ App::get('/v1/teams/:teamId/memberships') ); } catch (OrderException $e) { $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL,$message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); } $total = $dbForProject->count( diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 4a551b7478..0b1b90f800 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -33,6 +33,7 @@ use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate; +use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; @@ -630,11 +631,15 @@ App::get('/v1/users') } $filterQueries = Query::groupByType($queries)['filters']; - - $response->dynamic(new Document([ - 'users' => $dbForProject->find('users', $queries), - 'total' => $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_USER_LIST); + try { + $response->dynamic(new Document([ + 'users' => $dbForProject->find('users', $queries), + 'total' => $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT), + ]), Response::MODEL_USER_LIST); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } }); App::get('/v1/users/:userId') @@ -980,11 +985,15 @@ App::get('/v1/users/:userId/targets') $cursor->setValue($cursorDocument); } - - $response->dynamic(new Document([ - 'targets' => $dbForProject->find('targets', $queries), - 'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT), - ]), Response::MODEL_TARGET_LIST); + try { + $response->dynamic(new Document([ + 'targets' => $dbForProject->find('targets', $queries), + 'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT), + ]), Response::MODEL_TARGET_LIST); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } }); App::get('/v1/users/identities') @@ -1045,11 +1054,15 @@ App::get('/v1/users/identities') } $filterQueries = Query::groupByType($queries)['filters']; - - $response->dynamic(new Document([ - 'identities' => $dbForProject->find('identities', $queries), - 'total' => $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_IDENTITY_LIST); + try { + $response->dynamic(new Document([ + 'identities' => $dbForProject->find('identities', $queries), + 'total' => $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT), + ]), Response::MODEL_IDENTITY_LIST); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } }); App::patch('/v1/users/:userId/status') From 8d3070416a614bec7e68ffd6bd624a54b3fdd17e Mon Sep 17 00:00:00 2001 From: arnab Date: Wed, 16 Apr 2025 17:29:36 +0530 Subject: [PATCH 07/11] added order exception in rest of the controllers --- app/controllers/api/account.php | 9 ++++- app/controllers/api/functions.php | 31 +++++++++----- app/controllers/api/messaging.php | 65 ++++++++++++++++++++---------- app/controllers/api/migrations.php | 15 ++++--- 4 files changed, 82 insertions(+), 38 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 20f64496ac..85a7982f3c 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -43,6 +43,7 @@ use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate; +use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; @@ -4845,8 +4846,12 @@ App::get('/v1/account/identities') } $filterQueries = Query::groupByType($queries)['filters']; - - $results = $dbForProject->find('identities', $queries); + try { + $results = $dbForProject->find('identities', $queries); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } $total = $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT); $response->dynamic(new Document([ diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index c7f24f984a..5ce8068c87 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -37,6 +37,7 @@ use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; +use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; @@ -483,11 +484,15 @@ App::get('/v1/functions') } $filterQueries = Query::groupByType($queries)['filters']; - - $response->dynamic(new Document([ - 'functions' => $dbForProject->find('functions', $queries), - 'total' => $dbForProject->count('functions', $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_FUNCTION_LIST); + try { + $response->dynamic(new Document([ + 'functions' => $dbForProject->find('functions', $queries), + 'total' => $dbForProject->count('functions', $filterQueries, APP_LIMIT_COUNT), + ]), Response::MODEL_FUNCTION_LIST); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } }); App::get('/v1/functions/runtimes') @@ -1537,8 +1542,12 @@ App::get('/v1/functions/:functionId/deployments') } $filterQueries = Query::groupByType($queries)['filters']; - - $results = $dbForProject->find('deployments', $queries); + try { + $results = $dbForProject->find('deployments', $queries); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } $total = $dbForProject->count('deployments', $filterQueries, APP_LIMIT_COUNT); foreach ($results as $result) { @@ -2331,8 +2340,12 @@ App::get('/v1/functions/:functionId/executions') } $filterQueries = Query::groupByType($queries)['filters']; - - $results = $dbForProject->find('executions', $queries); + try { + $results = $dbForProject->find('executions', $queries); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } $total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT); $roles = Authorization::getRoles(); diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 178266db60..5683f05539 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -30,6 +30,7 @@ use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; +use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; @@ -951,11 +952,15 @@ App::get('/v1/messaging/providers') $cursor->setValue($cursorDocument); } - - $response->dynamic(new Document([ - 'providers' => $dbForProject->find('providers', $queries), - 'total' => $dbForProject->count('providers', $queries, APP_LIMIT_COUNT), - ]), Response::MODEL_PROVIDER_LIST); + try { + $response->dynamic(new Document([ + 'providers' => $dbForProject->find('providers', $queries), + 'total' => $dbForProject->count('providers', $queries, APP_LIMIT_COUNT), + ]), Response::MODEL_PROVIDER_LIST); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } }); App::get('/v1/messaging/providers/:providerId/logs') @@ -2181,11 +2186,15 @@ App::get('/v1/messaging/topics') $cursor->setValue($cursorDocument[0]); } - - $response->dynamic(new Document([ - 'topics' => $dbForProject->find('topics', $queries), - 'total' => $dbForProject->count('topics', $queries, APP_LIMIT_COUNT), - ]), Response::MODEL_TOPIC_LIST); + try { + $response->dynamic(new Document([ + 'topics' => $dbForProject->find('topics', $queries), + 'total' => $dbForProject->count('topics', $queries, APP_LIMIT_COUNT), + ]), Response::MODEL_TOPIC_LIST); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } }); App::get('/v1/messaging/topics/:topicId/logs') @@ -2579,8 +2588,12 @@ App::get('/v1/messaging/topics/:topicId/subscribers') $cursor->setValue($cursorDocument); } - - $subscribers = $dbForProject->find('subscribers', $queries); + try { + $subscribers = $dbForProject->find('subscribers', $queries); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } $subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) { return function () use ($subscriber, $dbForProject) { @@ -3360,11 +3373,15 @@ App::get('/v1/messaging/messages') $cursor->setValue($cursorDocument); } - - $response->dynamic(new Document([ - 'messages' => $dbForProject->find('messages', $queries), - 'total' => $dbForProject->count('messages', $queries, APP_LIMIT_COUNT), - ]), Response::MODEL_MESSAGE_LIST); + try { + $response->dynamic(new Document([ + 'messages' => $dbForProject->find('messages', $queries), + 'total' => $dbForProject->count('messages', $queries, APP_LIMIT_COUNT), + ]), Response::MODEL_MESSAGE_LIST); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } }); App::get('/v1/messaging/messages/:messageId/logs') @@ -3533,11 +3550,15 @@ App::get('/v1/messaging/messages/:messageId/targets') $cursor->setValue($cursorDocument); } - - $response->dynamic(new Document([ - 'targets' => $dbForProject->find('targets', $queries), - 'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT), - ]), Response::MODEL_TARGET_LIST); + try { + $response->dynamic(new Document([ + 'targets' => $dbForProject->find('targets', $queries), + 'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT), + ]), Response::MODEL_TARGET_LIST); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } }); App::get('/v1/messaging/messages/:messageId') diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index 4061f2c2c4..17327ea764 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -12,6 +12,7 @@ use Appwrite\Utopia\Response; use Utopia\App; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; @@ -347,11 +348,15 @@ App::get('/v1/migrations') } $filterQueries = Query::groupByType($queries)['filters']; - - $response->dynamic(new Document([ - 'migrations' => $dbForProject->find('migrations', $queries), - 'total' => $dbForProject->count('migrations', $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_MIGRATION_LIST); + try { + $response->dynamic(new Document([ + 'migrations' => $dbForProject->find('migrations', $queries), + 'total' => $dbForProject->count('migrations', $filterQueries, APP_LIMIT_COUNT), + ]), Response::MODEL_MIGRATION_LIST); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } }); App::get('/v1/migrations/:migrationId') From ecd77cb2e361ab614f958d857d1199a2cf7a5fe3 Mon Sep 17 00:00:00 2001 From: arnab Date: Wed, 16 Apr 2025 17:53:39 +0530 Subject: [PATCH 08/11] added order exception in projects and vcs --- app/controllers/api/projects.php | 15 ++++++++++----- app/controllers/api/vcs.php | 9 +++++++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index b29bd227aa..1762c565c7 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -28,6 +28,7 @@ use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate; +use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; @@ -357,11 +358,15 @@ App::get('/v1/projects') } $filterQueries = Query::groupByType($queries)['filters']; - - $response->dynamic(new Document([ - 'projects' => $dbForPlatform->find('projects', $queries), - 'total' => $dbForPlatform->count('projects', $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_PROJECT_LIST); + try { + $response->dynamic(new Document([ + 'projects' => $dbForPlatform->find('projects', $queries), + 'total' => $dbForPlatform->count('projects', $filterQueries, APP_LIMIT_COUNT), + ]), Response::MODEL_PROJECT_LIST); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } }); App::get('/v1/projects/:projectId') diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 2c145febcc..a230ae5d0c 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -19,6 +19,7 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; +use Utopia\Database\Exception\Order as OrderException; use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; @@ -1113,8 +1114,12 @@ App::get('/v1/vcs/installations') } $filterQueries = Query::groupByType($queries)['filters']; - - $results = $dbForPlatform->find('installations', $queries); + try { + $results = $dbForPlatform->find('installations', $queries); + } catch (OrderException $e) { + $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + } $total = $dbForPlatform->count('installations', $filterQueries, APP_LIMIT_COUNT); $response->dynamic(new Document([ From b2f7ed48dd3e27a5770861d5f7118bd73f5e0981 Mon Sep 17 00:00:00 2001 From: ArnabChatterjee20k Date: Thu, 17 Apr 2025 10:16:26 +0530 Subject: [PATCH 09/11] Simplify constructor usage by inlining message; limit try/catch to DB operations --- app/controllers/api/account.php | 3 +- app/controllers/api/databases.php | 49 +++++++++++++------------- app/controllers/api/functions.php | 23 ++++++------- app/controllers/api/messaging.php | 55 ++++++++++++++++-------------- app/controllers/api/migrations.php | 13 +++---- app/controllers/api/projects.php | 13 +++---- app/controllers/api/storage.php | 16 ++++----- app/controllers/api/teams.php | 18 +++++----- app/controllers/api/users.php | 39 +++++++++++---------- app/controllers/api/vcs.php | 5 ++- 10 files changed, 120 insertions(+), 114 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 85a7982f3c..55bfd69442 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -4849,8 +4849,7 @@ App::get('/v1/account/identities') try { $results = $dbForProject->find('identities', $queries); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } $total = $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT); diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index dfebfe2069..28ee3ebdf9 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -598,14 +598,15 @@ App::get('/v1/databases') $filterQueries = Query::groupByType($queries)['filters']; try { - $response->dynamic(new Document([ - 'databases' => $dbForProject->find('databases', $queries), - 'total' => $dbForProject->count('databases', $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_DATABASE_LIST); + $databases = $dbForProject->find('databases', $queries); + $total = $dbForProject->count('databases', $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + $response->dynamic(new Document([ + 'databases' => $databases, + 'total' => $total, + ]), Response::MODEL_DATABASE_LIST); }); App::get('/v1/databases/:databaseId') @@ -976,15 +977,17 @@ App::get('/v1/databases/:databaseId/collections') } $filterQueries = Query::groupByType($queries)['filters']; + try { - $response->dynamic(new Document([ - 'collections' => $dbForProject->find('database_' . $database->getInternalId(), $queries), - 'total' => $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_COLLECTION_LIST); + $collections = $dbForProject->find('database_' . $database->getInternalId(), $queries); + $total = $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + $response->dynamic(new Document([ + 'collections' => $collections, + 'total' => $total, + ]), Response::MODEL_COLLECTION_LIST); }); App::get('/v1/databases/:databaseId/collections/:collectionId') @@ -1994,11 +1997,10 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') $filters = Query::groupByType($queries)['filters']; try { $attributes = $dbForProject->find('attributes', $queries); + $total = $dbForProject->count('attributes', $filters, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } - $total = $dbForProject->count('attributes', $filters, APP_LIMIT_COUNT); $response->dynamic(new Document([ 'attributes' => $attributes, @@ -2995,14 +2997,16 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') $filterQueries = Query::groupByType($queries)['filters']; try { - $response->dynamic(new Document([ - 'total' => $dbForProject->count('indexes', $filterQueries, APP_LIMIT_COUNT), - 'indexes' => $dbForProject->find('indexes', $queries), - ]), Response::MODEL_INDEX_LIST); + $total = $dbForProject->count('indexes', $filterQueries, APP_LIMIT_COUNT); + $indexes = $dbForProject->find('indexes', $queries); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + + $response->dynamic(new Document([ + 'total' => $total, + 'indexes' => $indexes, + ]), Response::MODEL_INDEX_LIST); }); App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') @@ -3468,8 +3472,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries); $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } $operations = 0; diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 5ce8068c87..ceb167f705 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -485,14 +485,15 @@ App::get('/v1/functions') $filterQueries = Query::groupByType($queries)['filters']; try { - $response->dynamic(new Document([ - 'functions' => $dbForProject->find('functions', $queries), - 'total' => $dbForProject->count('functions', $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_FUNCTION_LIST); + $functions = $dbForProject->find('functions', $queries); + $total = $dbForProject->count('functions', $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + $response->dynamic(new Document([ + 'functions' => $functions, + 'total' => $total, + ]), Response::MODEL_FUNCTION_LIST); }); App::get('/v1/functions/runtimes') @@ -1544,11 +1545,10 @@ App::get('/v1/functions/:functionId/deployments') $filterQueries = Query::groupByType($queries)['filters']; try { $results = $dbForProject->find('deployments', $queries); + $total = $dbForProject->count('deployments', $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } - $total = $dbForProject->count('deployments', $filterQueries, APP_LIMIT_COUNT); foreach ($results as $result) { $build = $dbForProject->getDocument('builds', $result->getAttribute('buildId', '')); @@ -2342,11 +2342,10 @@ App::get('/v1/functions/:functionId/executions') $filterQueries = Query::groupByType($queries)['filters']; try { $results = $dbForProject->find('executions', $queries); + $total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } - $total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT); $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 5683f05539..9dbd2f3403 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -953,14 +953,15 @@ App::get('/v1/messaging/providers') $cursor->setValue($cursorDocument); } try { - $response->dynamic(new Document([ - 'providers' => $dbForProject->find('providers', $queries), - 'total' => $dbForProject->count('providers', $queries, APP_LIMIT_COUNT), - ]), Response::MODEL_PROVIDER_LIST); + $providers = $dbForProject->find('providers', $queries); + $total = $dbForProject->count('providers', $queries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + $response->dynamic(new Document([ + 'providers' => $providers, + 'total' => $total, + ]), Response::MODEL_PROVIDER_LIST); }); App::get('/v1/messaging/providers/:providerId/logs') @@ -2187,14 +2188,15 @@ App::get('/v1/messaging/topics') $cursor->setValue($cursorDocument[0]); } try { - $response->dynamic(new Document([ - 'topics' => $dbForProject->find('topics', $queries), - 'total' => $dbForProject->count('topics', $queries, APP_LIMIT_COUNT), - ]), Response::MODEL_TOPIC_LIST); + $topics = $dbForProject->find('topics', $queries); + $total = $dbForProject->count('topics', $queries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + $response->dynamic(new Document([ + 'topics' => $topics, + 'total' => $total, + ]), Response::MODEL_TOPIC_LIST); }); App::get('/v1/messaging/topics/:topicId/logs') @@ -2591,8 +2593,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers') try { $subscribers = $dbForProject->find('subscribers', $queries); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } $subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) { @@ -3374,14 +3375,15 @@ App::get('/v1/messaging/messages') $cursor->setValue($cursorDocument); } try { - $response->dynamic(new Document([ - 'messages' => $dbForProject->find('messages', $queries), - 'total' => $dbForProject->count('messages', $queries, APP_LIMIT_COUNT), - ]), Response::MODEL_MESSAGE_LIST); + $messages = $dbForProject->find('messages', $queries); + $total = $dbForProject->count('messages', $queries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + $response->dynamic(new Document([ + 'messages' => $messages, + 'total' => $total, + ]), Response::MODEL_MESSAGE_LIST); }); App::get('/v1/messaging/messages/:messageId/logs') @@ -3551,14 +3553,15 @@ App::get('/v1/messaging/messages/:messageId/targets') $cursor->setValue($cursorDocument); } try { - $response->dynamic(new Document([ - 'targets' => $dbForProject->find('targets', $queries), - 'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT), - ]), Response::MODEL_TARGET_LIST); + $targets = $dbForProject->find('targets', $queries); + $total = $dbForProject->count('targets', $queries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + $response->dynamic(new Document([ + 'targets' => $targets, + 'total' => $total, + ]), Response::MODEL_TARGET_LIST); }); App::get('/v1/messaging/messages/:messageId') diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index 17327ea764..9d8d69bc1b 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -349,14 +349,15 @@ App::get('/v1/migrations') $filterQueries = Query::groupByType($queries)['filters']; try { - $response->dynamic(new Document([ - 'migrations' => $dbForProject->find('migrations', $queries), - 'total' => $dbForProject->count('migrations', $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_MIGRATION_LIST); + $migrations = $dbForProject->find('migrations', $queries); + $total = $dbForProject->count('migrations', $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + $response->dynamic(new Document([ + 'migrations' => $migrations, + 'total' => $total, + ]), Response::MODEL_MIGRATION_LIST); }); App::get('/v1/migrations/:migrationId') diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 1762c565c7..22facc0b23 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -359,14 +359,15 @@ App::get('/v1/projects') $filterQueries = Query::groupByType($queries)['filters']; try { - $response->dynamic(new Document([ - 'projects' => $dbForPlatform->find('projects', $queries), - 'total' => $dbForPlatform->count('projects', $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_PROJECT_LIST); + $projects = $dbForPlatform->find('projects', $queries); + $total = $dbForPlatform->count('projects', $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + $response->dynamic(new Document([ + 'projects' => $projects, + 'total' => $total, + ]), Response::MODEL_PROJECT_LIST); }); App::get('/v1/projects/:projectId') diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 388a015cb5..398369317f 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -218,14 +218,15 @@ App::get('/v1/storage/buckets') $filterQueries = Query::groupByType($queries)['filters']; try { - $response->dynamic(new Document([ - 'buckets' => $dbForProject->find('buckets', $queries), - 'total' => $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_BUCKET_LIST); + $buckets = $dbForProject->find('buckets', $queries); + $total = $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + $response->dynamic(new Document([ + 'buckets' => $buckets, + 'total' => $total, + ]), Response::MODEL_BUCKET_LIST); }); App::get('/v1/storage/buckets/:bucketId') @@ -843,8 +844,7 @@ App::get('/v1/storage/buckets/:bucketId/files') } catch (NotFoundException) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } $response->dynamic(new Document([ diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index f9f663acb1..e5d3bb8bd0 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -207,11 +207,10 @@ App::get('/v1/teams') $filterQueries = Query::groupByType($queries)['filters']; try { $results = $dbForProject->find('teams', $queries); + $total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } - $total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT); $response->dynamic(new Document([ 'teams' => $results, @@ -869,16 +868,15 @@ App::get('/v1/teams/:teamId/memberships') collection: 'memberships', queries: $queries, ); + $total = $dbForProject->count( + collection: 'memberships', + queries: $filterQueries, + max: APP_LIMIT_COUNT + ); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } - $total = $dbForProject->count( - collection: 'memberships', - queries: $filterQueries, - max: APP_LIMIT_COUNT - ); $memberships = array_filter($memberships, fn (Document $membership) => !empty($membership->getAttribute('userId'))); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 0b1b90f800..4a02b94bdd 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -632,14 +632,15 @@ App::get('/v1/users') $filterQueries = Query::groupByType($queries)['filters']; try { - $response->dynamic(new Document([ - 'users' => $dbForProject->find('users', $queries), - 'total' => $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_USER_LIST); + $users = $dbForProject->find('users', $queries); + $total = $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + $response->dynamic(new Document([ + 'users' => $users, + 'total' => $total, + ]), Response::MODEL_USER_LIST); }); App::get('/v1/users/:userId') @@ -986,14 +987,15 @@ App::get('/v1/users/:userId/targets') $cursor->setValue($cursorDocument); } try { - $response->dynamic(new Document([ - 'targets' => $dbForProject->find('targets', $queries), - 'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT), - ]), Response::MODEL_TARGET_LIST); + $targets = $dbForProject->find('targets', $queries); + $total = $dbForProject->count('targets', $queries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + $response->dynamic(new Document([ + 'targets' => $targets, + 'total' => $total, + ]), Response::MODEL_TARGET_LIST); }); App::get('/v1/users/identities') @@ -1055,14 +1057,15 @@ App::get('/v1/users/identities') $filterQueries = Query::groupByType($queries)['filters']; try { - $response->dynamic(new Document([ - 'identities' => $dbForProject->find('identities', $queries), - 'total' => $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT), - ]), Response::MODEL_IDENTITY_LIST); + $identities = $dbForProject->find('identities', $queries); + $total = $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + $response->dynamic(new Document([ + 'identities' => $identities, + 'total' => $total, + ]), Response::MODEL_IDENTITY_LIST); }); App::patch('/v1/users/:userId/status') diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index a230ae5d0c..e09367f790 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -1116,11 +1116,10 @@ App::get('/v1/vcs/installations') $filterQueries = Query::groupByType($queries)['filters']; try { $results = $dbForPlatform->find('installations', $queries); + $total = $dbForPlatform->count('installations', $filterQueries, APP_LIMIT_COUNT); } catch (OrderException $e) { - $message = "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."; - throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, $message); + throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } - $total = $dbForPlatform->count('installations', $filterQueries, APP_LIMIT_COUNT); $response->dynamic(new Document([ 'installations' => $results, From fb6ec704a19bca5ef5e0402d3cf7e66faa31e0a6 Mon Sep 17 00:00:00 2001 From: arnab Date: Thu, 17 Apr 2025 14:44:59 +0530 Subject: [PATCH 10/11] test case added --- .../e2e/Services/Databases/DatabasesBase.php | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index dca832ac01..8066c15ec9 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -1641,9 +1641,63 @@ trait DatabasesBase $this->assertEquals(2019, $documents['body']['documents'][0]['releaseYear']); $this->assertCount(3, $documents['body']['documents']); + + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/attributes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'queries' => [ + Query::equal('type', ['string'])->toString(), + Query::cursorAfter(new Document(['$id' => 'title']))->toString() + ], + ]); + + + $patchNull = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/attributes/string/description', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'default' => null, + 'required' => false, + ]); + $document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => ID::unique(), + 'data' => [ + 'title' => 'Dummy', + 'releaseYear' => 1944, + 'birthDay' => '1975-06-12 14:12:55+02:00', + 'actors' => [ + 'Dummy', + ], + ] + ]); + + $this->assertEquals(201, $document1['headers']['status-code']); + $documentsPaginated = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::orderAsc('dummy')->toString(), + Query::cursorAfter(new Document(['$id' => $document1['body']['$id']]))->toString() + ], + ]); + + $this->assertEquals(400, $documentsPaginated['headers']['status-code']); + $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $document1['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); return ['documents' => $documents['body']['documents'], 'databaseId' => $databaseId]; } + /** * @depends testListDocuments */ From e6e28f32ffe2079f14a41d93abcf629b9309411f Mon Sep 17 00:00:00 2001 From: arnab Date: Thu, 17 Apr 2025 14:59:37 +0530 Subject: [PATCH 11/11] added comments for test case --- .../e2e/Services/Databases/DatabasesBase.php | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 8066c15ec9..0c7e1d386e 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -1641,20 +1641,7 @@ trait DatabasesBase $this->assertEquals(2019, $documents['body']['documents'][0]['releaseYear']); $this->assertCount(3, $documents['body']['documents']); - - - $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/attributes', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'], - ]), [ - 'queries' => [ - Query::equal('type', ['string'])->toString(), - Query::cursorAfter(new Document(['$id' => 'title']))->toString() - ], - ]); - - + // changing description attribute to be null by default instead of empty string $patchNull = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/attributes/string/description', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1663,6 +1650,7 @@ trait DatabasesBase 'default' => null, 'required' => false, ]); + // creating a dummy doc with null description $document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1679,6 +1667,7 @@ trait DatabasesBase ]); $this->assertEquals(201, $document1['headers']['status-code']); + // fetching docs with cursor after the dummy doc with order attr description which is null $documentsPaginated = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1688,8 +1677,10 @@ trait DatabasesBase Query::cursorAfter(new Document(['$id' => $document1['body']['$id']]))->toString() ], ]); - + // should throw 400 as the order attr description of the selected doc is null $this->assertEquals(400, $documentsPaginated['headers']['status-code']); + + // deleting the dummy doc created $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/' . $document1['body']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'],