diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 649c346452..00d86b7007 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -1056,12 +1056,12 @@ App::get('/v1/database/collections/:collectionId/documents') ->param('queries', [], new ArrayList(new Text(128)), 'Array of query strings.', true) ->param('limit', 25, new Range(0, 100), 'Maximum number of documents to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true) ->param('offset', 0, new Range(0, 900000000), 'Offset value. The default value is 0. Use this param to manage pagination.', true) - // TODO@kodumbeats 'after' param for pagination ->param('orderAttributes', [], new ArrayList(new Text(128)), 'Array of attributes used to sort results.', true) ->param('orderTypes', [], new ArrayList(new WhiteList(['DESC', 'ASC'], true)), 'Array of order directions for sorting attribtues. Possible values are DESC for descending order, or ASC for ascending order.', true) + ->param('orderAfter', '', new UID(), 'ID of the document used to return documents listed after. Should be used for efficient pagination working with many documents.', true) ->inject('response') ->inject('dbForExternal') - ->action(function ($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $response, $dbForExternal) { + ->action(function ($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $orderAfter, $response, $dbForExternal) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForExternal */ @@ -1087,7 +1087,15 @@ App::get('/v1/database/collections/:collectionId/documents') throw new Exception($validator->getDescription(), 400); } - $documents = $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes); + if (!empty($orderAfter)) { + $orderAfterDocument = $dbForExternal->getDocument($collectionId, $orderAfter); + + if ($orderAfterDocument->isEmpty()) { + throw new Exception('Document for orderAfter not found', 400); + } + } + + $documents = $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $orderAfterDocument ?? null); $response->dynamic(new Document([ 'sum' => \count($documents), diff --git a/composer.json b/composer.json index f595bfd1a8..47cfe2d005 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": "0.6.*", + "utopia-php/database": "dev-main as 0.6.0", "utopia-php/locale": "0.3.*", "utopia-php/registry": "0.5.*", "utopia-php/preloader": "0.2.*", diff --git a/composer.lock b/composer.lock index b43adc9166..a1bd64bb84 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": "f9727be2725e573a92b2d1aeeb77b421", + "content-hash": "1669de851a29702eba426a09a6871921", "packages": [ { "name": "adhocore/jwt", @@ -1984,16 +1984,16 @@ }, { "name": "utopia-php/database", - "version": "0.6.0", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "561adc215fce3bd3b8c3ebb971ca354fb1526f26" + "reference": "c48b60884f63a547520ecef35714827af1870bc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/561adc215fce3bd3b8c3ebb971ca354fb1526f26", - "reference": "561adc215fce3bd3b8c3ebb971ca354fb1526f26", + "url": "https://api.github.com/repos/utopia-php/database/zipball/c48b60884f63a547520ecef35714827af1870bc4", + "reference": "c48b60884f63a547520ecef35714827af1870bc4", "shasum": "" }, "require": { @@ -2011,6 +2011,7 @@ "utopia-php/cli": "^0.11.0", "vimeo/psalm": "4.0.1" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -2041,9 +2042,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.6.0" + "source": "https://github.com/utopia-php/database/tree/main" }, - "time": "2021-08-03T15:13:48+00:00" + "time": "2021-08-04T16:53:44+00:00" }, { "name": "utopia-php/domains", @@ -6254,9 +6255,18 @@ "time": "2015-12-17T08:42:14+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "utopia-php/database", + "version": "dev-main", + "alias": "0.6.0", + "alias_normalized": "0.6.0.0" + } + ], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "utopia-php/database": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -6278,5 +6288,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.1.0" + "plugin-api-version": "2.0.0" } diff --git a/tests/e2e/Services/Database/DatabaseBase.php b/tests/e2e/Services/Database/DatabaseBase.php index 755321ed73..cae46e3ffe 100644 --- a/tests/e2e/Services/Database/DatabaseBase.php +++ b/tests/e2e/Services/Database/DatabaseBase.php @@ -289,6 +289,115 @@ trait DatabaseBase return []; } + /** + * @depends testCreateDocument + */ + public function testListDocumentsAfterPagination(array $data):array + { + /** + * Test after without order. + */ + $base = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals('Captain America', $base['body']['documents'][0]['title']); + $this->assertEquals('Spider-Man: Far From Home', $base['body']['documents'][1]['title']); + $this->assertEquals('Spider-Man: Homecoming', $base['body']['documents'][2]['title']); + $this->assertCount(3, $base['body']['documents']); + + $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'orderAfter' => $base['body']['documents'][0]['$id'] + ]); + + $this->assertEquals($base['body']['documents'][1]['$id'], $documents['body']['documents'][0]['$id']); + $this->assertEquals($base['body']['documents'][2]['$id'], $documents['body']['documents'][1]['$id']); + $this->assertCount(2, $documents['body']['documents']); + + $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'orderAfter' => $base['body']['documents'][2]['$id'] + ]); + + $this->assertEmpty($documents['body']['documents']); + + /** + * Test with ASC order and after. + */ + $base = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'orderAttributes' => ['releaseYear'], + 'orderTypes' => ['ASC'], + ]); + + $this->assertEquals(1944, $base['body']['documents'][0]['releaseYear']); + $this->assertEquals(2017, $base['body']['documents'][1]['releaseYear']); + $this->assertEquals(2019, $base['body']['documents'][2]['releaseYear']); + $this->assertCount(3, $base['body']['documents']); + + $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'orderAttributes' => ['releaseYear'], + 'orderTypes' => ['ASC'], + 'orderAfter' => $base['body']['documents'][1]['$id'] + ]); + + $this->assertEquals($base['body']['documents'][2]['$id'], $documents['body']['documents'][0]['$id']); + $this->assertCount(1, $documents['body']['documents']); + + /** + * Test with DESC order and after. + */ + $base = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'orderAttributes' => ['releaseYear'], + 'orderTypes' => ['DESC'], + ]); + + $this->assertEquals(1944, $base['body']['documents'][2]['releaseYear']); + $this->assertEquals(2017, $base['body']['documents'][1]['releaseYear']); + $this->assertEquals(2019, $base['body']['documents'][0]['releaseYear']); + $this->assertCount(3, $base['body']['documents']); + + $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'orderAttributes' => ['releaseYear'], + 'orderTypes' => ['DESC'], + 'orderAfter' => $base['body']['documents'][1]['$id'] + ]); + + $this->assertEquals($base['body']['documents'][2]['$id'], $documents['body']['documents'][0]['$id']); + $this->assertCount(1, $documents['body']['documents']); + + /** + * Test after with unknown document. + */ + $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'orderAfter' => 'unknown' + ]); + + $this->assertEquals($documents['headers']['status-code'], 400); + + return []; + } + /** * @depends testCreateDocument */ diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index 508866b6a1..6a614c02eb 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -34,7 +34,7 @@ trait StorageBase */ return ['fileId' => $file['body']['$id']]; } - + /** * @depends testCreateFile */ @@ -169,7 +169,7 @@ trait StorageBase /** * Test for FAILURE */ - + return $data; }