From 2db6f7e72f7b4ff1f587c1eb26f06f861663e1f1 Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 12 Feb 2026 07:54:30 +0200 Subject: [PATCH 01/15] check $ruleType --- src/Appwrite/Utopia/Response.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index fd518e4a9a..3fc043725d 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -456,6 +456,8 @@ class Response extends SwooleResponse foreach ($data[$key] as $index => $item) { if ($item instanceof Document) { + $ruleType = null; + if (\is_array($rule['type'])) { foreach ($rule['type'] as $type) { $condition = false; @@ -474,7 +476,7 @@ class Response extends SwooleResponse $ruleType = $rule['type']; } - if (!self::hasModel($ruleType)) { + if ($ruleType === null || !self::hasModel($ruleType)) { throw new Exception('Missing model for rule: ' . $ruleType); } From 0eb00934f6e140505d3efdad368fe23c57bb6092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 16 Feb 2026 13:28:40 +0100 Subject: [PATCH 02/15] Fix race condition --- .../Modules/Functions/Workers/Builds.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index aaa5baf3cf..3bbb2924fa 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -932,16 +932,6 @@ class Builds extends Action $this->runGitAction('ready', $github, $providerCommitHash, $owner, $repositoryName, $project, $resource, $deployment->getId(), $dbForProject, $dbForPlatform, $queueForRealtime, $platform); } - /** Screenshot site */ - if ($resource->getCollection() === 'sites') { - $queueForScreenshots - ->setDeploymentId($deployment->getId()) - ->setProject($project) - ->trigger(); - - Console::log('Site screenshot queued'); - } - /** Set auto deploy */ $activateBuild = false; if ($deployment->getAttribute('activate') === true) { @@ -1022,6 +1012,16 @@ class Builds extends Action Console::log('Deployment activated'); } + + /** Screenshot site */ + if ($resource->getCollection() === 'sites') { + $queueForScreenshots + ->setDeploymentId($deployment->getId()) + ->setProject($project) + ->trigger(); + + Console::log('Site screenshot queued'); + } $this->afterDeploymentSuccess( $project, From 7bcc27ed07a4dd1ae9bb6223c1fc7c2e8cf463da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 16 Feb 2026 13:31:36 +0100 Subject: [PATCH 03/15] Fix linter --- src/Appwrite/Platform/Modules/Functions/Workers/Builds.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index 3bbb2924fa..0245084b5e 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -1012,7 +1012,7 @@ class Builds extends Action Console::log('Deployment activated'); } - + /** Screenshot site */ if ($resource->getCollection() === 'sites') { $queueForScreenshots From 79a0b33b92d097e8495378b102a40cd643d9ae30 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 16 Feb 2026 18:46:55 +0530 Subject: [PATCH 04/15] Set project on queueForMails so span logs include project context The Mails worker span logs were missing project.id, project.sequence, project.region, and project.database because setProject was never called on queueForMails. This adds setProject in the shared API controller and in workers (Webhooks, Migrations) that trigger mails. Also injects project into the Mails worker action. --- app/controllers/shared/api.php | 1 + src/Appwrite/Platform/Workers/Mails.php | 4 +++- src/Appwrite/Platform/Workers/Migrations.php | 1 + src/Appwrite/Platform/Workers/Webhooks.php | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index f25f9717ed..378c5d5c9c 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -496,6 +496,7 @@ Http::init() $queueForMessaging->setProject($project); $queueForFunctions->setProject($project); $queueForBuilds->setProject($project); + $queueForMails->setProject($project); /* Auto-set platforms */ $queueForFunctions->setPlatform($platform); diff --git a/src/Appwrite/Platform/Workers/Mails.php b/src/Appwrite/Platform/Workers/Mails.php index b1f17fc648..72f7cddd06 100644 --- a/src/Appwrite/Platform/Workers/Mails.php +++ b/src/Appwrite/Platform/Workers/Mails.php @@ -6,6 +6,7 @@ use Appwrite\Template\Template; use Exception; use PHPMailer\PHPMailer\PHPMailer; use Swoole\Runtime; +use Utopia\Database\Document; use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Queue\Message; @@ -32,6 +33,7 @@ class Mails extends Action $this ->desc('Mails worker') ->inject('message') + ->inject('project') ->inject('register') ->inject('log') ->callback($this->action(...)); @@ -53,7 +55,7 @@ class Mails extends Action * @return void * @throws Exception */ - public function action(Message $message, Registry $register, Log $log): void + public function action(Message $message, Document $project, Registry $register, Log $log): void { Runtime::setHookFlags(SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_TCP); $payload = $message->getPayload() ?? []; diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index c6b005c334..94cbcf341c 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -735,6 +735,7 @@ class Migrations extends Action ]; $queueForMails + ->setProject($project) ->setSubject($subject) ->setPreview($preview) ->setBody($emailBody) diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php index dbfbe591a6..56839058de 100644 --- a/src/Appwrite/Platform/Workers/Webhooks.php +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -253,6 +253,7 @@ class Webhooks extends Action ->setParam('{{year}}', date("Y")); $queueForMails + ->setProject($project) ->setSubject($subject) ->setPreview($preview) ->setBody($body->render()); From c7bbf6a987a6e5cd39b9e29bf59e7c88197d0afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 16 Feb 2026 16:14:43 +0100 Subject: [PATCH 05/15] fix org keys auth --- app/controllers/shared/api.php | 10 ++++++++-- app/init/resources.php | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 378c5d5c9c..7e4cff32f4 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -169,8 +169,10 @@ Http::init() // Handle special app role case if ($apiKey->getRole() === User::ROLE_APPS) { - // Disable authorization checks for API keys - $authorization->setDefaultStatus(false); + // Disable authorization checks for project API keys + if ($project->getId() !== 'console') { + $authorization->setDefaultStatus(false); + } $user = new User([ '$id' => '', @@ -245,6 +247,10 @@ Http::init() } } + $authorization->addRole(Role::team($team->getId())->toString()); + $authorization->addRole(Role::team($team->getId(), 'owner')->toString()); + $authorization->addRole(Role::member($team->getId())->toString()); + $queueForAudits->setUser($user); } } // Admin User Authentication diff --git a/app/init/resources.php b/app/init/resources.php index cdc2e8a367..b2a7a0189d 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -1304,6 +1304,7 @@ Http::setResource('team', function (Document $project, Database $dbForPlatform, } else { $route = $utopia->match($request); $path = !empty($route) ? $route->getPath() : $request->getURI(); + $orgHeader = $request->getHeader('x-appwrite-organization', ''); if (str_starts_with($path, '/v1/projects/:projectId')) { $uri = $request->getURI(); $pid = explode('/', $uri)[3]; @@ -1318,6 +1319,8 @@ Http::setResource('team', function (Document $project, Database $dbForPlatform, $team = $authorization->skip(fn () => $dbForPlatform->getDocument('teams', $teamId)); return $team; + } elseif (!empty($orgHeader)) { + return $authorization->skip(fn () => $dbForPlatform->getDocument('teams', $orgHeader)); } } From 4b084849206e5f2b637dc5f0ea7c0ea1be5a2311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 16 Feb 2026 16:24:33 +0100 Subject: [PATCH 06/15] Fix tests --- app/controllers/shared/api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 7e4cff32f4..15cd925f27 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -170,7 +170,7 @@ Http::init() // Handle special app role case if ($apiKey->getRole() === User::ROLE_APPS) { // Disable authorization checks for project API keys - if ($project->getId() !== 'console') { + if ($apiKey->getType() === API_KEY_STANDARD && $apiKey->getProjectId() === $project->getId()) { $authorization->setDefaultStatus(false); } From 57127d40da3c43267a09e49adc4ff57d8f95aeb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 16 Feb 2026 16:47:19 +0100 Subject: [PATCH 07/15] Fix failing tests --- docker-compose.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 5a6367f402..ab33920060 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -750,6 +750,12 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_DB_ADAPTER - _APP_SMTP_HOST - _APP_SMTP_PORT - _APP_SMTP_SECURE From 13edcbaf6f257d44b0b83e2d1cf0db51ce88bb47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 16 Feb 2026 17:06:11 +0100 Subject: [PATCH 08/15] fix abuse test; fix mail-related tests --- tests/e2e/Services/Projects/ProjectsConsoleClientTest.php | 4 ++-- tests/resources/docker/docker-compose.yml | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 1d15f10971..64556522f5 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -5384,8 +5384,8 @@ class ProjectsConsoleClientTest extends Scope ], [ 'success' => 'https://example.com', 'failure' => 'https://example.com' - ]); - $this->assertEquals(200, $response['headers']['status-code']); + ], followRedirects: false); + $this->assertEquals(301, $response['headers']['status-code']); /** Ensure any hostname is allowed */ $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, [ diff --git a/tests/resources/docker/docker-compose.yml b/tests/resources/docker/docker-compose.yml index 8530df0db6..02593f8123 100644 --- a/tests/resources/docker/docker-compose.yml +++ b/tests/resources/docker/docker-compose.yml @@ -279,6 +279,12 @@ services: - _APP_ENV - _APP_REDIS_HOST - _APP_REDIS_PORT + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_DB_ADAPTER - _APP_SMTP_HOST - _APP_SMTP_PORT From bb26a9f583eb20fca0feaadc8a3a82e48a188a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 16 Feb 2026 17:18:58 +0100 Subject: [PATCH 09/15] improve devkey test for oauth --- docker-compose.yml | 1 + .../Projects/ProjectsConsoleClientTest.php | 31 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index ab33920060..635b1cb2cf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -738,6 +738,7 @@ services: depends_on: - redis - maildev + - ${_APP_DB_HOST:-mariadb} # - smtp environment: - _APP_ENV diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 64556522f5..4eccad4966 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -5376,7 +5376,7 @@ class ProjectsConsoleClientTest extends Scope ]); $this->assertEquals(400, $response['headers']['status-code']); - /** Test oauth2 with devKey and now get oauth2 is disabled */ + /** Test oauth2 with devKey and now flow works with untrusted URL too */ $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, @@ -5385,7 +5385,36 @@ class ProjectsConsoleClientTest extends Scope 'success' => 'https://example.com', 'failure' => 'https://example.com' ], followRedirects: false); + $this->assertEquals(301, $response['headers']['status-code']); + $this->assertArrayHasKey('location', $response['headers']); + + $location = $response['headers']['location']; + + + $locationClient = new Client(); + $locationClient->setEndpoint(''); + $locationClient->addHeader('x-appwrite-dev-key', $devKey['secret']); + + $response = $locationClient->call(Client::METHOD_GET, $location, followRedirects: false); + + $this->assertEquals(301, $response['headers']['status-code']); + $this->assertArrayHasKey('location', $response['headers']); + + $location = $response['headers']['location']; + $this->assertStringStartsWith('http://appwrite:/v1/account/sessions/oauth2/callback/mock/', $response['headers']['location']); + + $response = $locationClient->call(Client::METHOD_GET, $location, followRedirects: false); + $this->assertEquals(301, $response['headers']['status-code']); + $this->assertArrayHasKey('location', $response['headers']); + + $location = $response['headers']['location']; + $this->assertStringStartsWith('http://appwrite:/v1/account/sessions/oauth2/mock/redirect', $response['headers']['location']); + + $response = $locationClient->call(Client::METHOD_GET, $location, followRedirects: false); + + $this->assertEquals(301, $response['headers']['status-code']); + $this->assertSame('https://example.com/#', $response['headers']['location']); /** Ensure any hostname is allowed */ $response = $this->client->call(Client::METHOD_GET, '/account/sessions/oauth2/' . $provider, [ From 46ed91fe487e45b3a5b8a0ea4303469ff369defb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 16 Feb 2026 17:39:12 +0100 Subject: [PATCH 10/15] Fix migratons with api keys --- app/controllers/shared/api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 15cd925f27..1b9726ded5 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -170,7 +170,7 @@ Http::init() // Handle special app role case if ($apiKey->getRole() === User::ROLE_APPS) { // Disable authorization checks for project API keys - if ($apiKey->getType() === API_KEY_STANDARD && $apiKey->getProjectId() === $project->getId()) { + if (($apiKey->getType() === API_KEY_STANDARD || $apiKey->getType() === API_KEY_DYNAMIC) && $apiKey->getProjectId() === $project->getId()) { $authorization->setDefaultStatus(false); } From 180ac93871f5a3df89c557652d2c2857b11e290a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 16 Feb 2026 17:48:26 +0100 Subject: [PATCH 11/15] Fix accoutn keys permissions --- app/controllers/shared/api.php | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 1b9726ded5..1596607446 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -30,6 +30,7 @@ use Utopia\Database\Document; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Authorization\Input; +use Utopia\Database\Validator\Roles; use Utopia\Http\Http; use Utopia\System\System; use Utopia\Telemetry\Adapter as Telemetry; @@ -247,11 +248,37 @@ Http::init() } } + $queueForAudits->setUser($user); + } + + // Apply permission + if ($apiKey->getType() === API_KEY_ORGANIZATION) { $authorization->addRole(Role::team($team->getId())->toString()); $authorization->addRole(Role::team($team->getId(), 'owner')->toString()); $authorization->addRole(Role::member($team->getId())->toString()); + } elseif ($apiKey->getType() === API_KEY_ACCOUNT) { + $authorization->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::users()->toString()); - $queueForAudits->setUser($user); + if ($user->getAttribute('emailVerification', false) && $user->getAttribute('phoneVerification', false)) { + $authorization->addRole(Role::user($user->getId(), Roles::DIMENSION_VERIFIED)->toString()); + $authorization->addRole(Role::users(Roles::DIMENSION_VERIFIED)->toString()); + } else { + $authorization->addRole(Role::user($user->getId(), Roles::DIMENSION_UNVERIFIED)->toString()); + $authorization->addRole(Role::users(Roles::DIMENSION_UNVERIFIED)->toString()); + } + + foreach (\array_filter($user->getAttribute('memberships', []), fn ($membership) => !isset($membership['confirm']) || !$membership['confirm']) as $nodeMembership) { + $authorization->addRole(Role::team($nodeMembership['teamId'])->toString()); + $authorization->addRole(Role::member($nodeMembership->getId())->toString()); + foreach (($node['roles'] ?? []) as $nodeRole) { + $authorization->addRole(Role::team($nodeMembership['teamId'], $nodeRole)->toString()); + } + } + + foreach ($user->getAttribute('labels', []) as $nodeLabel) { + $authorization->addRole('label:' . $nodeLabel); + } } } // Admin User Authentication elseif (($project->getId() === 'console' && !$team->isEmpty() && !$user->isEmpty()) || ($project->getId() !== 'console' && !$user->isEmpty() && $mode === APP_MODE_ADMIN)) { From 343bed9b9dbdf22c092565f38f1b7c46d24897cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 16 Feb 2026 17:59:44 +0100 Subject: [PATCH 12/15] PR review fixes --- app/controllers/shared/api.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 1596607446..1c0c7a89dc 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -255,7 +255,6 @@ Http::init() if ($apiKey->getType() === API_KEY_ORGANIZATION) { $authorization->addRole(Role::team($team->getId())->toString()); $authorization->addRole(Role::team($team->getId(), 'owner')->toString()); - $authorization->addRole(Role::member($team->getId())->toString()); } elseif ($apiKey->getType() === API_KEY_ACCOUNT) { $authorization->addRole(Role::user($user->getId())->toString()); $authorization->addRole(Role::users()->toString()); @@ -268,7 +267,7 @@ Http::init() $authorization->addRole(Role::users(Roles::DIMENSION_UNVERIFIED)->toString()); } - foreach (\array_filter($user->getAttribute('memberships', []), fn ($membership) => !isset($membership['confirm']) || !$membership['confirm']) as $nodeMembership) { + foreach (\array_filter($user->getAttribute('memberships', []), fn ($membership) => ($membership['confirm'] ?? false) === true) as $nodeMembership) { $authorization->addRole(Role::team($nodeMembership['teamId'])->toString()); $authorization->addRole(Role::member($nodeMembership->getId())->toString()); foreach (($node['roles'] ?? []) as $nodeRole) { From 9572201863369060cf6bbf86d7148ea6b605c2f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 16 Feb 2026 18:08:52 +0100 Subject: [PATCH 13/15] AI review fixes --- app/controllers/shared/api.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 1c0c7a89dc..2c0c61332c 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -259,7 +259,7 @@ Http::init() $authorization->addRole(Role::user($user->getId())->toString()); $authorization->addRole(Role::users()->toString()); - if ($user->getAttribute('emailVerification', false) && $user->getAttribute('phoneVerification', false)) { + if ($user->getAttribute('emailVerification', false) || $user->getAttribute('phoneVerification', false)) { $authorization->addRole(Role::user($user->getId(), Roles::DIMENSION_VERIFIED)->toString()); $authorization->addRole(Role::users(Roles::DIMENSION_VERIFIED)->toString()); } else { @@ -270,7 +270,7 @@ Http::init() foreach (\array_filter($user->getAttribute('memberships', []), fn ($membership) => ($membership['confirm'] ?? false) === true) as $nodeMembership) { $authorization->addRole(Role::team($nodeMembership['teamId'])->toString()); $authorization->addRole(Role::member($nodeMembership->getId())->toString()); - foreach (($node['roles'] ?? []) as $nodeRole) { + foreach (($nodeMembership['roles'] ?? []) as $nodeRole) { $authorization->addRole(Role::team($nodeMembership['teamId'], $nodeRole)->toString()); } } From d8bf3acaeda62846be2f39e8d3e7103b45d33a2b Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:00:23 +0000 Subject: [PATCH 14/15] test: add e2e tests for listing string type attributes Co-Authored-By: Claude Opus 4.6 --- .../Legacy/DatabasesStringTypesTest.php | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php b/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php index 8b7886b864..d1ea6ae921 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php @@ -514,6 +514,63 @@ class DatabasesStringTypesTest extends Scope /** * @depends testCreateLongtextAttribute */ + public function testListStringTypeAttributes(array $data): array + { + $databaseId = $data['databaseId']; + $collectionId = $data['collectionId']; + + // Wait for attributes to be created + sleep(2); + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + $attributes = $response['body']['attributes']; + $types = array_column($attributes, 'type'); + + $this->assertContains('varchar', $types); + $this->assertContains('text', $types); + $this->assertContains('mediumtext', $types); + $this->assertContains('longtext', $types); + + return $data; + } + + /** + * @depends testListStringTypeAttributes + */ + public function testGetCollectionWithStringTypeAttributes(array $data): array + { + $databaseId = $data['databaseId']; + $collectionId = $data['collectionId']; + + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + + $attributes = $response['body']['attributes']; + $types = array_column($attributes, 'type'); + + $this->assertContains('varchar', $types); + $this->assertContains('text', $types); + $this->assertContains('mediumtext', $types); + $this->assertContains('longtext', $types); + + return $data; + } + + /** + * @depends testGetCollectionWithStringTypeAttributes + */ public function testUpdateVarcharAttribute(array $data): array { $this->markTestSkipped('Skipped until utopia-php/database updateAttribute supports VARCHAR type'); From 953c0cd4b4b34ff89db705944da26ac23a110d57 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:29:56 +0000 Subject: [PATCH 15/15] fix: add missing string type models to AttributeList and Collection Co-Authored-By: Claude Opus 4.6 --- src/Appwrite/Utopia/Response/Model/AttributeList.php | 4 ++++ src/Appwrite/Utopia/Response/Model/Collection.php | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/Appwrite/Utopia/Response/Model/AttributeList.php b/src/Appwrite/Utopia/Response/Model/AttributeList.php index 6b9a7365bd..0b17a655ab 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeList.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeList.php @@ -30,6 +30,10 @@ class AttributeList extends Model Response::MODEL_ATTRIBUTE_POINT, Response::MODEL_ATTRIBUTE_LINE, Response::MODEL_ATTRIBUTE_POLYGON, + Response::MODEL_ATTRIBUTE_VARCHAR, + Response::MODEL_ATTRIBUTE_TEXT, + Response::MODEL_ATTRIBUTE_MEDIUMTEXT, + Response::MODEL_ATTRIBUTE_LONGTEXT, Response::MODEL_ATTRIBUTE_STRING // needs to be last, since its condition would dominate any other string attribute ], 'description' => 'List of attributes.', diff --git a/src/Appwrite/Utopia/Response/Model/Collection.php b/src/Appwrite/Utopia/Response/Model/Collection.php index 407db3aea9..4ab7de8e4d 100644 --- a/src/Appwrite/Utopia/Response/Model/Collection.php +++ b/src/Appwrite/Utopia/Response/Model/Collection.php @@ -73,6 +73,10 @@ class Collection extends Model Response::MODEL_ATTRIBUTE_POINT, Response::MODEL_ATTRIBUTE_LINE, Response::MODEL_ATTRIBUTE_POLYGON, + Response::MODEL_ATTRIBUTE_VARCHAR, + Response::MODEL_ATTRIBUTE_TEXT, + Response::MODEL_ATTRIBUTE_MEDIUMTEXT, + Response::MODEL_ATTRIBUTE_LONGTEXT, Response::MODEL_ATTRIBUTE_STRING, // needs to be last, since its condition would dominate any other string attribute ], 'description' => 'Collection attributes.',