From deb8b3a789d5d9d129fd3dc0059bcc0884e73568 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 17 Jun 2025 11:11:47 +0530 Subject: [PATCH 01/11] revert: stuff that is not supposed to be in this branch. --- .coderabbit.yaml | 7 ++++- .github/workflows/tests.yml | 6 ++-- app/config/specs/open-api3-latest-client.json | 23 ++++++++++++++ .../specs/open-api3-latest-console.json | 11 +++++++ app/config/specs/swagger2-latest-client.json | 23 ++++++++++++++ app/config/specs/swagger2-latest-console.json | 9 ++++++ app/controllers/api/account.php | 6 ++-- app/controllers/api/vcs.php | 5 ++-- app/controllers/general.php | 21 +++++++------ app/views/install/compose.phtml | 2 +- docker-compose.yml | 4 +-- .../Functions/Http/Executions/Create.php | 6 ++-- src/Appwrite/Platform/Workers/Deletes.php | 2 +- src/Appwrite/Platform/Workers/Functions.php | 6 ++-- .../Services/Sites/SitesCustomServerTest.php | 30 +++++++++---------- 15 files changed, 118 insertions(+), 43 deletions(-) diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 822c470cd8..42b705b571 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -9,4 +9,9 @@ reviews: - main - 1.6.x - 1.7.x - - 1.8.x \ No newline at end of file + - 1.8.x + high_level_summary: false + poem: false + sequence_diagrams: false + collapse_walkthrough: true + changed_files_summary: false diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ff548f3447..97f3696e67 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -122,7 +122,7 @@ jobs: docker load --input /tmp/${{ env.IMAGE }}.tar docker compose up -d sleep 10 - + - name: Wait for Open Runtimes timeout-minutes: 3 run: | @@ -273,7 +273,7 @@ jobs: export _APP_DATABASE_SHARED_TABLES=database_db_main export _APP_DATABASE_SHARED_TABLES_V1= fi - + docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ @@ -359,4 +359,4 @@ jobs: docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ - appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys \ No newline at end of file + appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 8f03738786..73a027f4bb 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -4492,6 +4492,29 @@ } ], "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." + }, + { + "name": "createDocuments", + "auth": { + "Key": [] + }, + "parameters": [ + "databaseId", + "collectionId", + "documents" + ], + "required": [ + "databaseId", + "collectionId", + "documents" + ], + "responses": [ + { + "code": 201, + "model": "#\/components\/schemas\/documentList" + } + ], + "description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." } ], "auth": { diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 85ef1334d4..92ba5c6829 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -35112,6 +35112,17 @@ "default": "" }, "in": "query" + }, + { + "name": "providerReference", + "description": "Git reference (branch, tag, commit) to get contents from", + "required": false, + "schema": { + "type": "string", + "x-example": "", + "default": "" + }, + "in": "query" } ] } diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 33fe1a93c9..e369e2f0f3 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -4638,6 +4638,29 @@ } ], "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." + }, + { + "name": "createDocuments", + "auth": { + "Key": [] + }, + "parameters": [ + "databaseId", + "collectionId", + "documents" + ], + "required": [ + "databaseId", + "collectionId", + "documents" + ], + "responses": [ + { + "code": 201, + "model": "#\/definitions\/documentList" + } + ], + "description": "Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console." } ], "auth": { diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index a11fd21b42..da959a8180 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -35319,6 +35319,15 @@ "x-example": "", "default": "", "in": "query" + }, + { + "name": "providerReference", + "description": "Git reference (branch, tag, commit) to get contents from", + "required": false, + "type": "string", + "x-example": "", + "default": "", + "in": "query" } ] } diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 0c53423325..2d0d0eac40 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -3880,7 +3880,7 @@ App::patch('/v1/account/mfa') if ($user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false)) { $factors[] = Type::PHONE; } - $factors = \array_unique($factors); + $factors = \array_values(\array_unique($factors)); $session->setAttribute('factors', $factors); $dbForProject->updateDocument('sessions', $session->getId(), $session); @@ -4065,7 +4065,7 @@ App::put('/v1/account/mfa/authenticators/:type') $factors = $session->getAttribute('factors', []); $factors[] = $type; - $factors = \array_unique($factors); + $factors = \array_values(\array_unique($factors)); $session->setAttribute('factors', $factors); $dbForProject->updateDocument('sessions', $session->getId(), $session); @@ -4549,7 +4549,7 @@ App::put('/v1/account/mfa/challenge') $factors = $session->getAttribute('factors', []); $factors[] = $type; - $factors = \array_unique($factors); + $factors = \array_values(\array_unique($factors)); $session ->setAttribute('factors', $factors) diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 31b2539b99..69d4c490d9 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -605,11 +605,12 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro ->param('installationId', '', new Text(256), 'Installation Id') ->param('providerRepositoryId', '', new Text(256), 'Repository Id') ->param('providerRootDirectory', '', new Text(256, 0), 'Path to get contents of nested directory', true) + ->param('providerReference', '', new Text(256, 0), 'Git reference (branch, tag, commit) to get contents from', true) ->inject('gitHub') ->inject('response') ->inject('project') ->inject('dbForPlatform') - ->action(function (string $installationId, string $providerRepositoryId, string $providerRootDirectory, GitHub $github, Response $response, Document $project, Database $dbForPlatform) { + ->action(function (string $installationId, string $providerRepositoryId, string $providerRootDirectory, string $providerReference, GitHub $github, Response $response, Document $project, Database $dbForPlatform) { $installation = $dbForPlatform->getDocument('installations', $installationId); if ($installation->isEmpty()) { @@ -631,7 +632,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); } - $contents = $github->listRepositoryContents($owner, $repositoryName, $providerRootDirectory); + $contents = $github->listRepositoryContents($owner, $repositoryName, $providerRootDirectory, $providerReference); $vcsContents = []; foreach ($contents as $content) { diff --git a/app/controllers/general.php b/app/controllers/general.php index 91e414d077..c979f8f1b5 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -513,28 +513,27 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw 'function' => $deployment->getAttribute('entrypoint', ''), 'site' => '', }; + $source = $deployment->getAttribute('buildPath', ''); + $extension = str_ends_with($source, '.tar') ? 'tar' : 'tar.gz'; - if ($type === 'function') { - $runtimeEntrypoint = match ($version) { - 'v2' => '', - default => 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $runtime['startCommand'] . '"' - }; - } elseif ($type === 'site') { + $startCommand = $runtime['startCommand']; + if ($type === 'site') { $frameworks = Config::getParam('frameworks', []); $framework = $frameworks[$resource->getAttribute('framework', '')] ?? null; - $startCommand = $runtime['startCommand']; - if (!is_null($framework)) { $adapter = ($framework['adapters'] ?? [])[$deployment->getAttribute('adapter', '')] ?? null; if (!is_null($adapter) && isset($adapter['startCommand'])) { $startCommand = $adapter['startCommand']; } } - - $runtimeEntrypoint = 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $startCommand . '"'; } + $runtimeEntrypoint = match ($version) { + 'v2' => '', + default => "cp /tmp/code.$extension /mnt/code/code.$extension && nohup helpers/start.sh \"$startCommand\"", + }; + $entrypoint = match ($type) { 'function' => $deployment->getAttribute('entrypoint', ''), 'site' => '', @@ -547,7 +546,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw variables: $vars, timeout: $resource->getAttribute('timeout', 30), image: $runtime['image'], - source: $deployment->getAttribute('buildPath', ''), + source: $source, entrypoint: $entrypoint, version: $version, path: $path, diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 2d8f2b35ab..e3699662a4 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -841,7 +841,7 @@ $image = $this->getParam('image', ''); - _APP_DB_PASS appwrite-assistant: - image: appwrite/assistant:0.4.0 + image: appwrite/assistant:0.8.3 container_name: appwrite-assistant <<: *x-logging restart: unless-stopped diff --git a/docker-compose.yml b/docker-compose.yml index 29a43aca91..0b653af8c2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -213,7 +213,7 @@ services: appwrite-console: <<: *x-logging container_name: appwrite-console - image: appwrite/console:6.0.32 + image: appwrite/console:6.0.41 restart: unless-stopped networks: - appwrite @@ -934,7 +934,7 @@ services: appwrite-assistant: container_name: appwrite-assistant - image: appwrite/assistant:0.7.0 + image: appwrite/assistant:0.8.3 networks: - appwrite environment: diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php index 0aec31e5fd..bbdbf8ab45 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php @@ -386,7 +386,9 @@ class Create extends Base try { $version = $function->getAttribute('version', 'v2'); $command = $runtime['startCommand']; - $command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"'; + $source = $deployment->getAttribute('buildPath', ''); + $extension = str_ends_with($source, '.tar') ? 'tar' : 'tar.gz'; + $command = $version === 'v2' ? '' : "cp /tmp/code.$extension /mnt/code/code.$extension && nohup helpers/start.sh \"$command\""; $executionResponse = $executor->createExecution( projectId: $project->getId(), deploymentId: $deployment->getId(), @@ -394,7 +396,7 @@ class Create extends Base variables: $vars, timeout: $function->getAttribute('timeout', 0), image: $runtime['image'], - source: $deployment->getAttribute('buildPath', ''), + source: $source, entrypoint: $deployment->getAttribute('entrypoint', ''), version: $version, path: $path, diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 8ed96c76a2..2f41001b58 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -495,7 +495,7 @@ class Deletes extends Action * @throws Authorization * @throws DatabaseException */ - private function deleteProject(Database $dbForPlatform, callable $getProjectDB, Device $deviceForFiles, Device $deviceForSites, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, CertificatesAdapter $certificates, Document $document): void + protected function deleteProject(Database $dbForPlatform, callable $getProjectDB, Device $deviceForFiles, Device $deviceForSites, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, CertificatesAdapter $certificates, Document $document): void { $projectInternalId = $document->getSequence(); $projectId = $document->getId(); diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index a9126583ce..8b3347775a 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -519,7 +519,9 @@ class Functions extends Action try { $version = $function->getAttribute('version', 'v2'); $command = $runtime['startCommand']; - $command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"'; + $source = $deployment->getAttribute('buildPath', ''); + $extension = str_ends_with($source, '.tar') ? 'tar' : 'tar.gz'; + $command = $version === 'v2' ? '' : "cp /tmp/code.$extension /mnt/code/code.$extension && nohup helpers/start.sh \"$command\""; $executionResponse = $executor->createExecution( projectId: $project->getId(), deploymentId: $deploymentId, @@ -527,7 +529,7 @@ class Functions extends Action variables: $vars, timeout: $function->getAttribute('timeout', 0), image: $runtime['image'], - source: $deployment->getAttribute('buildPath', ''), + source: $source, entrypoint: $deployment->getAttribute('entrypoint', ''), version: $version, path: $path, diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index 8459e46c6f..b3ea045430 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -477,7 +477,7 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($domain); $deploymentId = $this->setupDeployment($siteId, [ - 'code' => $this->packageSite('static'), + 'code' => $this->packageSite('static-single-file'), 'activate' => 'true' ]); $this->assertNotEmpty($deploymentId); @@ -856,7 +856,7 @@ class SitesCustomServerTest extends Scope $deployment = $this->createDeployment($siteId, [ 'siteId' => $siteId, - 'code' => $this->packageSite('static'), + 'code' => $this->packageSite('static-single-file'), 'activate' => true, ]); @@ -874,7 +874,7 @@ class SitesCustomServerTest extends Scope }, 50000, 500); $deployment = $this->createDeployment($siteId, [ - 'code' => $this->packageSite('static'), + 'code' => $this->packageSite('static-single-file'), 'activate' => 'false' ]); @@ -917,7 +917,7 @@ class SitesCustomServerTest extends Scope $this->assertNotNull($siteId); $deployment = $this->createDeployment($siteId, [ - 'code' => $this->packageSite('static'), + 'code' => $this->packageSite('static-single-file'), 'activate' => 'false' ]); @@ -969,7 +969,7 @@ class SitesCustomServerTest extends Scope $this->assertNotNull($siteId); $deployment = $this->createDeployment($siteId, [ - 'code' => $this->packageSite('static'), + 'code' => $this->packageSite('static-single-file'), 'activate' => 'false' ]); @@ -1014,7 +1014,7 @@ class SitesCustomServerTest extends Scope $this->assertNotNull($siteId); $deployment = $this->createDeployment($siteId, [ - 'code' => $this->packageSite('static'), + 'code' => $this->packageSite('static-single-file'), 'activate' => 'false' ]); @@ -1022,7 +1022,7 @@ class SitesCustomServerTest extends Scope $this->assertEquals(202, $deployment['headers']['status-code']); $deployment = $this->createDeployment($siteId, [ - 'code' => $this->packageSite('static'), + 'code' => $this->packageSite('static-single-file'), 'activate' => 'false' ]); @@ -1193,7 +1193,7 @@ class SitesCustomServerTest extends Scope $this->assertNotNull($siteId); $deployment = $this->createDeployment($siteId, [ - 'code' => $this->packageSite('static'), + 'code' => $this->packageSite('static-single-file'), 'activate' => 'false' ]); @@ -1318,7 +1318,7 @@ class SitesCustomServerTest extends Scope $this->assertNotNull($siteId); $deployment = $this->createDeployment($siteId, [ - 'code' => $this->packageSite('static'), + 'code' => $this->packageSite('static-single-file'), 'activate' => 'false' ]); @@ -1818,7 +1818,7 @@ class SitesCustomServerTest extends Scope ]); $deploymentId = $this->setupDeployment($siteId, [ - 'code' => $this->packageSite('static'), + 'code' => $this->packageSite('static-single-file'), 'activate' => true ]); @@ -2435,7 +2435,7 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); $deploymentId = $this->setupDeployment($siteId, [ - 'code' => $this->packageSite('static'), + 'code' => $this->packageSite('static-single-file'), 'activate' => 'true' ]); @@ -2477,7 +2477,7 @@ class SitesCustomServerTest extends Scope // test canceled deployment error page $deployment = $this->createDeployment($siteId, [ - 'code' => $this->packageSite('static'), + 'code' => $this->packageSite('static-single-file'), 'activate' => 'true' ]); $deploymentId = $deployment['body']['$id'] ?? ''; @@ -2517,7 +2517,7 @@ class SitesCustomServerTest extends Scope $this->assertStringContainsString('View deployments', $response['body']); $deployment = $this->createDeployment($siteId, [ - 'code' => $this->packageSite('astro'), + 'code' => $this->packageSite('static-single-file'), 'activate' => 'true' ]); @@ -2606,7 +2606,7 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); $deployment = $this->createDeployment($siteId, [ - 'code' => $this->packageSite('static'), + 'code' => $this->packageSite('static-single-file'), 'activate' => true ]); $this->assertEquals(202, $deployment['headers']['status-code']); @@ -2635,7 +2635,7 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); $deployment = $this->createDeployment($siteId, [ - 'code' => $this->packageSite('static'), + 'code' => $this->packageSite('static-single-file'), 'activate' => true ]); $this->assertEquals(202, $deployment['headers']['status-code']); From 57d8185b166c5e05bcf4ab632ba276886c266a0e Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 17 Jun 2025 11:26:22 +0530 Subject: [PATCH 02/11] test: backwards compat for request/response filters. --- .github/workflows/tests.yml | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 97f3696e67..b3f17100b8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -146,6 +146,7 @@ jobs: Avatars, Console, Databases, + Databases 1.7.4, Functions, FunctionsSchedule, GraphQL, @@ -191,14 +192,21 @@ jobs: - name: Run ${{ matrix.service }} tests with Project table mode run: | - echo "Using project tables" + if [[ "${{ matrix.service }}" == "Databases 1.7.4" ]]; then + echo "Using Database 1.7 format" + export _APP_SYSTEM_RESPONSE_FORMAT=1.7.4 + service="Databases" + else + service="${{ matrix.service }}" + fi + export _APP_DATABASE_SHARED_TABLES= export _APP_DATABASE_SHARED_TABLES_V1= docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ - appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys + appwrite test /usr/src/code/tests/e2e/Services/$service --debug --exclude-group devKeys e2e_shared_mode_test: name: E2E Shared Mode Service Test @@ -214,6 +222,7 @@ jobs: Avatars, Console, Databases, + Databases 1.7.4, Functions, FunctionsSchedule, GraphQL, @@ -273,11 +282,20 @@ jobs: export _APP_DATABASE_SHARED_TABLES=database_db_main export _APP_DATABASE_SHARED_TABLES_V1= fi + + if [[ "${{ matrix.service }}" == "Databases 1.7.4" ]]; then + echo "Using Database 1.6 format" + export _APP_SYSTEM_RESPONSE_FORMAT=1.7.4 + service="Databases" + else + export _APP_SYSTEM_RESPONSE_FORMAT= + service="${{ matrix.service }}" + fi docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ - appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys + appwrite test /usr/src/code/tests/e2e/Services/$service --debug --exclude-group devKeys e2e_dev_keys: name: E2E Service Test (Dev Keys) From bfb383e28ae808d695bfbb204545d70d2a446c45 Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 17 Jun 2025 11:47:40 +0530 Subject: [PATCH 03/11] test: backwards compat for request/response filters on latest `1.6.x`. --- .github/workflows/tests.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b3f17100b8..1c7292ea91 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -146,7 +146,7 @@ jobs: Avatars, Console, Databases, - Databases 1.7.4, + Databases 1.6.2, Functions, FunctionsSchedule, GraphQL, @@ -192,9 +192,9 @@ jobs: - name: Run ${{ matrix.service }} tests with Project table mode run: | - if [[ "${{ matrix.service }}" == "Databases 1.7.4" ]]; then - echo "Using Database 1.7 format" - export _APP_SYSTEM_RESPONSE_FORMAT=1.7.4 + if [[ "${{ matrix.service }}" == "Databases 1.6.2" ]]; then + echo "Using Database 1.6.2 format" + export _APP_SYSTEM_RESPONSE_FORMAT=1.6.2 service="Databases" else service="${{ matrix.service }}" @@ -222,7 +222,7 @@ jobs: Avatars, Console, Databases, - Databases 1.7.4, + Databases 1.6.2, Functions, FunctionsSchedule, GraphQL, @@ -283,9 +283,9 @@ jobs: export _APP_DATABASE_SHARED_TABLES_V1= fi - if [[ "${{ matrix.service }}" == "Databases 1.7.4" ]]; then - echo "Using Database 1.6 format" - export _APP_SYSTEM_RESPONSE_FORMAT=1.7.4 + if [[ "${{ matrix.service }}" == "Databases 1.6.2" ]]; then + echo "Using Database 1.6.2 format" + export _APP_SYSTEM_RESPONSE_FORMAT=1.6.2 service="Databases" else export _APP_SYSTEM_RESPONSE_FORMAT= From 1e39d8d6ee0474f74e16db0795c77fbcc7e2a65b Mon Sep 17 00:00:00 2001 From: Darshan Date: Tue, 17 Jun 2025 12:01:50 +0530 Subject: [PATCH 04/11] revert: sanity check tests! --- .github/workflows/tests.yml | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1c7292ea91..97f3696e67 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -146,7 +146,6 @@ jobs: Avatars, Console, Databases, - Databases 1.6.2, Functions, FunctionsSchedule, GraphQL, @@ -192,21 +191,14 @@ jobs: - name: Run ${{ matrix.service }} tests with Project table mode run: | - if [[ "${{ matrix.service }}" == "Databases 1.6.2" ]]; then - echo "Using Database 1.6.2 format" - export _APP_SYSTEM_RESPONSE_FORMAT=1.6.2 - service="Databases" - else - service="${{ matrix.service }}" - fi - + echo "Using project tables" export _APP_DATABASE_SHARED_TABLES= export _APP_DATABASE_SHARED_TABLES_V1= docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ - appwrite test /usr/src/code/tests/e2e/Services/$service --debug --exclude-group devKeys + appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys e2e_shared_mode_test: name: E2E Shared Mode Service Test @@ -222,7 +214,6 @@ jobs: Avatars, Console, Databases, - Databases 1.6.2, Functions, FunctionsSchedule, GraphQL, @@ -282,20 +273,11 @@ jobs: export _APP_DATABASE_SHARED_TABLES=database_db_main export _APP_DATABASE_SHARED_TABLES_V1= fi - - if [[ "${{ matrix.service }}" == "Databases 1.6.2" ]]; then - echo "Using Database 1.6.2 format" - export _APP_SYSTEM_RESPONSE_FORMAT=1.6.2 - service="Databases" - else - export _APP_SYSTEM_RESPONSE_FORMAT= - service="${{ matrix.service }}" - fi docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ - appwrite test /usr/src/code/tests/e2e/Services/$service --debug --exclude-group devKeys + appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys e2e_dev_keys: name: E2E Service Test (Dev Keys) From 6214c2d104aea5653dccc68fd2868476ca961c77 Mon Sep 17 00:00:00 2001 From: Darshan Date: Wed, 18 Jun 2025 12:07:00 +0530 Subject: [PATCH 05/11] update: disable `1.8.x` request/response filters for `lazy-loading-relationships`. --- app/controllers/general.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index c979f8f1b5..d4b078c0d1 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -844,10 +844,10 @@ App::init() if (version_compare($requestFormat, '1.7.0', '<')) { $request->addFilter(new RequestV19()); } - if (version_compare($requestFormat, '1.8.0', '<')) { + /*if (version_compare($requestFormat, '1.8.0', '<')) { $dbForProject = $getProjectDB($project); $request->addFilter(new RequestV20($dbForProject, $route)); - } + }*/ } $domain = $request->getHostname(); @@ -1017,9 +1017,9 @@ App::init() if (version_compare($responseFormat, '1.7.0', '<')) { $response->addFilter(new ResponseV19()); } - if (version_compare($responseFormat, '1.8.0', '<')) { + /*if (version_compare($responseFormat, '1.8.0', '<')) { $response->addFilter(new ResponseV20()); - } + }*/ if (version_compare($responseFormat, APP_VERSION_STABLE, '>')) { $response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is " . APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks"); } From d338007d688d582c7248967adfff3060a4f7685f Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 23 Jun 2025 10:44:25 +0530 Subject: [PATCH 06/11] update: re-add filters since the PR is in draft and will be merged in the end. --- app/controllers/general.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 4a4522afae..5f094b1549 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -845,10 +845,10 @@ App::init() if (version_compare($requestFormat, '1.7.0', '<')) { $request->addFilter(new RequestV19()); } - /*if (version_compare($requestFormat, '1.8.0', '<')) { + if (version_compare($requestFormat, '1.8.0', '<')) { $dbForProject = $getProjectDB($project); $request->addFilter(new RequestV20($dbForProject, $route)); - }*/ + } } $domain = $request->getHostname(); @@ -1018,9 +1018,9 @@ App::init() if (version_compare($responseFormat, '1.7.0', '<')) { $response->addFilter(new ResponseV19()); } - /*if (version_compare($responseFormat, '1.8.0', '<')) { + if (version_compare($responseFormat, '1.8.0', '<')) { $response->addFilter(new ResponseV20()); - }*/ + } if (version_compare($responseFormat, APP_VERSION_STABLE, '>')) { $response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is " . APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks"); } From 7969e5713430fbcf75f455b8d58eb12758814bd8 Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 23 Jun 2025 12:11:00 +0530 Subject: [PATCH 07/11] ci: empty commit From 597230006fa8218c61b3a3da64b4157b63a82779 Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 23 Jun 2025 12:23:26 +0530 Subject: [PATCH 08/11] fix: oha as per latest updates. --- .github/workflows/benchmark.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 6d73787d00..9ba52dff23 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -65,7 +65,7 @@ jobs: sudo apt update sudo apt install oha - name: Benchmark PR - run: 'oha -z 180s http://localhost/v1/health/version -j > benchmark.json' + run: 'oha -z 180s http://localhost/v1/health/version -o json > benchmark.json' - name: Cleaning run: docker compose down -v - name: Installing latest version @@ -78,7 +78,7 @@ jobs: docker compose up -d sleep 10 - name: Benchmark Latest - run: oha -z 180s http://localhost/v1/health/version -j > benchmark-latest.json + run: oha -z 180s http://localhost/v1/health/version -o json > benchmark-latest.json - name: Prepare comment run: | echo '## :sparkles: Benchmark results' > benchmark.txt From ffccdef68c420bf5b09e78cfd27ec9f69950d59c Mon Sep 17 00:00:00 2001 From: Darshan Date: Mon, 23 Jun 2025 12:58:46 +0530 Subject: [PATCH 09/11] fix: oha as per latest updates. --- .github/workflows/benchmark.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 9ba52dff23..262c79ab3d 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -65,7 +65,7 @@ jobs: sudo apt update sudo apt install oha - name: Benchmark PR - run: 'oha -z 180s http://localhost/v1/health/version -o json > benchmark.json' + run: 'oha -z 180s http://localhost/v1/health/version --output-format json --output benchmark.json' - name: Cleaning run: docker compose down -v - name: Installing latest version @@ -78,7 +78,7 @@ jobs: docker compose up -d sleep 10 - name: Benchmark Latest - run: oha -z 180s http://localhost/v1/health/version -o json > benchmark-latest.json + run: 'oha -z 180s http://localhost/v1/health/version --output-format json --output benchmark-latest.json' - name: Prepare comment run: | echo '## :sparkles: Benchmark results' > benchmark.txt From 5b8751d270b83219e9b25518ab9f0d31958ea51f Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 17 Jul 2025 18:23:33 +0530 Subject: [PATCH 10/11] add: changes to module structure. --- .../Databases/Collections/Documents/Get.php | 10 +++- .../Databases/Collections/Documents/XList.php | 59 ++++++++++++++----- 2 files changed, 53 insertions(+), 16 deletions(-) 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'); + } } } } From 944f5ed7c1ca97b5cababf0a94ea55bfe844e612 Mon Sep 17 00:00:00 2001 From: Darshan Date: Thu, 17 Jul 2025 18:58:56 +0530 Subject: [PATCH 11/11] update: mirror tests to Tables api. --- .../Databases/Tables/DatabasesBase.php | 37 ++++++++++--- .../Tables/DatabasesCustomServerTest.php | 52 ++++++++++++++++--- 2 files changed, 77 insertions(+), 12 deletions(-) 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'],