From 7648978cfa78e15c62fcb79a1b30b39bbb95d98d Mon Sep 17 00:00:00 2001 From: adityaoberai Date: Wed, 29 Apr 2026 19:22:46 +0530 Subject: [PATCH 01/26] Add codex plugin to SDKs --- app/config/sdks.php | 20 ++++++++++++++++++++ src/Appwrite/Platform/Tasks/SDKs.php | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/app/config/sdks.php b/app/config/sdks.php index e89265b05e..cbfe76dce4 100644 --- a/app/config/sdks.php +++ b/app/config/sdks.php @@ -320,6 +320,26 @@ return [ 'repoBranch' => 'main', 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/claude-plugin/CHANGELOG.md'), ], + [ + 'key' => 'codex-plugin', + 'name' => 'CodexPlugin', + 'version' => '0.1.0', + 'url' => 'https://github.com/appwrite/codex-plugin.git', + 'enabled' => true, + 'beta' => false, + 'dev' => false, + 'hidden' => false, + 'spec' => 'static', + 'family' => APP_SDK_PLATFORM_STATIC, + 'prism' => 'codex-plugin', + 'source' => \realpath(__DIR__ . '/../sdks/static-codex-plugin'), + 'gitUrl' => 'git@github.com:appwrite/codex-plugin.git', + 'gitRepoName' => 'codex-plugin', + 'gitUserName' => 'appwrite', + 'gitBranch' => 'dev', + 'repoBranch' => 'main', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/codex-plugin/CHANGELOG.md'), + ], ], ], diff --git a/src/Appwrite/Platform/Tasks/SDKs.php b/src/Appwrite/Platform/Tasks/SDKs.php index b1580f0e68..fbf965bd00 100644 --- a/src/Appwrite/Platform/Tasks/SDKs.php +++ b/src/Appwrite/Platform/Tasks/SDKs.php @@ -7,6 +7,7 @@ use Appwrite\SDK\Language\Android; use Appwrite\SDK\Language\Apple; use Appwrite\SDK\Language\ClaudePlugin; use Appwrite\SDK\Language\CLI; +use Appwrite\SDK\Language\CodexPlugin; use Appwrite\SDK\Language\CursorPlugin; use Appwrite\SDK\Language\Dart; use Appwrite\SDK\Language\Deno; @@ -455,6 +456,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND case 'claude-plugin': $config = new ClaudePlugin(); break; + case 'codex-plugin': + $config = new CodexPlugin(); + break; default: throw new \Exception('Language "' . $language['key'] . '" not supported'); } From 74b84ba38c82d37f9aa144663e5c33c9cdc92742 Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Fri, 8 May 2026 09:52:47 +0100 Subject: [PATCH 02/26] feat: bump utopia-php/abuse and utopia-php/migration to feat/sdk-23 branches --- composer.json | 4 +-- composer.lock | 90 +++++++++++++++++++++++++-------------------------- 2 files changed, 46 insertions(+), 48 deletions(-) diff --git a/composer.json b/composer.json index 9a84be6111..c71bf9fd61 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,7 @@ "ext-sockets": "*", "appwrite/php-runtimes": "0.20.*", "appwrite/php-clamav": "2.0.*", - "utopia-php/abuse": "1.2.*", + "utopia-php/abuse": "dev-feat/sdk-23 as 1.2.x-dev", "utopia-php/agents": "1.2.*", "utopia-php/analytics": "0.15.*", "utopia-php/audit": "2.2.*", @@ -74,7 +74,7 @@ "utopia-php/locale": "0.8.*", "utopia-php/logger": "0.6.*", "utopia-php/messaging": "0.22.*", - "utopia-php/migration": "1.*", + "utopia-php/migration": "dev-feat/sdk-23 as 1.10.x-dev", "utopia-php/platform": "0.13.*", "utopia-php/pools": "1.*", "utopia-php/span": "1.1.*", diff --git a/composer.lock b/composer.lock index d356362788..5b2c9b90a6 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": "ec2ad489c60f0102f0dfab223b6d1fe4", + "content-hash": "42cab472fe9b0df81ec7832c8f943577", "packages": [ { "name": "adhocore/jwt", @@ -69,25 +69,25 @@ }, { "name": "appwrite/appwrite", - "version": "19.1.0", + "version": "23.0.0", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-for-php.git", - "reference": "8738e812062f899c85b2598eef43d6a247f08a56" + "reference": "d22b36167931294bec4259f0803aba14537030ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/8738e812062f899c85b2598eef43d6a247f08a56", - "reference": "8738e812062f899c85b2598eef43d6a247f08a56", + "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/d22b36167931294bec4259f0803aba14537030ec", + "reference": "d22b36167931294bec4259f0803aba14537030ec", "shasum": "" }, "require": { "ext-curl": "*", "ext-json": "*", - "php": ">=7.1.0" + "php": ">=8.2.0" }, "require-dev": { - "mockery/mockery": "^1.6.12", + "mockery/mockery": "1.6.12", "phpunit/phpunit": "^10" }, "type": "library", @@ -100,14 +100,14 @@ "license": [ "BSD-3-Clause" ], - "description": "Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API", + "description": "Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API", "support": { "email": "team@appwrite.io", "issues": "https://github.com/appwrite/sdk-for-php/issues", - "source": "https://github.com/appwrite/sdk-for-php/tree/19.1.0", + "source": "https://github.com/appwrite/sdk-for-php/tree/23.0.0", "url": "https://appwrite.io/support" }, - "time": "2025-12-18T08:07:43+00:00" + "time": "2026-04-16T12:38:44+00:00" }, { "name": "appwrite/php-clamav", @@ -3359,20 +3359,20 @@ }, { "name": "utopia-php/abuse", - "version": "1.2.3", + "version": "dev-feat/sdk-23", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "53f4274939353522ba331f55bcff6e6011ffc56c" + "reference": "2a1fb7973d848cff0ec75fa69a93bc4d2fbc7ada" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/53f4274939353522ba331f55bcff6e6011ffc56c", - "reference": "53f4274939353522ba331f55bcff6e6011ffc56c", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/2a1fb7973d848cff0ec75fa69a93bc4d2fbc7ada", + "reference": "2a1fb7973d848cff0ec75fa69a93bc4d2fbc7ada", "shasum": "" }, "require": { - "appwrite/appwrite": "19.*", + "appwrite/appwrite": "23.*", "ext-curl": "*", "ext-pdo": "*", "ext-redis": "*", @@ -3405,9 +3405,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/1.2.3" + "source": "https://github.com/utopia-php/abuse/tree/feat/sdk-23" }, - "time": "2026-04-29T11:19:08+00:00" + "time": "2026-05-08T07:04:08+00:00" }, { "name": "utopia-php/agents", @@ -4539,20 +4539,20 @@ }, { "name": "utopia-php/migration", - "version": "1.10.1", + "version": "dev-feat/sdk-23", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "759d6d61b327313cbeeeb4ea0c3e2459164b4827" + "reference": "0305666acabcd4b384033e4315517e7bf2254552" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/759d6d61b327313cbeeeb4ea0c3e2459164b4827", - "reference": "759d6d61b327313cbeeeb4ea0c3e2459164b4827", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/0305666acabcd4b384033e4315517e7bf2254552", + "reference": "0305666acabcd4b384033e4315517e7bf2254552", "shasum": "" }, "require": { - "appwrite/appwrite": "19.*", + "appwrite/appwrite": "23.*", "ext-curl": "*", "ext-openssl": "*", "halaxa/json-machine": "^1.2", @@ -4574,25 +4574,7 @@ "Utopia\\Migration\\": "src/Migration" } }, - "autoload-dev": { - "psr-4": { - "Utopia\\Tests\\": "tests/Migration" - } - }, - "scripts": { - "test": [ - "./vendor/bin/phpunit" - ], - "lint": [ - "./vendor/bin/pint --test" - ], - "format": [ - "./vendor/bin/pint" - ], - "check": [ - "./vendor/bin/phpstan analyse --level 3 src tests --memory-limit 2G" - ] - }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -4605,10 +4587,10 @@ "utopia" ], "support": { - "source": "https://github.com/utopia-php/migration/tree/1.10.1", - "issues": "https://github.com/utopia-php/migration/issues" + "issues": "https://github.com/utopia-php/migration/issues", + "source": "https://github.com/utopia-php/migration/tree/feat/sdk-23" }, - "time": "2026-05-07T07:23:57+00:00" + "time": "2026-05-08T08:02:48+00:00" }, { "name": "utopia-php/mongo", @@ -8469,9 +8451,25 @@ "time": "2024-11-07T12:36:22+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "utopia-php/abuse", + "version": "dev-feat/sdk-23", + "alias": "1.2.x-dev", + "alias_normalized": "1.2.9999999.9999999-dev" + }, + { + "package": "utopia-php/migration", + "version": "dev-feat/sdk-23", + "alias": "1.10.x-dev", + "alias_normalized": "1.10.9999999.9999999-dev" + } + ], "minimum-stability": "dev", - "stability-flags": {}, + "stability-flags": { + "utopia-php/abuse": 20, + "utopia-php/migration": 20 + }, "prefer-stable": true, "prefer-lowest": false, "platform": { From 5f6b1dda1a408d3e3faf6f69f885b445bbe94f2c Mon Sep 17 00:00:00 2001 From: Aditya Oberai Date: Fri, 8 May 2026 09:35:38 +0000 Subject: [PATCH 03/26] update composer lock --- composer.lock | 46 ++++++++++++++-------------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/composer.lock b/composer.lock index d356362788..7600e305d2 100644 --- a/composer.lock +++ b/composer.lock @@ -4539,16 +4539,16 @@ }, { "name": "utopia-php/migration", - "version": "1.10.1", + "version": "1.10.2", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "759d6d61b327313cbeeeb4ea0c3e2459164b4827" + "reference": "211d01b90ccab9729029151c6c61f543bd755c2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/759d6d61b327313cbeeeb4ea0c3e2459164b4827", - "reference": "759d6d61b327313cbeeeb4ea0c3e2459164b4827", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/211d01b90ccab9729029151c6c61f543bd755c2e", + "reference": "211d01b90ccab9729029151c6c61f543bd755c2e", "shasum": "" }, "require": { @@ -4574,25 +4574,7 @@ "Utopia\\Migration\\": "src/Migration" } }, - "autoload-dev": { - "psr-4": { - "Utopia\\Tests\\": "tests/Migration" - } - }, - "scripts": { - "test": [ - "./vendor/bin/phpunit" - ], - "lint": [ - "./vendor/bin/pint --test" - ], - "format": [ - "./vendor/bin/pint" - ], - "check": [ - "./vendor/bin/phpstan analyse --level 3 src tests --memory-limit 2G" - ] - }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -4605,10 +4587,10 @@ "utopia" ], "support": { - "source": "https://github.com/utopia-php/migration/tree/1.10.1", - "issues": "https://github.com/utopia-php/migration/issues" + "issues": "https://github.com/utopia-php/migration/issues", + "source": "https://github.com/utopia-php/migration/tree/1.10.2" }, - "time": "2026-05-07T07:23:57+00:00" + "time": "2026-05-08T06:25:47+00:00" }, { "name": "utopia-php/mongo", @@ -5492,16 +5474,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.27.5", + "version": "1.28.0", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "9faa38b48d422f3da764a719712905c83b3922cb" + "reference": "e363fffd220172c5f1a5032038fa3fdafeeb2dfb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9faa38b48d422f3da764a719712905c83b3922cb", - "reference": "9faa38b48d422f3da764a719712905c83b3922cb", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/e363fffd220172c5f1a5032038fa3fdafeeb2dfb", + "reference": "e363fffd220172c5f1a5032038fa3fdafeeb2dfb", "shasum": "" }, "require": { @@ -5537,9 +5519,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.27.5" + "source": "https://github.com/appwrite/sdk-generator/tree/1.28.0" }, - "time": "2026-05-05T12:09:40+00:00" + "time": "2026-05-08T03:37:44+00:00" }, { "name": "brianium/paratest", From 4563706b764b4313e6acc5a1b3532272a6c094fa Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Fri, 8 May 2026 12:38:20 +0100 Subject: [PATCH 04/26] chore: bump utopia-php/migration to a5195ba (SDK 23 typed models refactor) --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 5b2c9b90a6..2c782e8524 100644 --- a/composer.lock +++ b/composer.lock @@ -4543,12 +4543,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "0305666acabcd4b384033e4315517e7bf2254552" + "reference": "a5195bad3e7f56c40d45fdf03166e6e59a170c02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/0305666acabcd4b384033e4315517e7bf2254552", - "reference": "0305666acabcd4b384033e4315517e7bf2254552", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/a5195bad3e7f56c40d45fdf03166e6e59a170c02", + "reference": "a5195bad3e7f56c40d45fdf03166e6e59a170c02", "shasum": "" }, "require": { @@ -4590,7 +4590,7 @@ "issues": "https://github.com/utopia-php/migration/issues", "source": "https://github.com/utopia-php/migration/tree/feat/sdk-23" }, - "time": "2026-05-08T08:02:48+00:00" + "time": "2026-05-08T11:11:49+00:00" }, { "name": "utopia-php/mongo", From 687698001f9f254e6c1aae3eb18c32820dbf64a0 Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Fri, 8 May 2026 16:30:02 +0100 Subject: [PATCH 05/26] chore: bump utopia-php/migration to 80e9a04 (SDK 23 nested typed object fixes) --- composer.lock | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/composer.lock b/composer.lock index 2c782e8524..b39b0f88af 100644 --- a/composer.lock +++ b/composer.lock @@ -69,16 +69,16 @@ }, { "name": "appwrite/appwrite", - "version": "23.0.0", + "version": "23.1.0", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-for-php.git", - "reference": "d22b36167931294bec4259f0803aba14537030ec" + "reference": "2f275921f10ceb7cff99f2d463f7328b296234fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/d22b36167931294bec4259f0803aba14537030ec", - "reference": "d22b36167931294bec4259f0803aba14537030ec", + "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/2f275921f10ceb7cff99f2d463f7328b296234fa", + "reference": "2f275921f10ceb7cff99f2d463f7328b296234fa", "shasum": "" }, "require": { @@ -104,10 +104,10 @@ "support": { "email": "team@appwrite.io", "issues": "https://github.com/appwrite/sdk-for-php/issues", - "source": "https://github.com/appwrite/sdk-for-php/tree/23.0.0", + "source": "https://github.com/appwrite/sdk-for-php/tree/23.1.0", "url": "https://appwrite.io/support" }, - "time": "2026-04-16T12:38:44+00:00" + "time": "2026-05-08T13:44:58+00:00" }, { "name": "appwrite/php-clamav", @@ -3614,16 +3614,16 @@ }, { "name": "utopia-php/cache", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/utopia-php/cache.git", - "reference": "05ceba981436a4022553f7aaa2a05fa049d0f71c" + "reference": "d36f9050c39c02e09a7763389c9e71258e74af1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/05ceba981436a4022553f7aaa2a05fa049d0f71c", - "reference": "05ceba981436a4022553f7aaa2a05fa049d0f71c", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/d36f9050c39c02e09a7763389c9e71258e74af1f", + "reference": "d36f9050c39c02e09a7763389c9e71258e74af1f", "shasum": "" }, "require": { @@ -3660,9 +3660,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/1.0.1" + "source": "https://github.com/utopia-php/cache/tree/1.0.2" }, - "time": "2026-03-12T03:39:09+00:00" + "time": "2026-05-08T11:40:20+00:00" }, { "name": "utopia-php/cli", @@ -4543,12 +4543,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "a5195bad3e7f56c40d45fdf03166e6e59a170c02" + "reference": "80e9a0498e1a1a2667607fa4b4aca44a7b4590ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/a5195bad3e7f56c40d45fdf03166e6e59a170c02", - "reference": "a5195bad3e7f56c40d45fdf03166e6e59a170c02", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/80e9a0498e1a1a2667607fa4b4aca44a7b4590ab", + "reference": "80e9a0498e1a1a2667607fa4b4aca44a7b4590ab", "shasum": "" }, "require": { @@ -4590,7 +4590,7 @@ "issues": "https://github.com/utopia-php/migration/issues", "source": "https://github.com/utopia-php/migration/tree/feat/sdk-23" }, - "time": "2026-05-08T11:11:49+00:00" + "time": "2026-05-08T15:29:18+00:00" }, { "name": "utopia-php/mongo", From c94ae409e862e8b80a15421a72108e9b183ada75 Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Fri, 8 May 2026 16:50:04 +0100 Subject: [PATCH 06/26] chore: refresh composer.lock for utopia-php/fetch ^1.1 and logger 0.8.* --- composer.lock | 100 +++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/composer.lock b/composer.lock index ba94ed274c..3e1d22321e 100644 --- a/composer.lock +++ b/composer.lock @@ -3411,21 +3411,21 @@ }, { "name": "utopia-php/agents", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/utopia-php/agents.git", - "reference": "052227953678a30ecc4b5467401fcb0b2386471e" + "reference": "0703f4cae02261e09a1bf0d39a4b1ce649cae634" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/agents/zipball/052227953678a30ecc4b5467401fcb0b2386471e", - "reference": "052227953678a30ecc4b5467401fcb0b2386471e", + "url": "https://api.github.com/repos/utopia-php/agents/zipball/0703f4cae02261e09a1bf0d39a4b1ce649cae634", + "reference": "0703f4cae02261e09a1bf0d39a4b1ce649cae634", "shasum": "" }, "require": { "php": ">=8.3", - "utopia-php/fetch": "0.5.*" + "utopia-php/fetch": "^1.1.0" }, "require-dev": { "laravel/pint": "^1.18", @@ -3458,9 +3458,9 @@ ], "support": { "issues": "https://github.com/utopia-php/agents/issues", - "source": "https://github.com/utopia-php/agents/tree/1.2.1" + "source": "https://github.com/utopia-php/agents/tree/1.2.2" }, - "time": "2026-02-24T06:03:55+00:00" + "time": "2026-05-08T10:38:23+00:00" }, { "name": "utopia-php/analytics", @@ -3510,22 +3510,22 @@ }, { "name": "utopia-php/audit", - "version": "2.2.2", + "version": "2.2.3", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "90886c202e7983999e6b6a8201004d5ab61d4b57" + "reference": "95e9961fa286d2fdb6bf3eaa198f21d51bf58d9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/90886c202e7983999e6b6a8201004d5ab61d4b57", - "reference": "90886c202e7983999e6b6a8201004d5ab61d4b57", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/95e9961fa286d2fdb6bf3eaa198f21d51bf58d9c", + "reference": "95e9961fa286d2fdb6bf3eaa198f21d51bf58d9c", "shasum": "" }, "require": { "php": ">=8.0", "utopia-php/database": "5.*", - "utopia-php/fetch": "0.5.*", + "utopia-php/fetch": "^1.1", "utopia-php/validators": "0.2.*" }, "require-dev": { @@ -3553,9 +3553,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/2.2.2" + "source": "https://github.com/utopia-php/audit/tree/2.2.3" }, - "time": "2026-05-04T06:48:58+00:00" + "time": "2026-05-08T10:38:23+00:00" }, { "name": "utopia-php/auth", @@ -4180,22 +4180,21 @@ }, { "name": "utopia-php/emails", - "version": "0.6.9", + "version": "0.6.10", "source": { "type": "git", "url": "https://github.com/utopia-php/emails.git", - "reference": "3a59fb392a03a88f5497e5fdb0ea84a252a4dfdf" + "reference": "2e397754ce68c2ba918564b9f31d9923c0a90429" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/emails/zipball/3a59fb392a03a88f5497e5fdb0ea84a252a4dfdf", - "reference": "3a59fb392a03a88f5497e5fdb0ea84a252a4dfdf", + "url": "https://api.github.com/repos/utopia-php/emails/zipball/2e397754ce68c2ba918564b9f31d9923c0a90429", + "reference": "2e397754ce68c2ba918564b9f31d9923c0a90429", "shasum": "" }, "require": { "php": ">=8.0", "utopia-php/domains": "^1.0", - "utopia-php/fetch": "^0.5", "utopia-php/validators": "0.*" }, "require-dev": { @@ -4203,7 +4202,8 @@ "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.3", "utopia-php/cli": "^0.22", - "utopia-php/console": "0.*" + "utopia-php/console": "0.*", + "utopia-php/fetch": "^1.1" }, "type": "library", "autoload": { @@ -4235,22 +4235,22 @@ ], "support": { "issues": "https://github.com/utopia-php/emails/issues", - "source": "https://github.com/utopia-php/emails/tree/0.6.9" + "source": "https://github.com/utopia-php/emails/tree/0.6.10" }, - "time": "2026-03-14T13:52:56+00:00" + "time": "2026-05-08T10:16:22+00:00" }, { "name": "utopia-php/fetch", - "version": "0.5.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/utopia-php/fetch.git", - "reference": "a96a010e1c273f3888765449687baf58cbc61fcd" + "reference": "64f2b3a789480f1deb102ce684dac4217d8e98d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/fetch/zipball/a96a010e1c273f3888765449687baf58cbc61fcd", - "reference": "a96a010e1c273f3888765449687baf58cbc61fcd", + "url": "https://api.github.com/repos/utopia-php/fetch/zipball/64f2b3a789480f1deb102ce684dac4217d8e98d5", + "reference": "64f2b3a789480f1deb102ce684dac4217d8e98d5", "shasum": "" }, "require": { @@ -4259,7 +4259,8 @@ "require-dev": { "laravel/pint": "^1.5.0", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^9.5", + "swoole/ide-helper": "^6.0" }, "type": "library", "autoload": { @@ -4274,9 +4275,9 @@ "description": "A simple library that provides an interface for making HTTP Requests.", "support": { "issues": "https://github.com/utopia-php/fetch/issues", - "source": "https://github.com/utopia-php/fetch/tree/0.5.1" + "source": "https://github.com/utopia-php/fetch/tree/1.1.2" }, - "time": "2025-12-18T16:25:10+00:00" + "time": "2026-04-29T11:19:19+00:00" }, { "name": "utopia-php/http", @@ -4434,20 +4435,21 @@ }, { "name": "utopia-php/logger", - "version": "0.6.2", + "version": "0.8.0", "source": { "type": "git", "url": "https://github.com/utopia-php/logger.git", - "reference": "25b5bd2ad8bb51292f76332faa7034644fd0941d" + "reference": "132236c42222cd614cb882938a48f8729ef3118b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/25b5bd2ad8bb51292f76332faa7034644fd0941d", - "reference": "25b5bd2ad8bb51292f76332faa7034644fd0941d", + "url": "https://api.github.com/repos/utopia-php/logger/zipball/132236c42222cd614cb882938a48f8729ef3118b", + "reference": "132236c42222cd614cb882938a48f8729ef3118b", "shasum": "" }, "require": { - "php": ">=8.0" + "php": ">=8.1", + "utopia-php/fetch": "^1.1" }, "require-dev": { "laravel/pint": "1.2.*", @@ -4482,9 +4484,9 @@ ], "support": { "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.6.2" + "source": "https://github.com/utopia-php/logger/tree/0.8.0" }, - "time": "2024-10-14T16:02:49+00:00" + "time": "2026-05-05T06:04:27+00:00" }, { "name": "utopia-php/messaging", @@ -5236,23 +5238,23 @@ }, { "name": "utopia-php/vcs", - "version": "3.2.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "44a84ab52b42fc12f812b4d7331286b519d39db3" + "reference": "03ccd12b75d67d29094eb760b468fddde4b6b5e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/44a84ab52b42fc12f812b4d7331286b519d39db3", - "reference": "44a84ab52b42fc12f812b4d7331286b519d39db3", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/03ccd12b75d67d29094eb760b468fddde4b6b5e5", + "reference": "03ccd12b75d67d29094eb760b468fddde4b6b5e5", "shasum": "" }, "require": { "adhocore/jwt": "^1.1", "php": ">=8.0", "utopia-php/cache": "1.0.*", - "utopia-php/fetch": "0.5.*" + "utopia-php/fetch": "^1.1" }, "require-dev": { "laravel/pint": "1.*.*", @@ -5279,9 +5281,9 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/3.2.0" + "source": "https://github.com/utopia-php/vcs/tree/3.2.1" }, - "time": "2026-04-08T16:00:31+00:00" + "time": "2026-05-08T10:13:53+00:00" }, { "name": "utopia-php/websocket", @@ -5474,16 +5476,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.27.5", + "version": "1.28.1", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "9faa38b48d422f3da764a719712905c83b3922cb" + "reference": "009118ccda8ccece2b9fc043c158cb1dd3efaa88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9faa38b48d422f3da764a719712905c83b3922cb", - "reference": "9faa38b48d422f3da764a719712905c83b3922cb", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/009118ccda8ccece2b9fc043c158cb1dd3efaa88", + "reference": "009118ccda8ccece2b9fc043c158cb1dd3efaa88", "shasum": "" }, "require": { @@ -5519,9 +5521,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.27.5" + "source": "https://github.com/appwrite/sdk-generator/tree/1.28.1" }, - "time": "2026-05-05T12:09:40+00:00" + "time": "2026-05-08T13:24:33+00:00" }, { "name": "brianium/paratest", From 5313460c7c27d187c44e370abe3fac3440fc5e62 Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Fri, 8 May 2026 17:53:09 +0100 Subject: [PATCH 07/26] fix(migrations): pass destination project DSN resolver to DestinationAppwrite Migration lib's `_databases.database` resolver now defaults to empty when no callable is supplied (utopia-php/migration ff3b444). The runtime falls back via `$project->getAttribute('database')`, but tests hit DSN("mysql://") and a 500 because that fallback is also empty for fresh test projects. Pass an explicit resolver returning the destination project's `database` attribute so migrated databases store a usable DSN. --- src/Appwrite/Platform/Workers/Migrations.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index b6c295b3bb..ddb5900f2e 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -293,6 +293,7 @@ class Migrations extends Action $this->getDatabasesDB, Config::getParam('collections', [])['databases']['collections'], OnDuplicate::tryFrom($options['onDuplicate'] ?? '') ?? OnDuplicate::Fail, + fn () => (string) $this->project->getAttribute('database', ''), ), DestinationCSV::getName() => new DestinationCSV( $this->deviceForFiles, From 6ee2196fae1f0b27c694b65acfc2320beb91d556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sat, 9 May 2026 09:54:54 +0200 Subject: [PATCH 08/26] Fix git hint regnerating nonstop --- src/Appwrite/Vcs/Comment.php | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Vcs/Comment.php b/src/Appwrite/Vcs/Comment.php index 4dc0174e50..8741ecff6c 100644 --- a/src/Appwrite/Vcs/Comment.php +++ b/src/Appwrite/Vcs/Comment.php @@ -50,6 +50,8 @@ class Comment protected string $statePrefix = '[appwrite]: #'; + protected ?string $tip = null; + /** * @var mixed[] $builds */ @@ -81,7 +83,14 @@ class Comment public function generateComment(): string { - $json = \json_encode($this->builds); + if ($this->tip === null) { + $this->tip = $this->tips[\array_rand($this->tips)]; + } + + $json = \json_encode([ + 'builds' => $this->builds, + 'tip' => $this->tip, + ]); $text = $this->statePrefix . \base64_encode($json) . "\n\n"; @@ -226,8 +235,7 @@ class Comment $i++; } - $tip = $this->tips[array_rand($this->tips)]; - $text .= "\n
\n\n> [!TIP]\n> $tip\n\n"; + $text .= "\n
\n\n> [!TIP]\n> {$this->tip}\n\n"; return $text; } @@ -252,8 +260,15 @@ class Comment $json = \base64_decode($state); - $builds = \json_decode($json, true); - $this->builds = \is_array($builds) ? $builds : []; + $data = \json_decode($json, true); + + if (\is_array($data) && \array_key_exists('builds', $data)) { + $this->builds = \is_array($data['builds']) ? $data['builds'] : []; + $this->tip = $data['tip'] ?? null; + } else { + // Backward compatibility with old state format (builds array only) + $this->builds = \is_array($data) ? $data : []; + } return $this; } From 43777ee6d99b79cc940c98be080247c8b3cd46d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sat, 9 May 2026 10:16:19 +0200 Subject: [PATCH 09/26] Add unit tests for github hints --- tests/unit/Vcs/CommentTest.php | 144 +++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 tests/unit/Vcs/CommentTest.php diff --git a/tests/unit/Vcs/CommentTest.php b/tests/unit/Vcs/CommentTest.php new file mode 100644 index 0000000000..c6f69e2f1b --- /dev/null +++ b/tests/unit/Vcs/CommentTest.php @@ -0,0 +1,144 @@ + 'localhost']); + $comment->addBuild( + new Document(['$id' => 'project1', 'name' => 'Test Project', 'region' => 'default']), + new Document(['$id' => 'func1', 'name' => 'Test Function']), + 'function', + 'ready', + 'dep1', + ['type' => 'logs'], + '' + ); + + $first = $comment->generateComment(); + $firstTip = $this->extractTip($first); + + $this->assertNotNull($firstTip); + $this->assertNotEmpty($firstTip); + + $second = $comment->generateComment(); + $secondTip = $this->extractTip($second); + + $this->assertEquals($firstTip, $secondTip); + } + + public function testTipIsRestoredFromParsedComment(): void + { + $comment = new Comment(['consoleHostname' => 'localhost']); + $comment->addBuild( + new Document(['$id' => 'project1', 'name' => 'Test Project', 'region' => 'default']), + new Document(['$id' => 'func1', 'name' => 'Test Function']), + 'function', + 'ready', + 'dep1', + ['type' => 'logs'], + '' + ); + + $original = $comment->generateComment(); + $originalTip = $this->extractTip($original); + + $parsed = new Comment(['consoleHostname' => 'localhost']); + $parsed->parseComment($original); + $parsed->addBuild( + new Document(['$id' => 'project1', 'name' => 'Test Project', 'region' => 'default']), + new Document(['$id' => 'func2', 'name' => 'Another Function']), + 'function', + 'building', + 'dep2', + ['type' => 'logs'], + '' + ); + + $regenerated = $parsed->generateComment(); + $regeneratedTip = $this->extractTip($regenerated); + + $this->assertEquals($originalTip, $regeneratedTip); + } + + public function testBackwardCompatibilityWithOldStateFormat(): void + { + $oldBuilds = [ + 'project1_func1' => [ + 'projectName' => 'Test Project', + 'projectId' => 'project1', + 'region' => 'default', + 'resourceName' => 'Test Function', + 'resourceId' => 'func1', + 'resourceType' => 'function', + 'buildStatus' => 'ready', + 'deploymentId' => 'dep1', + 'action' => ['type' => 'logs'], + 'previewUrl' => '', + ], + ]; + + $oldState = '[appwrite]: #' . \base64_encode(\json_encode($oldBuilds)) . "\n\n"; + $oldState .= "> [!TIP]\n> Old tip that should be ignored\n\n"; + + $comment = new Comment(['consoleHostname' => 'localhost']); + $comment->parseComment($oldState); + + $new = $comment->generateComment(); + $newTip = $this->extractTip($new); + + $this->assertNotNull($newTip); + $this->assertNotEquals('Old tip that should be ignored', $newTip); + } + + public function testParseOldStateFormatWithOnlyBuilds(): void + { + $oldBuilds = [ + 'project1_func1' => [ + 'projectName' => 'Test Project', + 'projectId' => 'project1', + 'region' => 'default', + 'resourceName' => 'Test Function', + 'resourceId' => 'func1', + 'resourceType' => 'function', + 'buildStatus' => 'ready', + 'deploymentId' => 'dep1', + 'action' => ['type' => 'logs'], + 'previewUrl' => '', + ], + ]; + + $state = '[appwrite]: #' . \base64_encode(\json_encode($oldBuilds)) . "\n\n"; + + $comment = new Comment(['consoleHostname' => 'localhost']); + $comment->parseComment($state); + + $this->assertEquals(false, $comment->isEmpty()); + + $first = $comment->generateComment(); + $firstTip = $this->extractTip($first); + + $this->assertNotNull($firstTip); + $this->assertNotEmpty($firstTip); + + $second = $comment->generateComment(); + $secondTip = $this->extractTip($second); + + $this->assertEquals($firstTip, $secondTip); + } + + private function extractTip(string $comment): ?string + { + if (\preg_match('/> \[!TIP\]\n> (.+)/', $comment, $matches)) { + return $matches[1]; + } + + return null; + } +} From 76a41d70b0150b59081e0087c8954e16bfad6a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sat, 9 May 2026 10:51:46 +0200 Subject: [PATCH 10/26] Dual read for google oauth secret Will allow future support for more params --- src/Appwrite/Auth/OAuth2/Google.php | 33 +++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2/Google.php b/src/Appwrite/Auth/OAuth2/Google.php index 79894c2422..0ee495df7d 100644 --- a/src/Appwrite/Auth/OAuth2/Google.php +++ b/src/Appwrite/Auth/OAuth2/Google.php @@ -72,7 +72,7 @@ class Google extends OAuth2 'https://oauth2.googleapis.com/token?' . \http_build_query([ 'code' => $code, 'client_id' => $this->appID, - 'client_secret' => $this->appSecret, + 'client_secret' => $this->getClientSecret(), 'redirect_uri' => $this->callback, 'scope' => null, 'grant_type' => 'authorization_code' @@ -95,7 +95,7 @@ class Google extends OAuth2 'https://oauth2.googleapis.com/token?' . \http_build_query([ 'refresh_token' => $refreshToken, 'client_id' => $this->appID, - 'client_secret' => $this->appSecret, + 'client_secret' => $this->getClientSecret(), 'grant_type' => 'refresh_token' ]) ), true); @@ -177,4 +177,33 @@ class Google extends OAuth2 return $this->user; } + + /** + * Extracts the Client Secret from the JSON stored in appSecret + * + * @return string + */ + protected function getClientSecret(): string + { + $secret = $this->getAppSecret(); + + return $secret['clientSecret'] ?? ''; + } + + /** + * Decode the JSON stored in appSecret. + * Falls back to treating the raw string as the client secret for backwards compatibility. + * + * @return array + */ + protected function getAppSecret(): array + { + try { + $secret = \json_decode($this->appSecret, true, 512, JSON_THROW_ON_ERROR); + } catch (\Throwable $th) { + return ['clientSecret' => $this->appSecret]; + } + + return $secret; + } } From a5ddc465e6112fddc756a9532d71bb4ec485158e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sat, 9 May 2026 12:53:11 +0200 Subject: [PATCH 11/26] PR review fixes --- tests/unit/Vcs/CommentTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/unit/Vcs/CommentTest.php b/tests/unit/Vcs/CommentTest.php index c6f69e2f1b..29973089c6 100644 --- a/tests/unit/Vcs/CommentTest.php +++ b/tests/unit/Vcs/CommentTest.php @@ -95,6 +95,7 @@ class CommentTest extends TestCase $this->assertNotNull($newTip); $this->assertNotEquals('Old tip that should be ignored', $newTip); + $this->assertContains($newTip, $this->getTips()); } public function testParseOldStateFormatWithOnlyBuilds(): void @@ -126,6 +127,7 @@ class CommentTest extends TestCase $this->assertNotNull($firstTip); $this->assertNotEmpty($firstTip); + $this->assertContains($firstTip, $this->getTips()); $second = $comment->generateComment(); $secondTip = $this->extractTip($second); @@ -141,4 +143,12 @@ class CommentTest extends TestCase return null; } + + private function getTips(): array + { + $reflection = new \ReflectionClass(Comment::class); + $property = $reflection->getProperty('tips'); + + return $property->getValue(new Comment(['consoleHostname' => 'localhost'])); + } } From 0e939ea9d747c1bf38ace4555623acc92927e010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sat, 9 May 2026 12:58:47 +0200 Subject: [PATCH 12/26] PR review fixes --- src/Appwrite/Auth/OAuth2/Google.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Google.php b/src/Appwrite/Auth/OAuth2/Google.php index 0ee495df7d..6028bd109b 100644 --- a/src/Appwrite/Auth/OAuth2/Google.php +++ b/src/Appwrite/Auth/OAuth2/Google.php @@ -187,7 +187,7 @@ class Google extends OAuth2 { $secret = $this->getAppSecret(); - return $secret['clientSecret'] ?? ''; + return $secret['clientSecret'] ?? $this->appSecret; } /** @@ -204,6 +204,10 @@ class Google extends OAuth2 return ['clientSecret' => $this->appSecret]; } + if (!\is_array($secret)) { + return ['clientSecret' => $this->appSecret]; + } + return $secret; } } From 698fde247f85e780323c5ca8d0faa87394434c82 Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Sun, 10 May 2026 04:46:40 +0100 Subject: [PATCH 13/26] chore: bump utopia-php/migration to 0e88268 (createVariable variableId fix) --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 3e1d22321e..f5dd560166 100644 --- a/composer.lock +++ b/composer.lock @@ -4545,12 +4545,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "80e9a0498e1a1a2667607fa4b4aca44a7b4590ab" + "reference": "0e88268d10fef0475de1fa292cbb571ce64e4837" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/80e9a0498e1a1a2667607fa4b4aca44a7b4590ab", - "reference": "80e9a0498e1a1a2667607fa4b4aca44a7b4590ab", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/0e88268d10fef0475de1fa292cbb571ce64e4837", + "reference": "0e88268d10fef0475de1fa292cbb571ce64e4837", "shasum": "" }, "require": { @@ -4592,7 +4592,7 @@ "issues": "https://github.com/utopia-php/migration/issues", "source": "https://github.com/utopia-php/migration/tree/feat/sdk-23" }, - "time": "2026-05-08T15:29:18+00:00" + "time": "2026-05-09T00:23:29+00:00" }, { "name": "utopia-php/mongo", From cd445ceccfc85e39926bbe009fe200b4e050a25e Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Sun, 10 May 2026 05:49:23 +0100 Subject: [PATCH 14/26] chore: bump utopia-php/migration to 6deabc6 (Sites::create named args) --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index f5dd560166..7ba7694a08 100644 --- a/composer.lock +++ b/composer.lock @@ -4545,12 +4545,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "0e88268d10fef0475de1fa292cbb571ce64e4837" + "reference": "6deabc665472639d5615e923cc43bb3d9c8aadca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/0e88268d10fef0475de1fa292cbb571ce64e4837", - "reference": "0e88268d10fef0475de1fa292cbb571ce64e4837", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/6deabc665472639d5615e923cc43bb3d9c8aadca", + "reference": "6deabc665472639d5615e923cc43bb3d9c8aadca", "shasum": "" }, "require": { @@ -4592,7 +4592,7 @@ "issues": "https://github.com/utopia-php/migration/issues", "source": "https://github.com/utopia-php/migration/tree/feat/sdk-23" }, - "time": "2026-05-09T00:23:29+00:00" + "time": "2026-05-10T04:07:41+00:00" }, { "name": "utopia-php/mongo", From fbfde6cc776066c708000d0220373d903ef8745e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 10 May 2026 11:11:47 +0200 Subject: [PATCH 15/26] Implement google oauth prompt param. --- src/Appwrite/Auth/OAuth2/Google.php | 15 +- .../Http/Project/OAuth2/Google/Update.php | 122 ++++++++++++++++ .../Utopia/Response/Model/OAuth2Google.php | 14 ++ tests/e2e/Services/Project/OAuth2Base.php | 134 ++++++++++++++++++ 4 files changed, 284 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Google.php b/src/Appwrite/Auth/OAuth2/Google.php index 6028bd109b..2b1d2d0029 100644 --- a/src/Appwrite/Auth/OAuth2/Google.php +++ b/src/Appwrite/Auth/OAuth2/Google.php @@ -55,7 +55,7 @@ class Google extends OAuth2 'state' => \json_encode($this->state), 'response_type' => 'code', 'access_type' => 'offline', - 'prompt' => 'consent' + 'prompt' => $this->getPrompt() ]); } @@ -190,6 +190,19 @@ class Google extends OAuth2 return $secret['clientSecret'] ?? $this->appSecret; } + /** + * Extracts the prompt values from the JSON stored in appSecret + * + * @return string + */ + protected function getPrompt(): string + { + $secret = $this->getAppSecret(); + $prompt = $secret['prompt'] ?? ['consent']; + + return \implode(' ', $prompt); + } + /** * Decode the JSON stored in appSecret. * Falls back to treating the raw string as the client secret for backwards compatibility. diff --git a/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Google/Update.php b/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Google/Update.php index 9b985f4aed..9fffeff73b 100644 --- a/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Google/Update.php +++ b/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Google/Update.php @@ -3,8 +3,22 @@ namespace Appwrite\Platform\Modules\Project\Http\Project\OAuth2\Google; use Appwrite\Auth\OAuth2\Google; +use Appwrite\Event\Event as QueueEvent; +use Appwrite\Extend\Exception; +use Appwrite\Platform\Action; use Appwrite\Platform\Modules\Project\Http\Project\OAuth2\Base; +use Appwrite\SDK\AuthType; +use Appwrite\SDK\Method; +use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; +use Utopia\Database\Database; +use Utopia\Database\Document; +use Utopia\Database\Validator\Authorization; +use Utopia\Validator\ArrayList; +use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; +use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; class Update extends Base { @@ -52,4 +66,112 @@ class Update extends Base { return 'GOCSPX-2k8gsR0000000000000000VNahJj'; } + + public static function getParameters(): array + { + return \array_merge(parent::getParameters(), [ + [ + '$id' => 'prompt', + 'name' => 'Prompt', + 'example' => '["consent"]', + 'hint' => '', + ], + ]); + } + + public function __construct() + { + $providerId = static::getProviderId(); + $providerLabel = static::getProviderLabel(); + + $this + ->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/project/oauth2/' . $providerId) + ->desc('Update project OAuth2 ' . $providerLabel) + ->groups(['api', 'project']) + ->label('scope', 'oauth2.write') + ->label('event', 'oauth2.[providerId].update') + ->label('audits.event', 'project.oauth2.[providerId].update') + ->label('audits.resource', 'project.oauth2/{response.$id}') + ->label('sdk', new Method( + namespace: 'project', + group: 'oauth2', + name: static::getProviderSDKMethod(), + description: 'Update the project OAuth2 ' . $providerLabel . ' configuration.', + auth: [AuthType::ADMIN, AuthType::KEY], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: static::getResponseModel(), + ) + ], + )) + ->param(static::getClientIdParamName(), null, new Nullable(new Text(256, 0)), static::getClientIdDescription(), optional: true) + ->param(static::getClientSecretParamName(), null, new Nullable(new Text(512, 0)), static::getClientSecretDescription(), optional: true) + ->param('prompt', null, new Nullable(new ArrayList(new WhiteList(['none', 'consent', 'select_account'], true), 3)), 'Array of Google OAuth2 prompt values. If "none" is included, it must be the only element.', optional: true) + ->param('enabled', null, new Nullable(new Boolean()), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.', true) + ->inject('response') + ->inject('dbForPlatform') + ->inject('project') + ->inject('authorization') + ->inject('queueForEvents') + ->callback($this->handle(...)); + } + + public function buildReadResponse(Document $project): Document + { + $providerId = static::getProviderId(); + $oAuthProviders = $project->getAttribute('oAuthProviders', []); + $decoded = $this->decodeStoredSecret($project); + + return new Document([ + '$id' => $providerId, + 'enabled' => $oAuthProviders[$providerId . 'Enabled'] ?? false, + static::getClientIdParamName() => $oAuthProviders[$providerId . 'Appid'] ?? '', + static::getClientSecretParamName() => '', + 'prompt' => $decoded['prompt'] ?? ['consent'], + ]); + } + + /** + * Custom callback used instead of the parent's `action()` because Google + * takes an additional optional `prompt` parameter. The method is named + * differently to avoid an LSP-incompatible override of Base::action(). + */ + public function handle( + ?string $clientId, + ?string $clientSecret, + ?array $prompt, + ?bool $enabled, + Response $response, + Database $dbForPlatform, + Document $project, + Authorization $authorization, + QueueEvent $queueForEvents + ): void { + $providerId = static::getProviderId(); + $queueForEvents->setParam('providerId', $providerId); + + if ($prompt !== null && \in_array('none', $prompt) && \count($prompt) > 1) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'When "none" is used as a prompt value, it must be the only element in the array.'); + } + + $storedRaw = $project->getAttribute('oAuthProviders', [])[$providerId . 'Secret'] ?? ''; + $existing = $this->decodeStoredSecret($project); + + // Backwards compatibility: secrets stored before the prompt feature + // were saved as plain strings. Treat the raw value as clientSecret. + if (!empty($storedRaw) && empty($existing)) { + $existing = ['clientSecret' => $storedRaw]; + } + + $encodedSecret = \json_encode([ + 'clientSecret' => $clientSecret ?? ($existing['clientSecret'] ?? ''), + 'prompt' => $prompt ?? ($existing['prompt'] ?? ['consent']), + ]); + + $project = $this->persistCredentials($project, $dbForPlatform, $authorization, $clientId, $encodedSecret, $enabled); + + $response->dynamic($this->buildReadResponse($project), static::getResponseModel()); + } } diff --git a/src/Appwrite/Utopia/Response/Model/OAuth2Google.php b/src/Appwrite/Utopia/Response/Model/OAuth2Google.php index 3dbc892631..ebef9aecf7 100644 --- a/src/Appwrite/Utopia/Response/Model/OAuth2Google.php +++ b/src/Appwrite/Utopia/Response/Model/OAuth2Google.php @@ -25,6 +25,20 @@ class OAuth2Google extends OAuth2Base return 'GOCSPX-2k8gsR0000000000000000VNahJj'; } + public function __construct() + { + parent::__construct(); + + $this->addRule('prompt', [ + 'type' => self::TYPE_ENUM, + 'description' => 'Google OAuth2 prompt values.', + 'default' => ['consent'], + 'example' => ['consent'], + 'array' => true, + 'enum' => ['none', 'consent', 'select_account'], + ]); + } + /** * Get Name * diff --git a/tests/e2e/Services/Project/OAuth2Base.php b/tests/e2e/Services/Project/OAuth2Base.php index 47d92a2a58..71d0dc29ec 100644 --- a/tests/e2e/Services/Project/OAuth2Base.php +++ b/tests/e2e/Services/Project/OAuth2Base.php @@ -2564,6 +2564,140 @@ trait OAuth2Base ]); } + // ========================================================================= + // Update Google (clientId + clientSecret + optional prompt) + // ========================================================================= + + public function testUpdateOAuth2Google(): void + { + $response = $this->updateOAuth2('google', [ + 'clientId' => '120000000095-92ifjb00000000000000000000g7ijfb.apps.googleusercontent.com', + 'clientSecret' => 'GOCSPX-2k8gsR0000000000000000VNahJj', + 'prompt' => ['select_account'], + 'enabled' => false, + ]); + + $this->assertSame(200, $response['headers']['status-code']); + $this->assertSame('google', $response['body']['$id']); + $this->assertSame('120000000095-92ifjb00000000000000000000g7ijfb.apps.googleusercontent.com', $response['body']['clientId']); + $this->assertSame(['select_account'], $response['body']['prompt']); + + // Cleanup + $this->updateOAuth2('google', [ + 'clientId' => '', + 'clientSecret' => '', + 'enabled' => false, + ]); + } + + public function testUpdateOAuth2GooglePartialPreservesPrompt(): void + { + // Seed clientSecret + prompt. + $this->updateOAuth2('google', [ + 'clientId' => 'google-seed-client', + 'clientSecret' => 'google-seed-secret', + 'prompt' => ['consent', 'select_account'], + 'enabled' => false, + ]); + + // Update only clientId. + $response = $this->updateOAuth2('google', [ + 'clientId' => 'google-rotated-client', + ]); + + $this->assertSame(200, $response['headers']['status-code']); + $this->assertSame('google-rotated-client', $response['body']['clientId']); + $this->assertSame(['consent', 'select_account'], $response['body']['prompt']); + + // Cleanup + $this->updateOAuth2('google', [ + 'clientId' => '', + 'clientSecret' => '', + 'enabled' => false, + ]); + } + + public function testUpdateOAuth2GooglePromptNoneAloneRejected(): void + { + $response = $this->updateOAuth2('google', [ + 'clientId' => 'whatever', + 'clientSecret' => 'whatever', + 'prompt' => ['none', 'consent'], + 'enabled' => false, + ]); + + $this->assertSame(400, $response['headers']['status-code']); + $this->assertSame('general_argument_invalid', $response['body']['type']); + } + + public function testUpdateOAuth2GooglePromptNoneAloneAccepted(): void + { + $response = $this->updateOAuth2('google', [ + 'clientId' => '120000000095-92ifjb00000000000000000000g7ijfb.apps.googleusercontent.com', + 'clientSecret' => 'GOCSPX-2k8gsR0000000000000000VNahJj', + 'prompt' => ['none'], + 'enabled' => false, + ]); + + $this->assertSame(200, $response['headers']['status-code']); + $this->assertSame(['none'], $response['body']['prompt']); + + // Cleanup + $this->updateOAuth2('google', [ + 'clientId' => '', + 'clientSecret' => '', + 'enabled' => false, + ]); + } + + public function testUpdateOAuth2GoogleEnableAndReadBack(): void + { + $update = $this->updateOAuth2('google', [ + 'clientId' => 'google-enable-client', + 'clientSecret' => 'google-enable-secret', + 'prompt' => ['select_account'], + 'enabled' => true, + ]); + + $this->assertSame(200, $update['headers']['status-code']); + $this->assertTrue($update['body']['enabled']); + + // GET must hide clientSecret while keeping clientId and prompt. + $get = $this->getOAuth2Provider('google'); + $this->assertSame(200, $get['headers']['status-code']); + $this->assertTrue($get['body']['enabled']); + $this->assertSame('google-enable-client', $get['body']['clientId']); + $this->assertSame(['select_account'], $get['body']['prompt']); + $this->assertSame('', $get['body']['clientSecret']); + + // Cleanup + $this->updateOAuth2('google', [ + 'clientId' => '', + 'clientSecret' => '', + 'enabled' => false, + ]); + } + + public function testUpdateOAuth2GoogleDefaultPrompt(): void + { + // When prompt is omitted, the default should be ['consent']. + $response = $this->updateOAuth2('google', [ + 'clientId' => 'google-default-client', + 'clientSecret' => 'google-default-secret', + 'enabled' => false, + ]); + + $this->assertSame(200, $response['headers']['status-code']); + $this->assertSame(['consent'], $response['body']['prompt']); + + // Cleanup + $this->updateOAuth2('google', [ + 'clientId' => '', + 'clientSecret' => '', + 'enabled' => false, + ]); + } + // ========================================================================= // Smoke test: every plain (clientId + clientSecret) provider // From 0406d9e04de65cc68e16e2c8c05080152728f015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 10 May 2026 11:13:23 +0200 Subject: [PATCH 16/26] improve copy --- .../Modules/Project/Http/Project/OAuth2/Google/Update.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Google/Update.php b/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Google/Update.php index 9fffeff73b..7c4b0e011d 100644 --- a/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Google/Update.php +++ b/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Google/Update.php @@ -108,7 +108,7 @@ class Update extends Base )) ->param(static::getClientIdParamName(), null, new Nullable(new Text(256, 0)), static::getClientIdDescription(), optional: true) ->param(static::getClientSecretParamName(), null, new Nullable(new Text(512, 0)), static::getClientSecretDescription(), optional: true) - ->param('prompt', null, new Nullable(new ArrayList(new WhiteList(['none', 'consent', 'select_account'], true), 3)), 'Array of Google OAuth2 prompt values. If "none" is included, it must be the only element.', optional: true) + ->param('prompt', null, new Nullable(new ArrayList(new WhiteList(['none', 'consent', 'select_account'], true), 3)), 'Array of Google OAuth2 prompt values. If "none" is included, it must be the only element. "none" means: don\'t display any authentication or consent screens. Must not be specified with other values. "consent" means: prompt the user for consent. "select_account" means: prompt the user to select an account.', optional: true) ->param('enabled', null, new Nullable(new Boolean()), 'OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid.', true) ->inject('response') ->inject('dbForPlatform') From e3dc30ad93cf9ac4c643d3e3b81a3a4690e651da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 10 May 2026 12:03:05 +0200 Subject: [PATCH 17/26] PR review fixes --- src/Appwrite/Auth/OAuth2/Google.php | 6 +++++- .../Project/Http/Project/OAuth2/Google/Update.php | 10 ++++++++-- tests/e2e/Services/Project/OAuth2Base.php | 13 +++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2/Google.php b/src/Appwrite/Auth/OAuth2/Google.php index 2b1d2d0029..1166a313c6 100644 --- a/src/Appwrite/Auth/OAuth2/Google.php +++ b/src/Appwrite/Auth/OAuth2/Google.php @@ -198,7 +198,11 @@ class Google extends OAuth2 protected function getPrompt(): string { $secret = $this->getAppSecret(); - $prompt = $secret['prompt'] ?? ['consent']; + $prompt = $secret['prompt'] ?? []; + + if (empty($prompt)) { + $prompt = ['consent']; + } return \implode(' ', $prompt); } diff --git a/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Google/Update.php b/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Google/Update.php index 7c4b0e011d..2a061d09ce 100644 --- a/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Google/Update.php +++ b/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Google/Update.php @@ -152,8 +152,14 @@ class Update extends Base $providerId = static::getProviderId(); $queueForEvents->setParam('providerId', $providerId); - if ($prompt !== null && \in_array('none', $prompt) && \count($prompt) > 1) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'When "none" is used as a prompt value, it must be the only element in the array.'); + if ($prompt !== null) { + if (empty($prompt)) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Prompt array cannot be empty.'); + } + + if (\in_array('none', $prompt) && \count($prompt) > 1) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'When "none" is used as a prompt value, it must be the only element in the array.'); + } } $storedRaw = $project->getAttribute('oAuthProviders', [])[$providerId . 'Secret'] ?? ''; diff --git a/tests/e2e/Services/Project/OAuth2Base.php b/tests/e2e/Services/Project/OAuth2Base.php index 71d0dc29ec..7ed5ff2f61 100644 --- a/tests/e2e/Services/Project/OAuth2Base.php +++ b/tests/e2e/Services/Project/OAuth2Base.php @@ -2630,6 +2630,19 @@ trait OAuth2Base $this->assertSame('general_argument_invalid', $response['body']['type']); } + public function testUpdateOAuth2GooglePromptEmptyArrayRejected(): void + { + $response = $this->updateOAuth2('google', [ + 'clientId' => 'whatever', + 'clientSecret' => 'whatever', + 'prompt' => [], + 'enabled' => false, + ]); + + $this->assertSame(400, $response['headers']['status-code']); + $this->assertSame('general_argument_invalid', $response['body']['type']); + } + public function testUpdateOAuth2GooglePromptNoneAloneAccepted(): void { $response = $this->updateOAuth2('google', [ From e45e5a09f49194b7075298484d0a0053d4246889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 10 May 2026 13:19:18 +0200 Subject: [PATCH 18/26] Reorder tests to make them pass --- tests/e2e/Services/Project/OAuth2Base.php | 46 +++++++++++++---------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/tests/e2e/Services/Project/OAuth2Base.php b/tests/e2e/Services/Project/OAuth2Base.php index 7ed5ff2f61..5959a584ea 100644 --- a/tests/e2e/Services/Project/OAuth2Base.php +++ b/tests/e2e/Services/Project/OAuth2Base.php @@ -2568,6 +2568,32 @@ trait OAuth2Base // Update Google (clientId + clientSecret + optional prompt) // ========================================================================= + /** + * Default prompt MUST run before any other Google test that sets a custom + * prompt value. The global resetProjectOAuth2() only clears Amazon state, + * so Google state leaks across tests in the same class. Running this first + * guarantees the stored JSON blob has no pre-existing "prompt" key. + */ + public function testUpdateOAuth2GoogleDefaultPrompt(): void + { + // When prompt is omitted and nothing is stored, the default is ['consent']. + $response = $this->updateOAuth2('google', [ + 'clientId' => 'google-default-client', + 'clientSecret' => 'google-default-secret', + 'enabled' => false, + ]); + + $this->assertSame(200, $response['headers']['status-code']); + $this->assertSame(['consent'], $response['body']['prompt']); + + // Cleanup + $this->updateOAuth2('google', [ + 'clientId' => '', + 'clientSecret' => '', + 'enabled' => false, + ]); + } + public function testUpdateOAuth2Google(): void { $response = $this->updateOAuth2('google', [ @@ -2691,26 +2717,6 @@ trait OAuth2Base ]); } - public function testUpdateOAuth2GoogleDefaultPrompt(): void - { - // When prompt is omitted, the default should be ['consent']. - $response = $this->updateOAuth2('google', [ - 'clientId' => 'google-default-client', - 'clientSecret' => 'google-default-secret', - 'enabled' => false, - ]); - - $this->assertSame(200, $response['headers']['status-code']); - $this->assertSame(['consent'], $response['body']['prompt']); - - // Cleanup - $this->updateOAuth2('google', [ - 'clientId' => '', - 'clientSecret' => '', - 'enabled' => false, - ]); - } - // ========================================================================= // Smoke test: every plain (clientId + clientSecret) provider // From fd625fca7b9a4d54e118467f28305d750136e269 Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Sun, 10 May 2026 22:09:39 +0100 Subject: [PATCH 19/26] fix(migrations): preserve source DSN for documentsdb/vectorsdb resolver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migration lib 1.10.2's getDatabaseDSN resolver returns the value written into destination's _databases.database. The previous resolver always returned the project's main DSN (mongodb in default CI) for every database type, including documentsdb / vectorsdb — which are routed to their own adapters (mongodb / postgresql) per _APP_DB_ADAPTER_DOCUMENTSDB / _APP_DB_ADAPTER_VECTORSDB. The wrong DSN routed vectorsdb attribute creates back to mongodb, producing 'Vector types are not supported by the current database' on the MixedDatabases / VectorsDB migration tests. Mirror cloud's resolver: keep the source DSN for documentsdb / vectorsdb (they target dedicated hosts), use destination project's main DSN otherwise. --- src/Appwrite/Platform/Workers/Migrations.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index ddb5900f2e..41f82f4dd0 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -293,7 +293,10 @@ class Migrations extends Action $this->getDatabasesDB, Config::getParam('collections', [])['databases']['collections'], OnDuplicate::tryFrom($options['onDuplicate'] ?? '') ?? OnDuplicate::Fail, - fn () => (string) $this->project->getAttribute('database', ''), + fn (ResourceDatabase $resource) => match ($resource->getType()) { + DATABASE_TYPE_DOCUMENTSDB, DATABASE_TYPE_VECTORSDB => (string) $resource->getDatabase(), + default => (string) $this->project->getAttribute('database', ''), + }, ), DestinationCSV::getName() => new DestinationCSV( $this->deviceForFiles, From 7b5cb379c4648fa4449cb2bc003391e03df740d0 Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Mon, 11 May 2026 02:21:18 +0100 Subject: [PATCH 20/26] chore: bump utopia-php/migration to e5dc657 (deno runtime fallback) --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 7ba7694a08..a8b83a5011 100644 --- a/composer.lock +++ b/composer.lock @@ -4545,12 +4545,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "6deabc665472639d5615e923cc43bb3d9c8aadca" + "reference": "e5dc657423bea96941cd71e5f0b060fd8b047b56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/6deabc665472639d5615e923cc43bb3d9c8aadca", - "reference": "6deabc665472639d5615e923cc43bb3d9c8aadca", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/e5dc657423bea96941cd71e5f0b060fd8b047b56", + "reference": "e5dc657423bea96941cd71e5f0b060fd8b047b56", "shasum": "" }, "require": { @@ -4592,7 +4592,7 @@ "issues": "https://github.com/utopia-php/migration/issues", "source": "https://github.com/utopia-php/migration/tree/feat/sdk-23" }, - "time": "2026-05-10T04:07:41+00:00" + "time": "2026-05-11T01:20:10+00:00" }, { "name": "utopia-php/mongo", From 64c9d8d85f0b355e623d276aa0e1bec83f5a75e8 Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Mon, 11 May 2026 02:28:07 +0100 Subject: [PATCH 21/26] chore: bump utopia-php/migration to a9bdfba (revert deno fallback) --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index a8b83a5011..cf3d01d31d 100644 --- a/composer.lock +++ b/composer.lock @@ -4545,12 +4545,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "e5dc657423bea96941cd71e5f0b060fd8b047b56" + "reference": "a9bdfba24cd1f069a0c4f28a6060999e5741fc28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/e5dc657423bea96941cd71e5f0b060fd8b047b56", - "reference": "e5dc657423bea96941cd71e5f0b060fd8b047b56", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/a9bdfba24cd1f069a0c4f28a6060999e5741fc28", + "reference": "a9bdfba24cd1f069a0c4f28a6060999e5741fc28", "shasum": "" }, "require": { @@ -4592,7 +4592,7 @@ "issues": "https://github.com/utopia-php/migration/issues", "source": "https://github.com/utopia-php/migration/tree/feat/sdk-23" }, - "time": "2026-05-11T01:20:10+00:00" + "time": "2026-05-11T01:27:17+00:00" }, { "name": "utopia-php/mongo", From 4957f568cdbc2e5f00f4852074fb07827bf3fffb Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Mon, 11 May 2026 02:38:01 +0100 Subject: [PATCH 22/26] chore: bump utopia-php/migration to 4dc7270 (empty teams guard) --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index cf3d01d31d..78c1560b0e 100644 --- a/composer.lock +++ b/composer.lock @@ -4545,12 +4545,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "a9bdfba24cd1f069a0c4f28a6060999e5741fc28" + "reference": "4dc72708497729f1aacb133a814bb5b0adab5a8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/a9bdfba24cd1f069a0c4f28a6060999e5741fc28", - "reference": "a9bdfba24cd1f069a0c4f28a6060999e5741fc28", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/4dc72708497729f1aacb133a814bb5b0adab5a8d", + "reference": "4dc72708497729f1aacb133a814bb5b0adab5a8d", "shasum": "" }, "require": { @@ -4592,7 +4592,7 @@ "issues": "https://github.com/utopia-php/migration/issues", "source": "https://github.com/utopia-php/migration/tree/feat/sdk-23" }, - "time": "2026-05-11T01:27:17+00:00" + "time": "2026-05-11T01:37:10+00:00" }, { "name": "utopia-php/mongo", From 74fbbea2b3b8f2277ed256925aafb69ddd6c87b1 Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Mon, 11 May 2026 05:41:37 +0100 Subject: [PATCH 23/26] chore: bump utopia-php/abuse cfd290a + migration 447a987 (PHP >=8.2) --- composer.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/composer.lock b/composer.lock index 78c1560b0e..e9de3fc489 100644 --- a/composer.lock +++ b/composer.lock @@ -3363,12 +3363,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "2a1fb7973d848cff0ec75fa69a93bc4d2fbc7ada" + "reference": "cfd290a843971d3aec8f4ef58d47761a7da5f436" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/2a1fb7973d848cff0ec75fa69a93bc4d2fbc7ada", - "reference": "2a1fb7973d848cff0ec75fa69a93bc4d2fbc7ada", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/cfd290a843971d3aec8f4ef58d47761a7da5f436", + "reference": "cfd290a843971d3aec8f4ef58d47761a7da5f436", "shasum": "" }, "require": { @@ -3376,7 +3376,7 @@ "ext-curl": "*", "ext-pdo": "*", "ext-redis": "*", - "php": ">=8.0", + "php": ">=8.2", "utopia-php/database": "5.*" }, "require-dev": { @@ -3407,7 +3407,7 @@ "issues": "https://github.com/utopia-php/abuse/issues", "source": "https://github.com/utopia-php/abuse/tree/feat/sdk-23" }, - "time": "2026-05-08T07:04:08+00:00" + "time": "2026-05-11T01:57:16+00:00" }, { "name": "utopia-php/agents", @@ -4545,12 +4545,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "4dc72708497729f1aacb133a814bb5b0adab5a8d" + "reference": "447a98706d752254a1cc7829a6d76f3a201b799c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/4dc72708497729f1aacb133a814bb5b0adab5a8d", - "reference": "4dc72708497729f1aacb133a814bb5b0adab5a8d", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/447a98706d752254a1cc7829a6d76f3a201b799c", + "reference": "447a98706d752254a1cc7829a6d76f3a201b799c", "shasum": "" }, "require": { @@ -4558,7 +4558,7 @@ "ext-curl": "*", "ext-openssl": "*", "halaxa/json-machine": "^1.2", - "php": ">=8.1", + "php": ">=8.2", "utopia-php/database": "5.*", "utopia-php/dsn": "0.2.*", "utopia-php/storage": "2.*" @@ -4592,7 +4592,7 @@ "issues": "https://github.com/utopia-php/migration/issues", "source": "https://github.com/utopia-php/migration/tree/feat/sdk-23" }, - "time": "2026-05-11T01:37:10+00:00" + "time": "2026-05-11T01:47:15+00:00" }, { "name": "utopia-php/mongo", From f0fb7bf877ebc536e3d23b76178a39691797c515 Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Mon, 11 May 2026 05:53:03 +0100 Subject: [PATCH 24/26] test: remove orphaned VectorsDB testGetCollectionLogs Endpoint deleted in 96fe989f6d ("update composer dependencies and remove obsolete log classes") but the two test methods calling it were left behind. They have been failing with 404 on every PR since. --- .../VectorsDB/DatabasesConsoleClientTest.php | 52 ------------------- .../Databases/VectorsDBConsoleClientTest.php | 52 ------------------- 2 files changed, 104 deletions(-) diff --git a/tests/e2e/Services/Databases/VectorsDB/DatabasesConsoleClientTest.php b/tests/e2e/Services/Databases/VectorsDB/DatabasesConsoleClientTest.php index abe4d4968b..a7cad1c0f7 100644 --- a/tests/e2e/Services/Databases/VectorsDB/DatabasesConsoleClientTest.php +++ b/tests/e2e/Services/Databases/VectorsDB/DatabasesConsoleClientTest.php @@ -10,7 +10,6 @@ use Tests\E2E\Scopes\SideConsole; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; -use Utopia\Database\Query; class DatabasesConsoleClientTest extends Scope { @@ -258,55 +257,4 @@ class DatabasesConsoleClientTest extends Scope $this->assertIsArray($response['body']['documents']); } - #[Depends('testCreateCollection')] - public function testGetCollectionLogs(array $data) - { - $databaseId = $data['databaseId']; - /** - * Test for SUCCESS - */ - $logs = $this->client->call(Client::METHOD_GET, '/vectorsdb/' . $databaseId . '/collections/' . $data['moviesId'] . '/logs', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(200, $logs['headers']['status-code']); - $this->assertIsArray($logs['body']['logs']); - $this->assertIsNumeric($logs['body']['total']); - - $logs = $this->client->call(Client::METHOD_GET, '/vectorsdb/' . $databaseId . '/collections/' . $data['moviesId'] . '/logs', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [Query::limit(1)->toString()] - ]); - - $this->assertEquals(200, $logs['headers']['status-code']); - $this->assertIsArray($logs['body']['logs']); - $this->assertLessThanOrEqual(1, count($logs['body']['logs'])); - $this->assertIsNumeric($logs['body']['total']); - - $logs = $this->client->call(Client::METHOD_GET, '/vectorsdb/' . $databaseId . '/collections/' . $data['moviesId'] . '/logs', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [Query::offset(1)->toString()] - ]); - - $this->assertEquals(200, $logs['headers']['status-code']); - $this->assertIsArray($logs['body']['logs']); - $this->assertIsNumeric($logs['body']['total']); - - $logs = $this->client->call(Client::METHOD_GET, '/vectorsdb/' . $databaseId . '/collections/' . $data['moviesId'] . '/logs', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [Query::offset(1)->toString(), Query::limit(1)->toString()] - ]); - - $this->assertEquals(200, $logs['headers']['status-code']); - $this->assertIsArray($logs['body']['logs']); - $this->assertLessThanOrEqual(1, count($logs['body']['logs'])); - $this->assertIsNumeric($logs['body']['total']); - } } diff --git a/tests/e2e/Services/Databases/VectorsDBConsoleClientTest.php b/tests/e2e/Services/Databases/VectorsDBConsoleClientTest.php index 80c2bc9d5c..238d197158 100644 --- a/tests/e2e/Services/Databases/VectorsDBConsoleClientTest.php +++ b/tests/e2e/Services/Databases/VectorsDBConsoleClientTest.php @@ -10,7 +10,6 @@ use Tests\E2E\Scopes\SideConsole; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; -use Utopia\Database\Query; class VectorsDBConsoleClientTest extends Scope { @@ -258,55 +257,4 @@ class VectorsDBConsoleClientTest extends Scope $this->assertIsArray($response['body']['documents']); } - #[Depends('testCreateCollection')] - public function testGetCollectionLogs(array $data) - { - $databaseId = $data['databaseId']; - /** - * Test for SUCCESS - */ - $logs = $this->client->call(Client::METHOD_GET, '/vectorsdb/' . $databaseId . '/collections/' . $data['moviesId'] . '/logs', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(200, $logs['headers']['status-code']); - $this->assertIsArray($logs['body']['logs']); - $this->assertIsNumeric($logs['body']['total']); - - $logs = $this->client->call(Client::METHOD_GET, '/vectorsdb/' . $databaseId . '/collections/' . $data['moviesId'] . '/logs', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [Query::limit(1)->toString()] - ]); - - $this->assertEquals(200, $logs['headers']['status-code']); - $this->assertIsArray($logs['body']['logs']); - $this->assertLessThanOrEqual(1, count($logs['body']['logs'])); - $this->assertIsNumeric($logs['body']['total']); - - $logs = $this->client->call(Client::METHOD_GET, '/vectorsdb/' . $databaseId . '/collections/' . $data['moviesId'] . '/logs', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [Query::offset(1)->toString()] - ]); - - $this->assertEquals(200, $logs['headers']['status-code']); - $this->assertIsArray($logs['body']['logs']); - $this->assertIsNumeric($logs['body']['total']); - - $logs = $this->client->call(Client::METHOD_GET, '/vectorsdb/' . $databaseId . '/collections/' . $data['moviesId'] . '/logs', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [Query::offset(1)->toString(), Query::limit(1)->toString()] - ]); - - $this->assertEquals(200, $logs['headers']['status-code']); - $this->assertIsArray($logs['body']['logs']); - $this->assertLessThanOrEqual(1, count($logs['body']['logs'])); - $this->assertIsNumeric($logs['body']['total']); - } } From a902c25363edbed6cad64fcba5df342d9bee6e3d Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Mon, 11 May 2026 06:51:56 +0100 Subject: [PATCH 25/26] refactor(migrations): extract destination DSN resolver to named method Replaces the inline match closure with resolveDestinationDatabaseDsn(), mirroring cloud's worker. Adds a docblock explaining why documentsdb / vectorsdb keep the source DSN. --- src/Appwrite/Platform/Workers/Migrations.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index 41f82f4dd0..b4b8893b62 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -293,10 +293,7 @@ class Migrations extends Action $this->getDatabasesDB, Config::getParam('collections', [])['databases']['collections'], OnDuplicate::tryFrom($options['onDuplicate'] ?? '') ?? OnDuplicate::Fail, - fn (ResourceDatabase $resource) => match ($resource->getType()) { - DATABASE_TYPE_DOCUMENTSDB, DATABASE_TYPE_VECTORSDB => (string) $resource->getDatabase(), - default => (string) $this->project->getAttribute('database', ''), - }, + $this->resolveDestinationDatabaseDsn(...), ), DestinationCSV::getName() => new DestinationCSV( $this->deviceForFiles, @@ -320,6 +317,19 @@ class Migrations extends Action }; } + /** + * Legacy / tablesdb databases route to the destination project's DSN (same as a fresh + * Databases create), while documentsdb / vectorsdb keep the source DSN — the dedicated-DB + * backfill that would re-point them is not run during migrations. + */ + private function resolveDestinationDatabaseDsn(ResourceDatabase $resource): string + { + return match ($resource->getType()) { + DATABASE_TYPE_DOCUMENTSDB, DATABASE_TYPE_VECTORSDB => (string) $resource->getDatabase(), + default => (string) $this->project->getAttribute('database', ''), + }; + } + /** * @throws AuthorizationException * @throws Structure From c26ff1849a15d4b92381e6f5780902e3e6742087 Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Mon, 11 May 2026 09:37:07 +0100 Subject: [PATCH 26/26] chore: bump utopia-php/abuse to 1.3.0 and migration to 1.11.0 (released) --- composer.json | 4 ++-- composer.lock | 46 +++++++++++++++------------------------------- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/composer.json b/composer.json index c8081ebae7..7d68a838f8 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,7 @@ "ext-sockets": "*", "appwrite/php-runtimes": "0.20.*", "appwrite/php-clamav": "2.0.*", - "utopia-php/abuse": "dev-feat/sdk-23 as 1.2.x-dev", + "utopia-php/abuse": "1.3.*", "utopia-php/agents": "1.2.*", "utopia-php/analytics": "0.15.*", "utopia-php/audit": "2.2.*", @@ -74,7 +74,7 @@ "utopia-php/locale": "0.8.*", "utopia-php/logger": "0.8.*", "utopia-php/messaging": "0.22.*", - "utopia-php/migration": "dev-feat/sdk-23 as 1.10.x-dev", + "utopia-php/migration": "1.*", "utopia-php/platform": "0.13.*", "utopia-php/pools": "1.*", "utopia-php/span": "1.1.*", diff --git a/composer.lock b/composer.lock index e9de3fc489..0aa58ca03e 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": "5f5ead1235607dad79f717eef479a470", + "content-hash": "d58736fec3028d1f9aedd055e6d82684", "packages": [ { "name": "adhocore/jwt", @@ -3359,16 +3359,16 @@ }, { "name": "utopia-php/abuse", - "version": "dev-feat/sdk-23", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "cfd290a843971d3aec8f4ef58d47761a7da5f436" + "reference": "5d7efbe5c6b0cf7d06003114fd86e24ba785582f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/cfd290a843971d3aec8f4ef58d47761a7da5f436", - "reference": "cfd290a843971d3aec8f4ef58d47761a7da5f436", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/5d7efbe5c6b0cf7d06003114fd86e24ba785582f", + "reference": "5d7efbe5c6b0cf7d06003114fd86e24ba785582f", "shasum": "" }, "require": { @@ -3405,9 +3405,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/feat/sdk-23" + "source": "https://github.com/utopia-php/abuse/tree/1.3.0" }, - "time": "2026-05-11T01:57:16+00:00" + "time": "2026-05-11T08:07:02+00:00" }, { "name": "utopia-php/agents", @@ -4541,16 +4541,16 @@ }, { "name": "utopia-php/migration", - "version": "dev-feat/sdk-23", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "447a98706d752254a1cc7829a6d76f3a201b799c" + "reference": "0fca44f40ad07bf2d56e9396afa6fa6d9b098ef1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/447a98706d752254a1cc7829a6d76f3a201b799c", - "reference": "447a98706d752254a1cc7829a6d76f3a201b799c", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/0fca44f40ad07bf2d56e9396afa6fa6d9b098ef1", + "reference": "0fca44f40ad07bf2d56e9396afa6fa6d9b098ef1", "shasum": "" }, "require": { @@ -4590,9 +4590,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/feat/sdk-23" + "source": "https://github.com/utopia-php/migration/tree/1.11.0" }, - "time": "2026-05-11T01:47:15+00:00" + "time": "2026-05-11T08:13:06+00:00" }, { "name": "utopia-php/mongo", @@ -8453,25 +8453,9 @@ "time": "2024-11-07T12:36:22+00:00" } ], - "aliases": [ - { - "package": "utopia-php/abuse", - "version": "dev-feat/sdk-23", - "alias": "1.2.x-dev", - "alias_normalized": "1.2.9999999.9999999-dev" - }, - { - "package": "utopia-php/migration", - "version": "dev-feat/sdk-23", - "alias": "1.10.x-dev", - "alias_normalized": "1.10.9999999.9999999-dev" - } - ], + "aliases": [], "minimum-stability": "dev", - "stability-flags": { - "utopia-php/abuse": 20, - "utopia-php/migration": 20 - }, + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": {