diff --git a/app/controllers/general.php b/app/controllers/general.php index caa31e13d2..6ecded2385 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -22,6 +22,7 @@ use Appwrite\Utopia\Request\Filters\V16 as RequestV16; use Appwrite\Utopia\Request\Filters\V17 as RequestV17; use Appwrite\Utopia\Request\Filters\V18 as RequestV18; use Appwrite\Utopia\Request\Filters\V19 as RequestV19; +use Appwrite\Utopia\Request\Filters\V20 as RequestV20; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Filters\V16 as ResponseV16; use Appwrite\Utopia\Response\Filters\V17 as ResponseV17; @@ -851,6 +852,10 @@ App::init() if (version_compare($requestFormat, '1.7.0', '<')) { $request->addFilter(new RequestV19()); } + if (version_compare($requestFormat, '1.8.0', '<')) { + $dbForProject = $getProjectDB($project); + $request->addFilter(new RequestV20($dbForProject, $route)); + } } $domain = $request->getHostname(); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php index 7376fe770a..0542123ee7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php @@ -87,7 +87,15 @@ class Get extends Action } try { - $document = $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId, $queries); + $selects = Query::groupByType($queries)['selections'] ?? []; + + if (! empty($selects)) { + // has selects, allow relationship on documents! + $document = $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId, $queries); + } else { + // has no selects, disable relationship looping on documents! + $document = $dbForProject->skipRelationships(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId, $queries)); + } } catch (QueryException $e) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index 2e76942db1..961ca2d5fd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -114,8 +114,21 @@ class XList extends Action $cursor->setValue($cursorDocument); } + + $selectQueries = []; + try { - $documents = $dbForProject->find('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $queries); + $selectQueries = Query::groupByType($queries)['selections'] ?? []; + + if (! empty($selectQueries)) { + // has selects, allow relationship on documents! + $documents = $dbForProject->find('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $queries); + } else { + // has no selects, disable relationship looping on documents! + /* @type Document[] $documents */ + $documents = $dbForProject->skipRelationships(fn () => $dbForProject->find('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $queries)); + } + $total = $dbForProject->count('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $queries, APP_LIMIT_COUNT); } catch (OrderException $e) { $documents = $this->isCollectionsAPI() ? 'documents' : 'rows'; @@ -150,24 +163,40 @@ class XList extends Action }, false); // Check if the SELECT query includes $databaseId and $collectionId + $hasWildcard = false; $hasDatabaseId = false; $hasCollectionId = false; - if ($select) { - $hasDatabaseId = \array_reduce($queries, function ($result, $query) { - return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$databaseId', $query->getValues())); - }, false); - $hasCollectionId = \array_reduce($queries, function ($result, $query) { - return $result || ($query->getMethod() === Query::TYPE_SELECT && \in_array('$collectionId', $query->getValues())); - }, false); - } + $hasSelectQueries = !empty($selectQueries); - if ($select) { - foreach ($documents as $document) { - if (!$hasDatabaseId) { - $document->removeAttribute('$databaseId'); + if ($hasSelectQueries) { + foreach ($selectQueries as $query) { + if ($query->getMethod() !== Query::TYPE_SELECT) { + continue; } - if (!$hasCollectionId) { - $document->removeAttribute('$collectionId'); + + $values = $query->getValues(); + if (\in_array('*', $values, true)) { + $hasWildcard = true; + break; + } + + if (\in_array('$databaseId', $values, true)) { + $hasDatabaseId = true; + } + + if (\in_array('$collectionId', $values, true)) { + $hasCollectionId = true; + } + } + + if (!$hasWildcard) { + foreach ($documents as $document) { + if (!$hasDatabaseId) { + $document->removeAttribute('$databaseId'); + } + if (!$hasCollectionId) { + $document->removeAttribute('$collectionId'); + } } } } diff --git a/src/Appwrite/Utopia/Request/Filter.php b/src/Appwrite/Utopia/Request/Filter.php index 59346c7e17..2a5fe4d394 100644 --- a/src/Appwrite/Utopia/Request/Filter.php +++ b/src/Appwrite/Utopia/Request/Filter.php @@ -2,8 +2,20 @@ namespace Appwrite\Utopia\Request; +use Utopia\Database\Database; +use Utopia\Route; + abstract class Filter { + private ?Route $route; + private ?Database $dbForProject; + + public function __construct(Database $dbForProject = null, Route $route = null) + { + $this->route = $route; + $this->dbForProject = $dbForProject; + } + /** * Parse params to another format. * @@ -13,4 +25,33 @@ abstract class Filter * @return array */ abstract public function parse(array $content, string $model): array; + + /** + * Get the database for the current project. + * + * @return null|Database + */ + public function getDbForProject(): ?Database + { + return $this->dbForProject; + } + + /** + * Returns the value of the given route param key, or a default if not found or on error. + * + * @param string $key + * @param mixed $default + * + * @return mixed + */ + public function getParamValue(string $key, mixed $default = ''): mixed + { + try { + $value = $this->route?->getParamValue($key) ?? $default; + } catch (\Exception $e) { + $value = $default; + } + + return $value; + } } diff --git a/src/Appwrite/Utopia/Request/Filters/V19.php b/src/Appwrite/Utopia/Request/Filters/V19.php index 8680cd642c..e7789ac0f7 100644 --- a/src/Appwrite/Utopia/Request/Filters/V19.php +++ b/src/Appwrite/Utopia/Request/Filters/V19.php @@ -39,7 +39,7 @@ class V19 extends Filter return $content; } - public function convertQueryAttribute(array $content, string $old, string $new) + public function convertQueryAttribute(array $content, string $old, string $new): array { if (isset($content['queries']) && is_array($content['queries'])) { foreach ($content['queries'] as $index => $query) { diff --git a/src/Appwrite/Utopia/Request/Filters/V20.php b/src/Appwrite/Utopia/Request/Filters/V20.php new file mode 100644 index 0000000000..8719cf7325 --- /dev/null +++ b/src/Appwrite/Utopia/Request/Filters/V20.php @@ -0,0 +1,124 @@ +manageSelectQueries($content, $model); + break; + } + return $content; + } + + /** + * From 1.8.x onward, related documents are no longer returned by default to improve performance. + * + * Use `Query::select(['related.*'])` for full documents or `Query::select(['related.key'])` for specific fields. + * + * This filter preserves 1.7.x behavior by including all related documents for backward compatibility with + * `listDocuments` and `getDocument` calls. + */ + protected function manageSelectQueries(array $content, string $model): array + { + $hasWildcard = false; + if (! isset($content['queries'])) { + $hasWildcard = true; + // only query, make it json encoded! + $content['queries'] = [Query::select(['*'])->toString()]; + } + + try { + $parsed = Query::parseQueries($content['queries']); + } catch (QueryException) { + // don't crash! + return $content; + } + + $selections = Query::groupByType($parsed)['selections'] ?? []; + + if (! $hasWildcard) { + // check if any select includes a wildcard as we added one above + foreach ($selections as $select) { + if (\in_array('*', $select->getValues(), true)) { + $hasWildcard = true; + break; + } + } + } + + if ($hasWildcard && $model === 'databases.listDocuments') { + $relatedKeys = $this->getRelatedCollectionKeys(); + + if (! empty($relatedKeys)) { + $selects = \array_values(\array_unique(\array_merge(['*'], $relatedKeys))); + + // remove previous select queries + $parsed = \array_filter( + $parsed, + fn ($query) => $query->getMethod() !== Query::TYPE_SELECT + ); + + // add wildcard + relationship(s) selects + $parsed[] = Query::select($selects); + } + } + + $resolvedQueries = []; + foreach ($parsed as $query) { + // make em json encoded! + $resolvedQueries[] = $query->toString(); + } + + $content['queries'] = $resolvedQueries; + + return $content; + } + + /** + * Returns all relationship attribute keys in `key.*` format for use with `Query::select`. + */ + private function getRelatedCollectionKeys(): array + { + $dbForProject = $this->getDbForProject(); + + if ($dbForProject === null) { + return []; + } + + $databaseId = $this->getParamValue('databaseId'); + $collectionId = $this->getParamValue('collectionId'); + + if (empty($databaseId) || empty($collectionId)) { + return []; + } + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + $collection = $dbForProject->getDocument( + 'database_' . $database->getSequence(), + $collectionId + ); + + $attributes = $collection->getAttribute('attributes', []); + + return \array_values(\array_map( + fn ($attr) => $attr['key'] . '.*', + \array_filter( + $attributes, + fn ($attr) => ($attr['type'] ?? null) === Database::VAR_RELATIONSHIP + ) + )); + } +} diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php index 53320e28a0..3af7383c4d 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php @@ -4563,7 +4563,11 @@ trait DatabasesBase $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $personCollection . '/documents/' . $person2['body']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['*', 'libraries.*'])->toString() + ] + ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertArrayNotHasKey('$collection', $response['body']); @@ -4573,7 +4577,11 @@ trait DatabasesBase $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $libraryCollection . '/documents/library11', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['person_one_to_many.$id'])->toString() + ] + ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertArrayHasKey('person_one_to_many', $response['body']); @@ -4723,7 +4731,11 @@ trait DatabasesBase $album = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $albums['body']['$id'] . '/documents/album1', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['*', 'artist.name', 'artist.$permissions'])->toString() + ] + ]); $this->assertEquals(200, $album['headers']['status-code']); $this->assertEquals('album1', $album['body']['$id']); @@ -4735,7 +4747,11 @@ trait DatabasesBase $artist = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $artists['body']['$id'] . '/documents/' . $album['body']['artist']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['*', 'albums.$id', 'albums.name', 'albums.$permissions'])->toString() + ] + ]); $this->assertEquals(200, $artist['headers']['status-code']); $this->assertEquals('Artist 1', $artist['body']['name']); @@ -4876,7 +4892,11 @@ trait DatabasesBase $sport = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $sports['body']['$id'] . '/documents/sport1', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['*', 'players.name', 'players.$permissions'])->toString() + ] + ]); $this->assertEquals(200, $sport['headers']['status-code']); $this->assertEquals('sport1', $sport['body']['$id']); @@ -4890,7 +4910,11 @@ trait DatabasesBase $player = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $players['body']['$id'] . '/documents/' . $sport['body']['players'][0]['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['*', 'sports.$id', 'sports.name', 'sports.$permissions'])->toString() + ] + ]); $this->assertEquals(200, $player['headers']['status-code']); $this->assertEquals('Player 1', $player['body']['name']); @@ -4918,6 +4942,7 @@ trait DatabasesBase ], $this->getHeaders()), [ 'queries' => [ Query::isNotNull('$id')->toString(), + Query::select(['*', 'libraries.*'])->toString(), Query::startsWith('fullName', 'Stevie')->toString(), Query::endsWith('fullName', 'Wonder')->toString(), Query::between('$createdAt', '1975-12-06', '2050-12-01')->toString(), diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php index 6dd125ebf5..84cd8aeb41 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php @@ -3794,7 +3794,11 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ]), [ + 'queries' => [ + Query::select(['new_level_2.*'])->toString() + ] + ]); $this->assertArrayHasKey('new_level_2', $newDocument['body']); $this->assertEquals(1, count($newDocument['body']['new_level_2'])); @@ -3904,7 +3908,11 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ]), [ + 'queries' => [ + Query::select(['new_level_2.*'])->toString() + ] + ]); $this->assertArrayHasKey('new_level_2', $newDocument['body']); $this->assertNotEmpty($newDocument['body']['new_level_2']); @@ -4014,7 +4022,11 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ]), [ + 'queries' => [ + Query::select(['new_level_2.*'])->toString() + ] + ]); $this->assertArrayHasKey('new_level_2', $newDocument['body']); $this->assertNotEmpty($newDocument['body']['new_level_2']); @@ -4025,7 +4037,11 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ]), [ + 'queries' => [ + Query::select(['*', 'level1.*'])->toString() + ] + ]); $this->assertArrayHasKey('level1', $level2Document['body']); $this->assertNotEmpty($level2Document['body']['level1']); @@ -4124,7 +4140,11 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ]), [ + 'queries' => [ + Query::select(['new_level_2.*'])->toString() + ] + ]); $this->assertArrayHasKey('new_level_2', $newDocument['body']); $this->assertNotEmpty($newDocument['body']['new_level_2']); @@ -4135,7 +4155,11 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ]), [ + 'queries' => [ + Query::select(['*', 'level1.*'])->toString() + ] + ]); $this->assertArrayHasKey('level1', $level2Document['body']); $this->assertNotEmpty($level2Document['body']['level1']); @@ -4480,6 +4504,14 @@ class DatabasesCustomServerTest extends Scope $createBulkDocuments(); + /** + * Wait for database to purge cache... + * + * This test specifically failed on 1.6.x response format, + * could be due to the slow or overworked machine, but being safe here! + */ + sleep(5); + // TEST: Update all documents $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/collections/' . $data['$id'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -4498,6 +4530,14 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertCount(10, $response['body']['documents']); + /** + * Wait for database to purge cache... + * + * This test specifically failed on 1.6.x response format, + * could be due to the slow or overworked machine, but being safe here! + */ + sleep(5); + $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/collections/' . $data['$id'] . '/documents', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], diff --git a/tests/e2e/Services/Databases/Tables/DatabasesBase.php b/tests/e2e/Services/Databases/Tables/DatabasesBase.php index 12426d78b2..29f92cc58a 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesBase.php @@ -4572,7 +4572,11 @@ trait DatabasesBase $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $personCollection . '/rows/' . $person2['body']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['*', 'libraries.*'])->toString() + ] + ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertArrayNotHasKey('$table', $response['body']); @@ -4582,7 +4586,11 @@ trait DatabasesBase $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $libraryCollection . '/rows/library11', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['person_one_to_many.$id'])->toString() + ] + ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertArrayHasKey('person_one_to_many', $response['body']); @@ -4732,7 +4740,11 @@ trait DatabasesBase $album = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $albums['body']['$id'] . '/rows/album1', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['*', 'artist.name', 'artist.$permissions'])->toString() + ] + ]); $this->assertEquals(200, $album['headers']['status-code']); $this->assertEquals('album1', $album['body']['$id']); @@ -4744,7 +4756,11 @@ trait DatabasesBase $artist = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $artists['body']['$id'] . '/rows/' . $album['body']['artist']['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['*', 'albums.$id', 'albums.name', 'albums.$permissions'])->toString() + ] + ]); $this->assertEquals(200, $artist['headers']['status-code']); $this->assertEquals('Artist 1', $artist['body']['name']); @@ -4885,7 +4901,11 @@ trait DatabasesBase $sport = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $sports['body']['$id'] . '/rows/sport1', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['*', 'players.name', 'players.$permissions'])->toString() + ] + ]); $this->assertEquals(200, $sport['headers']['status-code']); $this->assertEquals('sport1', $sport['body']['$id']); @@ -4899,7 +4919,11 @@ trait DatabasesBase $player = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/tables/' . $players['body']['$id'] . '/rows/' . $sport['body']['players'][0]['$id'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['*', 'sports.$id', 'sports.name', 'sports.$permissions'])->toString() + ] + ]); $this->assertEquals(200, $player['headers']['status-code']); $this->assertEquals('Player 1', $player['body']['name']); @@ -4927,6 +4951,7 @@ trait DatabasesBase ], $this->getHeaders()), [ 'queries' => [ Query::isNotNull('$id')->toString(), + Query::select(['*', 'libraries.*'])->toString(), Query::startsWith('fullName', 'Stevie')->toString(), Query::endsWith('fullName', 'Wonder')->toString(), Query::between('$createdAt', '1975-12-06', '2050-12-01')->toString(), diff --git a/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php index 710b167b64..99f1b58c01 100644 --- a/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/Tables/DatabasesCustomServerTest.php @@ -3740,7 +3740,11 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ]), [ + 'queries' => [ + Query::select(['new_level_2.*'])->toString() + ] + ]); $this->assertArrayHasKey('new_level_2', $newRow['body']); $this->assertEquals(1, count($newRow['body']['new_level_2'])); @@ -3850,7 +3854,11 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ]), [ + 'queries' => [ + Query::select(['new_level_2.*'])->toString() + ] + ]); $this->assertArrayHasKey('new_level_2', $newRow['body']); $this->assertNotEmpty($newRow['body']['new_level_2']); @@ -3960,7 +3968,11 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ]), [ + 'queries' => [ + Query::select(['new_level_2.*'])->toString() + ] + ]); $this->assertArrayHasKey('new_level_2', $newRow['body']); $this->assertNotEmpty($newRow['body']['new_level_2']); @@ -3971,7 +3983,11 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ]), [ + 'queries' => [ + Query::select(['*', 'level1.*'])->toString() + ] + ]); $this->assertArrayHasKey('level1', $level2Row['body']); $this->assertNotEmpty($level2Row['body']['level1']); @@ -4070,7 +4086,11 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ]), [ + 'queries' => [ + Query::select(['new_level_2.*'])->toString() + ] + ]); $this->assertArrayHasKey('new_level_2', $newRow['body']); $this->assertNotEmpty($newRow['body']['new_level_2']); @@ -4081,7 +4101,11 @@ class DatabasesCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); + ]), [ + 'queries' => [ + Query::select(['*', 'level1.*'])->toString() + ] + ]); $this->assertArrayHasKey('level1', $level2Row['body']); $this->assertNotEmpty($level2Row['body']['level1']); @@ -4426,6 +4450,14 @@ class DatabasesCustomServerTest extends Scope $createBulkRows(); + /** + * Wait for database to purge cache... + * + * This test specifically failed on 1.6.x response format, + * could be due to the slow or overworked machine, but being safe here! + */ + sleep(5); + // TEST: Update all rows $response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', @@ -4444,6 +4476,14 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertCount(10, $response['body']['rows']); + /** + * Wait for database to purge cache... + * + * This test specifically failed on 1.6.x response format, + * could be due to the slow or overworked machine, but being safe here! + */ + sleep(5); + $rows = $this->client->call(Client::METHOD_GET, '/databases/' . $data['databaseId'] . '/tables/' . $data['$id'] . '/rows', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'],