From 08f0a43465e39c727a5b171988e2dbb366702676 Mon Sep 17 00:00:00 2001 From: fogelito Date: Mon, 26 Jan 2026 09:42:37 +0200 Subject: [PATCH] Run tests --- src/Appwrite/Utopia/Request/Filters/V22.php | 81 +++++++++++++------ .../Databases/Legacy/DatabasesBase.php | 2 - .../Databases/TablesDB/DatabasesBase.php | 43 ++++++++++ 3 files changed, 98 insertions(+), 28 deletions(-) diff --git a/src/Appwrite/Utopia/Request/Filters/V22.php b/src/Appwrite/Utopia/Request/Filters/V22.php index 1c20f521bb..20a643ffd5 100644 --- a/src/Appwrite/Utopia/Request/Filters/V22.php +++ b/src/Appwrite/Utopia/Request/Filters/V22.php @@ -9,21 +9,30 @@ use Utopia\Database\Query; class V22 extends Filter { + /** + * @var string[] + */ + private array $internalAttributes = [ + '$id', + '$sequence', + '$permissions', + '$createdAt', + '$updatedAt', + ]; + public function parse(array $content, string $model): array { - if (isset($content['queries'])) { - $content = $this->convertSelectQueries($content); + if (!isset($content['queries'])) { + return $content; } + $content = $this->convertSelectQueries($content); + return $content; } private function convertSelectQueries(array $content): array { - if (!isset($content['queries'])) { - return $content; - } - try { $parsed = Query::parseQueries($content['queries']); } catch (QueryException $e) { @@ -31,35 +40,26 @@ class V22 extends Filter } $queries = []; - $internals = false; - $values = []; + $selects = []; + // Collect all selects foreach ($parsed as $query) { - try { - if ($query->getMethod() === 'select') { - foreach ($query->getValues() as $select) { - $queries[] = Query::select($select); - $values[] = $select; - } - } else { - $queries[] = $query; + if ($query->getMethod() === 'select') { + foreach ($query->getValues() as $val) { + $selects[] = $val; } - - } catch (\Throwable $th) { - throw new Exception(Exception::GENERAL_QUERY_INVALID, $th->getMessage()); + } else { + $queries[] = $query; } } - if (count($values)) { - $queries[] = Query::select('$sequence'); - $queries[] = Query::select('$id'); - $queries[] = Query::select('$updatedAt'); - $queries[] = Query::select('$createdAt'); - $queries[] = Query::select('$permissions'); + // Expand selects with internal attributes once per object level + if (!empty($selects)) { + $queries = array_merge($queries, $this->expandSelects($selects)); } + // Convert all queries to string $resolvedQueries = []; - foreach ($queries as $query) { $resolvedQueries[] = $query->toString(); } @@ -68,4 +68,33 @@ class V22 extends Filter return $content; } + + private function expandSelects(array $selects): array + { + $expanded = []; + $addedInternalForLevel = []; + + foreach ($selects as $select) { + $expanded[] = Query::select($select); + + // Skip internal attribute expansion if wildcard * + if ($select === '*') { + continue; + } + + // Determine prefix for this level + $parts = explode('.', $select); + $prefix = implode('.', array_slice($parts, 0, -1)); // empty string for top level + + if (!isset($addedInternalForLevel[$prefix])) { + // Add internal attributes once per object level + foreach ($this->internalAttributes as $attr) { + $expanded[] = Query::select($prefix ? "$prefix.$attr" : $attr); + } + $addedInternalForLevel[$prefix] = true; + } + } + + return $expanded; + } } diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php index 5d1e822653..5651e4c276 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesBase.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesBase.php @@ -2463,8 +2463,6 @@ trait DatabasesBase ], ]); - var_dump($response); - $this->assertEquals(200, $response['headers']['status-code']); $this->assertArrayHasKey('title', $response['body']['documents'][0]); $this->assertArrayNotHasKey('birthDay', $response['body']['documents'][0]); diff --git a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php index 1d914b6220..15bd319007 100644 --- a/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php +++ b/tests/e2e/Services/Databases/TablesDB/DatabasesBase.php @@ -2348,13 +2348,56 @@ trait DatabasesBase Query::select('title')->toString(), Query::select('releaseYear')->toString(), Query::select('$id')->toString(), + Query::select('$sequence')->toString(), ], ]); $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($row['title'], $response['body']['title']); $this->assertEquals($row['releaseYear'], $response['body']['releaseYear']); + $this->assertEquals($row['$sequence'], $response['body']['$sequence']); $this->assertArrayNotHasKey('birthDay', $response['body']); + $this->assertArrayHasKey('$sequence', $response['body']); + + $sequence = (string)$response['body']['$sequence']; + + // Query by sequence + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $row['$tableId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::equal('$sequence', [$sequence])->toString(), + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(1, $response['body']['total']); + $this->assertEquals($row['title'], $response['body']['rows'][0]['title']); + $this->assertEquals($row['releaseYear'], $response['body']['rows'][0]['releaseYear']); + $this->assertEquals($row['$sequence'], $response['body']['rows'][0]['$sequence']); + + /** + * Use specific X-Appwrite-Response-Format 1.8.0 + */ + $response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $row['$tableId'] . '/rows', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'X-Appwrite-Response-Format' => '1.8.0', + ], $this->getHeaders()), [ + 'queries' => [ + '{"method":"select","values":["title"]}', + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertArrayHasKey('title', $response['body']['rows'][0]); + $this->assertArrayNotHasKey('birthDay', $response['body']['rows'][0]); + $this->assertArrayHasKey('$sequence', $response['body']['rows'][0]); + $this->assertArrayHasKey('$id', $response['body']['rows'][0]); + $this->assertArrayHasKey('$createdAt', $response['body']['rows'][0]); + $this->assertArrayHasKey('$updatedAt', $response['body']['rows'][0]); + $this->assertArrayHasKey('$permissions', $response['body']['rows'][0]); } /**