From 43f75d082e4871ffeb5e86e651b5009bb4fce607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 26 Aug 2025 15:15:10 +0200 Subject: [PATCH 01/25] Add support for select queries --- .../Database/Validator/Queries/Base.php | 67 ++++++++++++------- .../Validator/Queries/Deployments.php | 5 ++ 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php index 1c5dec44dd..60c9e03c70 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php @@ -11,6 +11,7 @@ use Utopia\Database\Validator\Query\Filter; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\Query\Order; +use Utopia\Database\Validator\Query\Select; class Base extends Queries { @@ -40,6 +41,7 @@ class Base extends Queries $allowedAttributesLookup[$attribute] = true; } + $allAttributes = []; $attributes = []; foreach ($collection['attributes'] as $attribute) { $key = $attribute['$id']; @@ -47,34 +49,44 @@ class Base extends Queries if (!isset($allowedAttributesLookup[$key])) { continue; } - - $attributes[] = new Document([ + + $attributeDocument = new Document([ 'key' => $key, 'type' => $attribute['type'], 'array' => $attribute['array'], ]); - } - $attributes[] = new Document([ - 'key' => '$id', - 'type' => Database::VAR_STRING, - 'array' => false, - ]); - $attributes[] = new Document([ - 'key' => '$createdAt', - 'type' => Database::VAR_DATETIME, - 'array' => false, - ]); - $attributes[] = new Document([ - 'key' => '$updatedAt', - 'type' => Database::VAR_DATETIME, - 'array' => false, - ]); - $attributes[] = new Document([ - 'key' => '$sequence', - 'type' => Database::VAR_INTEGER, - 'array' => false, - ]); + $attributes[] = $attributeDocument; + $allAttributes[] = $attributeDocument; + } + + $internalAttributes = [ + new Document([ + 'key' => '$id', + 'type' => Database::VAR_STRING, + 'array' => false, + ]), + new Document([ + 'key' => '$createdAt', + 'type' => Database::VAR_DATETIME, + 'array' => false, + ]), + new Document([ + 'key' => '$updatedAt', + 'type' => Database::VAR_DATETIME, + 'array' => false, + ]), + new Document([ + 'key' => '$sequence', + 'type' => Database::VAR_INTEGER, + 'array' => false, + ]) + ]; + + foreach($internalAttributes as $attribute) { + $attributes[] = $attribute; + $allAttributes[] = $attribute; + } $validators = [ new Limit(), @@ -83,7 +95,16 @@ class Base extends Queries new Filter($attributes, APP_DATABASE_QUERY_MAX_VALUES), new Order($attributes), ]; + + if($this->isSelectQueryAllowed()) { + $validators[] = new Select($allAttributes); + } parent::__construct($validators); } + + public function isSelectQueryAllowed(): bool + { + return true; + } } diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Deployments.php b/src/Appwrite/Utopia/Database/Validator/Queries/Deployments.php index 73631ecfb8..0d5565c968 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Deployments.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Deployments.php @@ -22,4 +22,9 @@ class Deployments extends Base { parent::__construct('deployments', self::ALLOWED_ATTRIBUTES); } + + public function isSelectQueryAllowed(): bool + { + return true; + } } From a413235f8d1fb51ffd5dccc3a6e12476db751c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 26 Aug 2025 15:29:11 +0200 Subject: [PATCH 02/25] add new tests --- .../Utopia/Database/Validator/Queries/Base.php | 14 +++++++------- .../Database/Validator/Queries/Deployments.php | 2 +- .../Functions/FunctionsCustomServerTest.php | 12 ++++++++++++ tests/e2e/Services/Sites/SitesCustomServerTest.php | 12 ++++++++++++ 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php index 60c9e03c70..34d00e426b 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php @@ -49,7 +49,7 @@ class Base extends Queries if (!isset($allowedAttributesLookup[$key])) { continue; } - + $attributeDocument = new Document([ 'key' => $key, 'type' => $attribute['type'], @@ -59,7 +59,7 @@ class Base extends Queries $attributes[] = $attributeDocument; $allAttributes[] = $attributeDocument; } - + $internalAttributes = [ new Document([ 'key' => '$id', @@ -82,8 +82,8 @@ class Base extends Queries 'array' => false, ]) ]; - - foreach($internalAttributes as $attribute) { + + foreach ($internalAttributes as $attribute) { $attributes[] = $attribute; $allAttributes[] = $attribute; } @@ -95,14 +95,14 @@ class Base extends Queries new Filter($attributes, APP_DATABASE_QUERY_MAX_VALUES), new Order($attributes), ]; - - if($this->isSelectQueryAllowed()) { + + if ($this->isSelectQueryAllowed()) { $validators[] = new Select($allAttributes); } parent::__construct($validators); } - + public function isSelectQueryAllowed(): bool { return true; diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Deployments.php b/src/Appwrite/Utopia/Database/Validator/Queries/Deployments.php index 0d5565c968..6348e69cd9 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Deployments.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Deployments.php @@ -22,7 +22,7 @@ class Deployments extends Base { parent::__construct('deployments', self::ALLOWED_ATTRIBUTES); } - + public function isSelectQueryAllowed(): bool { return true; diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 1d993c19b7..a4fbc37933 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -721,6 +721,18 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals($deployments['headers']['status-code'], 200); $this->assertCount(1, $deployments['body']['deployments']); + $deployments = $this->listDeployments($functionId, [ + 'queries' => [ + Query::select(['status'])->toString(), + ], + ]); + + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertArrayHasKey('status', $deployments['body']['deployments'][0]); + $this->assertArrayHasKey('status', $deployments['body']['deployments'][1]); + $this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][0]); + $this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][1]); + $deployments = $this->listDeployments($functionId, [ 'queries' => [ Query::offset(1)->toString(), diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index 0e792d6c36..8d1ee2f637 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -1052,6 +1052,18 @@ class SitesCustomServerTest extends Scope $this->assertEquals($deployments['headers']['status-code'], 200); $this->assertCount(1, $deployments['body']['deployments']); + $deployments = $this->listDeployments($siteId, [ + 'queries' => [ + Query::select(['status'])->toString(), + ], + ]); + + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertArrayHasKey('status', $deployments['body']['deployments'][0]); + $this->assertArrayHasKey('status', $deployments['body']['deployments'][1]); + $this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][0]); + $this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][1]); + $deployments = $this->listDeployments($siteId, [ 'queries' => [ Query::offset(1)->toString(), From 2ce0f370dcfb31759926aa4179b59f9c07c6aeac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 26 Aug 2025 15:44:15 +0200 Subject: [PATCH 03/25] PR review changes --- .../Utopia/Database/Validator/Queries/Base.php | 2 +- tests/e2e/Services/Users/UsersBase.php | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php index 34d00e426b..0eae588f80 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php @@ -105,6 +105,6 @@ class Base extends Queries public function isSelectQueryAllowed(): bool { - return true; + return false; } } diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php index 0aa5784930..2cf1e4c65d 100644 --- a/tests/e2e/Services/Users/UsersBase.php +++ b/tests/e2e/Services/Users/UsersBase.php @@ -445,6 +445,19 @@ trait UsersBase $user1 = $response['body']['users'][1]; + // This test ensures that by default, endpoints dont support select queries + // If we add select query to this endpoint, you will need to remove this test + // Please make sure to add it to another place, unless all endpoints support select queries + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::select(['name'])->toString() + ] + ]); + $this->assertEquals($response['headers']['status-code'], 400); + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], From cdd85c0b169a54a40b45161844ef1137706b7e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 26 Aug 2025 15:51:24 +0200 Subject: [PATCH 04/25] PR review changes --- .../Utopia/Database/Validator/Queries/Base.php | 9 ++++----- .../Services/Functions/FunctionsCustomServerTest.php | 12 ++++++++++++ tests/e2e/Services/Sites/SitesCustomServerTest.php | 12 ++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php index 0eae588f80..9d9bbde00b 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php @@ -46,18 +46,17 @@ class Base extends Queries foreach ($collection['attributes'] as $attribute) { $key = $attribute['$id']; - if (!isset($allowedAttributesLookup[$key])) { - continue; - } - $attributeDocument = new Document([ 'key' => $key, 'type' => $attribute['type'], 'array' => $attribute['array'], ]); - $attributes[] = $attributeDocument; $allAttributes[] = $attributeDocument; + + if (isset($allowedAttributesLookup[$key])) { + $attributes[] = $attributeDocument; + } } $internalAttributes = [ diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index a4fbc37933..a67fbc0012 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -733,6 +733,18 @@ class FunctionsCustomServerTest extends Scope $this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][0]); $this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][1]); + // Extra select query check, for attribute not allowed by filter queries + $deployments = $this->listDeployments($functionId, [ + 'queries' => [ + Query::select(['buildLogs'])->toString(), + ], + ]); + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertArrayHasKey('buildLogs', $deployments['body']['deployments'][0]); + $this->assertArrayHasKey('buildLogs', $deployments['body']['deployments'][1]); + $this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][0]); + $this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][1]); + $deployments = $this->listDeployments($functionId, [ 'queries' => [ Query::offset(1)->toString(), diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index 8d1ee2f637..92af8ffac8 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -1064,6 +1064,18 @@ class SitesCustomServerTest extends Scope $this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][0]); $this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][1]); + // Extra select query check, for attribute not allowed by filter queries + $deployments = $this->listDeployments($siteId, [ + 'queries' => [ + Query::select(['buildLogs'])->toString(), + ], + ]); + $this->assertEquals($deployments['headers']['status-code'], 200); + $this->assertArrayHasKey('buildLogs', $deployments['body']['deployments'][0]); + $this->assertArrayHasKey('buildLogs', $deployments['body']['deployments'][1]); + $this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][0]); + $this->assertArrayNotHasKey('sourceSize', $deployments['body']['deployments'][1]); + $deployments = $this->listDeployments($siteId, [ 'queries' => [ Query::offset(1)->toString(), From f2dd03e597f797ab8c29c63127031e8da74c0069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 27 Aug 2025 11:20:14 +0200 Subject: [PATCH 05/25] Add request to filters, to allow deployment model to respect select queries --- app/http.php | 2 +- app/realtime.php | 5 +- src/Appwrite/Platform/Workers/Functions.php | 4 +- src/Appwrite/SDK/Method.php | 5 +- src/Appwrite/Utopia/Response.php | 7 +-- src/Appwrite/Utopia/Response/Model.php | 46 ++++++++++++++++++- .../Response/Model/AttributeRelationship.php | 3 +- .../Utopia/Response/Model/ColumnIndex.php | 3 +- .../Response/Model/ColumnRelationship.php | 3 +- .../Utopia/Response/Model/Deployment.php | 8 ++++ .../Utopia/Response/Model/Document.php | 7 +-- .../Utopia/Response/Model/Execution.php | 3 +- .../Utopia/Response/Model/Migration.php | 3 +- .../Utopia/Response/Model/Project.php | 3 +- .../Utopia/Response/Model/ResourceToken.php | 3 +- src/Appwrite/Utopia/Response/Model/Row.php | 7 +-- src/Appwrite/Utopia/Response/Model/Table.php | 3 +- src/Appwrite/Utopia/Response/Model/Team.php | 3 +- src/Appwrite/Utopia/Response/Model/User.php | 3 +- .../Utopia/Response/Model/Variable.php | 3 +- tests/unit/GraphQL/BuilderTest.php | 5 +- tests/unit/Utopia/ResponseTest.php | 5 +- 22 files changed, 106 insertions(+), 28 deletions(-) diff --git a/app/http.php b/app/http.php index 30f4013821..1949afbfff 100644 --- a/app/http.php +++ b/app/http.php @@ -426,7 +426,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool App::setResource('swooleResponse', fn () => $swooleResponse); $request = new Request($swooleRequest); - $response = new Response($swooleResponse); + $response = new Response($swooleResponse, $request); if (Files::isFileLoaded($request->getURI())) { $time = (60 * 60 * 24 * 365 * 2); // 45 days cache diff --git a/app/realtime.php b/app/realtime.php index bb0d4da78c..12559dd186 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -504,7 +504,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $register, $stats, &$realtime, $logError) { $app = new App('UTC'); $request = new Request($request); - $response = new Response(new SwooleResponse()); + $response = new Response(new SwooleResponse(), $request); Console::info("Connection open (user: {$connection})"); @@ -625,7 +625,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $server->onMessage(function (int $connection, string $message) use ($server, $register, $realtime, $containerId) { try { - $response = new Response(new SwooleResponse()); + $request = new Request(new SwooleRequest()); + $response = new Response(new SwooleResponse(), $request); $projectId = $realtime->connections[$connection]['projectId']; $database = getConsoleDB(); diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index 864d6451b8..3ef9b8703e 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -8,9 +8,11 @@ use Appwrite\Event\Func; use Appwrite\Event\Realtime; use Appwrite\Event\StatsUsage; use Appwrite\Event\Webhook; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response\Model\Execution; use Exception; use Executor\Executor; +use Swoole\Http\Request as SwooleRequest; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -610,7 +612,7 @@ class Functions extends Action $execution = $dbForProject->updateDocument('executions', $executionId, $execution); $executionModel = new Execution(); - $realtimeExecution = $executionModel->filter(new Document($execution->getArrayCopy())); + $realtimeExecution = $executionModel->filter(new Document($execution->getArrayCopy()), new Request(new SwooleRequest())); $realtimeExecution = $realtimeExecution->getArrayCopy(\array_keys($executionModel->getRules())); $queueForEvents diff --git a/src/Appwrite/SDK/Method.php b/src/Appwrite/SDK/Method.php index 77e3e1f1af..42c94e58ff 100644 --- a/src/Appwrite/SDK/Method.php +++ b/src/Appwrite/SDK/Method.php @@ -3,7 +3,9 @@ namespace Appwrite\SDK; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; +use Swoole\Http\Request as HttpRequest; use Swoole\Http\Response as HttpResponse; class Method @@ -101,7 +103,8 @@ class Method protected function validateResponseModel(string|array $responseModel): void { - $response = new Response(new HttpResponse()); + $request = new Request(new HttpRequest()); + $response = new Response(new HttpResponse(), $request); if (!\is_array($responseModel)) { $responseModel = [$responseModel]; diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index cec275869a..92156654cd 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -402,9 +402,10 @@ class Response extends SwooleResponse /** * Response constructor. * - * @param float $time + * @param SwooleHTTPResponse $response Native response to be passed to parent constructor + * @param Request $request Relevant request object, useful for some response filters */ - public function __construct(SwooleHTTPResponse $response) + public function __construct(SwooleHTTPResponse $response, protected Request $request) { $this // General @@ -719,7 +720,7 @@ class Response extends SwooleResponse $model = $this->getModel($model); $output = []; - $data = $model->filter($data); + $data = $model->filter($data, $this->request); if ($model->isAny()) { $this->payload = $data->getArrayCopy(); diff --git a/src/Appwrite/Utopia/Response/Model.php b/src/Appwrite/Utopia/Response/Model.php index 962da4834c..95f26b3ca2 100644 --- a/src/Appwrite/Utopia/Response/Model.php +++ b/src/Appwrite/Utopia/Response/Model.php @@ -2,7 +2,9 @@ namespace Appwrite\Utopia\Response; +use Appwrite\Utopia\Request; use Utopia\Database\Document; +use Utopia\Database\Query; abstract class Model { @@ -44,10 +46,12 @@ abstract class Model /** * Filter Document Structure + * @param Document $document Document to apply filter on + * @param Request $request Relevant request object, useful for select query filtering * * @return Document */ - public function filter(Document $document): Document + public function filter(Document $document, Request $request): Document { return $document; } @@ -186,4 +190,44 @@ abstract class Model { return $this->public; } + + /** + * Apply Select Queries + * + * Helper method to respect request select queries, + * to prevent default rule values from being applied on not-selected attributes + * + * @param Document $document + * @param Request $request + * @return void + */ + public function applySelectQueries(Document $document, Request $request): void + { + $queries = $request->getParam('queries', []); + + $queries = Query::parseQueries($queries); + $selectQueries = Query::groupByType($queries)['selections'] ?? []; + + // No select queries means no filtering out + if (empty($selectQueries)) { + return; + } + + $attributes = []; + foreach ($selectQueries as $query) { + foreach ($query->getValues() as $attribute) { + $attributes[] = $attribute; + } + } + + foreach ($this->getRules() as $ruleName => $rule) { + if (\str_starts_with($ruleName, '$')) { + continue; + } + + if (!\in_array($ruleName, $attributes)) { + $this->removeRule($ruleName); + } + } + } } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeRelationship.php b/src/Appwrite/Utopia/Response/Model/AttributeRelationship.php index d88fbd1530..b4abb0d554 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeRelationship.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeRelationship.php @@ -2,6 +2,7 @@ namespace Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Database\Document; @@ -80,7 +81,7 @@ class AttributeRelationship extends Attribute * * @return Document */ - public function filter(Document $document): Document + public function filter(Document $document, Request $request): Document { $options = $document->getAttribute('options'); if (!\is_null($options)) { diff --git a/src/Appwrite/Utopia/Response/Model/ColumnIndex.php b/src/Appwrite/Utopia/Response/Model/ColumnIndex.php index 547312d677..b345c96c02 100644 --- a/src/Appwrite/Utopia/Response/Model/ColumnIndex.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnIndex.php @@ -2,6 +2,7 @@ namespace Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; @@ -93,7 +94,7 @@ class ColumnIndex extends Model return Response::MODEL_COLUMN_INDEX; } - public function filter(Document $document): Document + public function filter(Document $document, Request $request): Document { $columns = $document->getAttribute('attributes', []); $document diff --git a/src/Appwrite/Utopia/Response/Model/ColumnRelationship.php b/src/Appwrite/Utopia/Response/Model/ColumnRelationship.php index 877982365b..33e7c1d4db 100644 --- a/src/Appwrite/Utopia/Response/Model/ColumnRelationship.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnRelationship.php @@ -2,6 +2,7 @@ namespace Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Database\Document; @@ -80,7 +81,7 @@ class ColumnRelationship extends Column * * @return Document */ - public function filter(Document $document): Document + public function filter(Document $document, Request $request): Document { $options = $document->getAttribute('options'); if (!\is_null($options)) { diff --git a/src/Appwrite/Utopia/Response/Model/Deployment.php b/src/Appwrite/Utopia/Response/Model/Deployment.php index 55c1589af0..9b26c7ec7e 100644 --- a/src/Appwrite/Utopia/Response/Model/Deployment.php +++ b/src/Appwrite/Utopia/Response/Model/Deployment.php @@ -2,8 +2,10 @@ namespace Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; +use Utopia\Database\Document; class Deployment extends Model { @@ -190,6 +192,12 @@ class Deployment extends Model return 'Deployment'; } + public function filter(Document $document, Request $request): Document + { + $this->applySelectQueries($document, $request); + return $document; + } + /** * Get Type * diff --git a/src/Appwrite/Utopia/Response/Model/Document.php b/src/Appwrite/Utopia/Response/Model/Document.php index 5bad504a63..b2a8239fbd 100644 --- a/src/Appwrite/Utopia/Response/Model/Document.php +++ b/src/Appwrite/Utopia/Response/Model/Document.php @@ -2,6 +2,7 @@ namespace Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Database\Document as DatabaseDocument; @@ -78,7 +79,7 @@ class Document extends Any ]); } - public function filter(DatabaseDocument $document): DatabaseDocument + public function filter(DatabaseDocument $document, Request $request): DatabaseDocument { $document->removeAttribute('$collection'); $document->removeAttribute('$tenant'); @@ -88,11 +89,11 @@ class Document extends Any if (\is_array($attribute)) { foreach ($attribute as $subAttribute) { if ($subAttribute instanceof DatabaseDocument) { - $this->filter($subAttribute); + $this->filter($subAttribute, $request); } } } elseif ($attribute instanceof DatabaseDocument) { - $this->filter($attribute); + $this->filter($attribute, $request); } } diff --git a/src/Appwrite/Utopia/Response/Model/Execution.php b/src/Appwrite/Utopia/Response/Model/Execution.php index 39d2203bf9..58d6f49b76 100644 --- a/src/Appwrite/Utopia/Response/Model/Execution.php +++ b/src/Appwrite/Utopia/Response/Model/Execution.php @@ -2,6 +2,7 @@ namespace Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\DateTime; @@ -156,7 +157,7 @@ class Execution extends Model * * @return Document */ - public function filter(Document $document): Document + public function filter(Document $document, Request $request): Document { $document->removeAttribute('resourceType'); $document->setAttribute('functionId', $document->getAttribute('resourceId', '')); diff --git a/src/Appwrite/Utopia/Response/Model/Migration.php b/src/Appwrite/Utopia/Response/Model/Migration.php index 76e00b3097..85890e69cd 100644 --- a/src/Appwrite/Utopia/Response/Model/Migration.php +++ b/src/Appwrite/Utopia/Response/Model/Migration.php @@ -2,6 +2,7 @@ namespace Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; @@ -109,7 +110,7 @@ class Migration extends Model return Response::MODEL_MIGRATION; } - public function filter(Document $document): Document + public function filter(Document $document, Request $request): Document { $errors = $document->getAttribute('errors', []); if (empty($errors)) { diff --git a/src/Appwrite/Utopia/Response/Model/Project.php b/src/Appwrite/Utopia/Response/Model/Project.php index abe67e7e86..1c1c0bce62 100644 --- a/src/Appwrite/Utopia/Response/Model/Project.php +++ b/src/Appwrite/Utopia/Response/Model/Project.php @@ -3,6 +3,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Auth\Auth; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Config\Config; @@ -340,7 +341,7 @@ class Project extends Model * * @return Document */ - public function filter(Document $document): Document + public function filter(Document $document, Request $request): Document { // SMTP $smtp = $document->getAttribute('smtp', []); diff --git a/src/Appwrite/Utopia/Response/Model/ResourceToken.php b/src/Appwrite/Utopia/Response/Model/ResourceToken.php index 87ab66ab5d..1f6a30aa15 100644 --- a/src/Appwrite/Utopia/Response/Model/ResourceToken.php +++ b/src/Appwrite/Utopia/Response/Model/ResourceToken.php @@ -3,6 +3,7 @@ namespace Appwrite\Utopia\Response\Model; use Ahc\Jwt\JWT; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; @@ -59,7 +60,7 @@ class ResourceToken extends Model ; } - public function filter(Document $document): Document + public function filter(Document $document, Request $request): Document { $expire = $document->getAttribute('expire'); diff --git a/src/Appwrite/Utopia/Response/Model/Row.php b/src/Appwrite/Utopia/Response/Model/Row.php index 77df48ce89..b605143ec7 100644 --- a/src/Appwrite/Utopia/Response/Model/Row.php +++ b/src/Appwrite/Utopia/Response/Model/Row.php @@ -2,6 +2,7 @@ namespace Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Database\Document as DatabaseDocument; @@ -75,7 +76,7 @@ class Row extends Any ]); } - public function filter(DatabaseDocument $document): DatabaseDocument + public function filter(DatabaseDocument $document, Request $request): DatabaseDocument { $document->removeAttribute('$collection'); $document->removeAttribute('$tenant'); @@ -84,11 +85,11 @@ class Row extends Any if (\is_array($column)) { foreach ($column as $subAttribute) { if ($subAttribute instanceof DatabaseDocument) { - $this->filter($subAttribute); + $this->filter($subAttribute, $request); } } } elseif ($column instanceof DatabaseDocument) { - $this->filter($column); + $this->filter($column, $request); } } diff --git a/src/Appwrite/Utopia/Response/Model/Table.php b/src/Appwrite/Utopia/Response/Model/Table.php index 722edcd4cf..6fe6e6d42a 100644 --- a/src/Appwrite/Utopia/Response/Model/Table.php +++ b/src/Appwrite/Utopia/Response/Model/Table.php @@ -2,6 +2,7 @@ namespace Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; @@ -111,7 +112,7 @@ class Table extends Model /** * Process Document before returning it to the client for backwards compatibility! */ - public function filter(Document $document): Document + public function filter(Document $document, Request $request): Document { $columns = $document->getAttribute('attributes', []); if (!empty($columns) && \is_array($columns)) { diff --git a/src/Appwrite/Utopia/Response/Model/Team.php b/src/Appwrite/Utopia/Response/Model/Team.php index d080a82bb1..845603b7bd 100644 --- a/src/Appwrite/Utopia/Response/Model/Team.php +++ b/src/Appwrite/Utopia/Response/Model/Team.php @@ -2,6 +2,7 @@ namespace Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; @@ -55,7 +56,7 @@ class Team extends Model * * @return Document */ - public function filter(Document $document): Document + public function filter(Document $document, Request $request): Document { $prefs = $document->getAttribute('prefs'); if ($prefs instanceof Document) { diff --git a/src/Appwrite/Utopia/Response/Model/User.php b/src/Appwrite/Utopia/Response/Model/User.php index 672b8885a0..e2a971179e 100644 --- a/src/Appwrite/Utopia/Response/Model/User.php +++ b/src/Appwrite/Utopia/Response/Model/User.php @@ -2,6 +2,7 @@ namespace Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; @@ -147,7 +148,7 @@ class User extends Model * * @return string */ - public function filter(Document $document): Document + public function filter(Document $document, Request $request): Document { $prefs = $document->getAttribute('prefs'); if ($prefs instanceof Document) { diff --git a/src/Appwrite/Utopia/Response/Model/Variable.php b/src/Appwrite/Utopia/Response/Model/Variable.php index 22f76e44d4..2a8fbfad72 100644 --- a/src/Appwrite/Utopia/Response/Model/Variable.php +++ b/src/Appwrite/Utopia/Response/Model/Variable.php @@ -2,6 +2,7 @@ namespace Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; @@ -69,7 +70,7 @@ class Variable extends Model * @param Document $document * @return Document */ - public function filter(Document $document): Document + public function filter(Document $document, Request $request): Document { $secret = $document->getAttribute('secret'); if ($secret === true) { diff --git a/tests/unit/GraphQL/BuilderTest.php b/tests/unit/GraphQL/BuilderTest.php index 3dd1bcadc7..54366b093f 100644 --- a/tests/unit/GraphQL/BuilderTest.php +++ b/tests/unit/GraphQL/BuilderTest.php @@ -3,8 +3,10 @@ namespace Tests\Unit\GraphQL; use Appwrite\GraphQL\Types\Mapper; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use PHPUnit\Framework\TestCase; +use Swoole\Http\Request as SwooleRequest; use Swoole\Http\Response as SwooleResponse; class BuilderTest extends TestCase @@ -13,7 +15,8 @@ class BuilderTest extends TestCase public function setUp(): void { - $this->response = new Response(new SwooleResponse()); + $request = new Request(new SwooleRequest()); + $this->response = new Response(new SwooleResponse(), $request); Mapper::init($this->response->getModels()); } diff --git a/tests/unit/Utopia/ResponseTest.php b/tests/unit/Utopia/ResponseTest.php index 452119fafb..58cd485da3 100644 --- a/tests/unit/Utopia/ResponseTest.php +++ b/tests/unit/Utopia/ResponseTest.php @@ -2,9 +2,11 @@ namespace Tests\Unit\Utopia; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Exception; use PHPUnit\Framework\TestCase; +use Swoole\Http\Request as SwooleRequest; use Swoole\Http\Response as SwooleResponse; use Tests\Unit\Utopia\Response\Filters\First; use Tests\Unit\Utopia\Response\Filters\Second; @@ -16,7 +18,8 @@ class ResponseTest extends TestCase public function setUp(): void { - $this->response = new Response(new SwooleResponse()); + $request = new Request(new SwooleRequest()); + $this->response = new Response(new SwooleResponse(), $request); $this->response->setModel(new Single()); $this->response->setModel(new Lists()); $this->response->setModel(new Nested()); From 262be06b0d693f8a8d33c0e001255edc803bad7f Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 29 Aug 2025 18:37:44 +0530 Subject: [PATCH 06/25] chore: improve deprecation warning --- app/controllers/general.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 22954afd96..91ba1477bd 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1065,7 +1065,6 @@ App::init() */ /** @var \Appwrite\SDK\Method $sdk */ $sdk = $route->getLabel('sdk', false); - $deprecationWarning = 'This route is deprecated. See the updated documentation for improved compatibility and migration details.'; $sdkItems = is_array($sdk) ? $sdk : (!empty($sdk) ? [$sdk] : []); if (!empty($sdkItems) && count($sdkItems) > 0) { $allDeprecated = true; @@ -1076,6 +1075,13 @@ App::init() } } if ($allDeprecated) { + $deprecatedMethod = $sdkItems[0]->getDeprecated(); + $replaceWith = $deprecatedMethod->getReplaceWith(); + if ($replaceWith) { + $replaceWith = preg_replace('/\./', '#', $replaceWith, 1); + } + $deprecatedReplaceWithLink = 'https://appwrite.io/docs/references/cloud/server-rest/' . $replaceWith; + $deprecationWarning = 'Route ' . $route->getPath() . ' is deprecated since ' . $deprecatedMethod->getSince() . '. Please use ' . $deprecatedMethod->getReplaceWith() . ' instead. See: ' . $deprecatedReplaceWithLink; $warnings[] = $deprecationWarning; } } From 4ef6c23a67009407b4a664cdb7537319ffc04a8c Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 29 Aug 2025 18:41:46 +0530 Subject: [PATCH 07/25] add quotes --- app/controllers/general.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 91ba1477bd..2138bad97b 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1081,7 +1081,7 @@ App::init() $replaceWith = preg_replace('/\./', '#', $replaceWith, 1); } $deprecatedReplaceWithLink = 'https://appwrite.io/docs/references/cloud/server-rest/' . $replaceWith; - $deprecationWarning = 'Route ' . $route->getPath() . ' is deprecated since ' . $deprecatedMethod->getSince() . '. Please use ' . $deprecatedMethod->getReplaceWith() . ' instead. See: ' . $deprecatedReplaceWithLink; + $deprecationWarning = 'Route ' . $route->getPath() . ' is deprecated since ' . $deprecatedMethod->getSince() . '. Please use `' . $deprecatedMethod->getReplaceWith() . '` instead. See: ' . $deprecatedReplaceWithLink; $warnings[] = $deprecationWarning; } } From 674d928ce24826d4bef3edc7f4e4e186fc907ac0 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Fri, 29 Aug 2025 21:33:53 +0530 Subject: [PATCH 08/25] show method is x-sdk-name is present --- app/controllers/general.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 2138bad97b..4561c78554 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1081,7 +1081,8 @@ App::init() $replaceWith = preg_replace('/\./', '#', $replaceWith, 1); } $deprecatedReplaceWithLink = 'https://appwrite.io/docs/references/cloud/server-rest/' . $replaceWith; - $deprecationWarning = 'Route ' . $route->getPath() . ' is deprecated since ' . $deprecatedMethod->getSince() . '. Please use `' . $deprecatedMethod->getReplaceWith() . '` instead. See: ' . $deprecatedReplaceWithLink; + $isSdkUsed = !empty($request->getHeader('x-sdk-name')) || !empty($request->getHeader('x-sdk-version')); + $deprecationWarning = (!$isSdkUsed ? 'Route ' . $route->getPath() : 'Method `' . $sdkItems[0]->getNamespace() . '.' . $sdkItems[0]->getMethodName() . '`') . ' is deprecated since ' . $deprecatedMethod->getSince() . '. Please use `' . $deprecatedMethod->getReplaceWith() . '` instead. See: ' . $deprecatedReplaceWithLink; $warnings[] = $deprecationWarning; } } From 73f5823746d9e9e747a6aa399cb5fb17c5aa3376 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Sat, 30 Aug 2025 11:27:51 +0530 Subject: [PATCH 09/25] chore: mark Row response model's param with readonly --- app/config/specs/open-api3-1.8.x-client.json | 9 ++++++--- app/config/specs/open-api3-1.8.x-console.json | 9 ++++++--- app/config/specs/open-api3-1.8.x-server.json | 9 ++++++--- app/config/specs/open-api3-latest-client.json | 9 ++++++--- app/config/specs/open-api3-latest-console.json | 9 ++++++--- app/config/specs/open-api3-latest-server.json | 9 ++++++--- app/config/specs/swagger2-1.8.x-client.json | 9 ++++++--- app/config/specs/swagger2-1.8.x-console.json | 9 ++++++--- app/config/specs/swagger2-1.8.x-server.json | 9 ++++++--- app/config/specs/swagger2-latest-client.json | 9 ++++++--- app/config/specs/swagger2-latest-console.json | 9 ++++++--- app/config/specs/swagger2-latest-server.json | 9 ++++++--- src/Appwrite/Utopia/Response/Model/Row.php | 3 +++ 13 files changed, 75 insertions(+), 36 deletions(-) diff --git a/app/config/specs/open-api3-1.8.x-client.json b/app/config/specs/open-api3-1.8.x-client.json index e3fd3bd04a..0841dabbd1 100644 --- a/app/config/specs/open-api3-1.8.x-client.json +++ b/app/config/specs/open-api3-1.8.x-client.json @@ -9948,17 +9948,20 @@ "type": "integer", "description": "Row automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$tableId": { "type": "string", "description": "Table ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index 29c45692ec..31cb8e5105 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -47120,17 +47120,20 @@ "type": "integer", "description": "Row automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$tableId": { "type": "string", "description": "Table ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 60c07da12a..0fe1cf72ae 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -35978,17 +35978,20 @@ "type": "integer", "description": "Row automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$tableId": { "type": "string", "description": "Table ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index e3fd3bd04a..0841dabbd1 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -9948,17 +9948,20 @@ "type": "integer", "description": "Row automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$tableId": { "type": "string", "description": "Table ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 29c45692ec..31cb8e5105 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -47120,17 +47120,20 @@ "type": "integer", "description": "Row automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$tableId": { "type": "string", "description": "Table ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 60c07da12a..0fe1cf72ae 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -35978,17 +35978,20 @@ "type": "integer", "description": "Row automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$tableId": { "type": "string", "description": "Table ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/swagger2-1.8.x-client.json b/app/config/specs/swagger2-1.8.x-client.json index 3a6152b6ad..efeb167a24 100644 --- a/app/config/specs/swagger2-1.8.x-client.json +++ b/app/config/specs/swagger2-1.8.x-client.json @@ -9944,17 +9944,20 @@ "type": "integer", "description": "Row automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$tableId": { "type": "string", "description": "Table ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 5e121e8840..788279b595 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -47166,17 +47166,20 @@ "type": "integer", "description": "Row automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$tableId": { "type": "string", "description": "Table ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index e4ffbbe973..9293ce6928 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -36115,17 +36115,20 @@ "type": "integer", "description": "Row automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$tableId": { "type": "string", "description": "Table ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 3a6152b6ad..efeb167a24 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -9944,17 +9944,20 @@ "type": "integer", "description": "Row automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$tableId": { "type": "string", "description": "Table ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 5e121e8840..788279b595 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -47166,17 +47166,20 @@ "type": "integer", "description": "Row automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$tableId": { "type": "string", "description": "Table ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index e4ffbbe973..9293ce6928 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -36115,17 +36115,20 @@ "type": "integer", "description": "Row automatically incrementing ID.", "x-example": 1, - "format": "int32" + "format": "int32", + "readOnly": true }, "$tableId": { "type": "string", "description": "Table ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$databaseId": { "type": "string", "description": "Database ID.", - "x-example": "5e5ea5c15117e" + "x-example": "5e5ea5c15117e", + "readOnly": true }, "$createdAt": { "type": "string", diff --git a/src/Appwrite/Utopia/Response/Model/Row.php b/src/Appwrite/Utopia/Response/Model/Row.php index 370d3065ba..14a9ec9cda 100644 --- a/src/Appwrite/Utopia/Response/Model/Row.php +++ b/src/Appwrite/Utopia/Response/Model/Row.php @@ -41,18 +41,21 @@ class Row extends Any 'description' => 'Row automatically incrementing ID.', 'default' => 0, 'example' => 1, + 'readOnly' => true, ]) ->addRule('$tableId', [ 'type' => self::TYPE_STRING, 'description' => 'Table ID.', 'default' => '', 'example' => '5e5ea5c15117e', + 'readOnly' => true, ]) ->addRule('$databaseId', [ 'type' => self::TYPE_STRING, 'description' => 'Database ID.', 'default' => '', 'example' => '5e5ea5c15117e', + 'readOnly' => true, ]) ->addRule('$createdAt', [ 'type' => self::TYPE_DATETIME, From 2710282216001a239f9fc2d9b0590f272fa8bfe5 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Sat, 30 Aug 2025 12:25:47 +0530 Subject: [PATCH 10/25] chore: use correct sdk name and platform in link --- app/controllers/general.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 4561c78554..57e1728a1b 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1076,13 +1076,16 @@ App::init() } if ($allDeprecated) { $deprecatedMethod = $sdkItems[0]->getDeprecated(); + $replaceWith = $deprecatedMethod->getReplaceWith(); if ($replaceWith) { $replaceWith = preg_replace('/\./', '#', $replaceWith, 1); } - $deprecatedReplaceWithLink = 'https://appwrite.io/docs/references/cloud/server-rest/' . $replaceWith; - $isSdkUsed = !empty($request->getHeader('x-sdk-name')) || !empty($request->getHeader('x-sdk-version')); - $deprecationWarning = (!$isSdkUsed ? 'Route ' . $route->getPath() : 'Method `' . $sdkItems[0]->getNamespace() . '.' . $sdkItems[0]->getMethodName() . '`') . ' is deprecated since ' . $deprecatedMethod->getSince() . '. Please use `' . $deprecatedMethod->getReplaceWith() . '` instead. See: ' . $deprecatedReplaceWithLink; + $sdkName = $request->getHeader('x-sdk-name', 'rest'); + $sdkPlatform = $request->getHeader('x-sdk-platform', 'server'); + $deprecatedReplaceWithLink = 'https://appwrite.io/docs/references/cloud/' . $sdkPlatform . '-' . strtolower($sdkName) . '/' . $replaceWith; + + $deprecationWarning = (!empty($sdkName) ? 'Route ' . $route->getPath() : 'Method `' . $sdkItems[0]->getNamespace() . '.' . $sdkItems[0]->getMethodName() . '`') . ' is deprecated since ' . $deprecatedMethod->getSince() . '. Please use `' . $deprecatedMethod->getReplaceWith() . '` instead. See: ' . $deprecatedReplaceWithLink; $warnings[] = $deprecationWarning; } } From 4ee0c12cb16685c065caf6c9c6cf9f41de3826e4 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Sat, 30 Aug 2025 12:27:50 +0530 Subject: [PATCH 11/25] chore: fix --- app/controllers/general.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 57e1728a1b..f1f6c52354 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1082,10 +1082,10 @@ App::init() $replaceWith = preg_replace('/\./', '#', $replaceWith, 1); } $sdkName = $request->getHeader('x-sdk-name', 'rest'); - $sdkPlatform = $request->getHeader('x-sdk-platform', 'server'); + $sdkPlatform = !empty($sdkName) ? $request->getHeader('x-sdk-platform', 'server') : 'server'; $deprecatedReplaceWithLink = 'https://appwrite.io/docs/references/cloud/' . $sdkPlatform . '-' . strtolower($sdkName) . '/' . $replaceWith; - $deprecationWarning = (!empty($sdkName) ? 'Route ' . $route->getPath() : 'Method `' . $sdkItems[0]->getNamespace() . '.' . $sdkItems[0]->getMethodName() . '`') . ' is deprecated since ' . $deprecatedMethod->getSince() . '. Please use `' . $deprecatedMethod->getReplaceWith() . '` instead. See: ' . $deprecatedReplaceWithLink; + $deprecationWarning = (empty($sdkName) ? 'Route ' . $route->getPath() : 'Method `' . $sdkItems[0]->getNamespace() . '.' . $sdkItems[0]->getMethodName() . '`') . ' is deprecated since ' . $deprecatedMethod->getSince() . '. Please use `' . $deprecatedMethod->getReplaceWith() . '` instead. See: ' . $deprecatedReplaceWithLink; $warnings[] = $deprecationWarning; } } From c6fb4687a29545d4bffee65f8e8eb87de4a684ea Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Sat, 30 Aug 2025 12:32:57 +0530 Subject: [PATCH 12/25] readability --- app/controllers/general.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index f1f6c52354..a38e9c9591 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1081,11 +1081,22 @@ App::init() if ($replaceWith) { $replaceWith = preg_replace('/\./', '#', $replaceWith, 1); } - $sdkName = $request->getHeader('x-sdk-name', 'rest'); - $sdkPlatform = !empty($sdkName) ? $request->getHeader('x-sdk-platform', 'server') : 'server'; + + $sdkNameHeader = $request->getHeader('x-sdk-name', ''); + $sdkPlatformHeader = $request->getHeader('x-sdk-platform', ''); + + $sdkExists = !empty($sdkNameHeader); + $sdkName = $sdkExists ? $sdkNameHeader : 'rest'; + $sdkPlatform = !empty($sdkPlatformHeader) ? $sdkPlatformHeader : 'server'; + $deprecatedReplaceWithLink = 'https://appwrite.io/docs/references/cloud/' . $sdkPlatform . '-' . strtolower($sdkName) . '/' . $replaceWith; - $deprecationWarning = (empty($sdkName) ? 'Route ' . $route->getPath() : 'Method `' . $sdkItems[0]->getNamespace() . '.' . $sdkItems[0]->getMethodName() . '`') . ' is deprecated since ' . $deprecatedMethod->getSince() . '. Please use `' . $deprecatedMethod->getReplaceWith() . '` instead. See: ' . $deprecatedReplaceWithLink; + $deprecationWarning = ( + !$sdkExists + ? 'Route ' . $route->getPath() + : 'Method `' . $sdkItems[0]->getNamespace() . '.' . $sdkItems[0]->getMethodName() . '`' + ) . ' is deprecated since ' . $deprecatedMethod->getSince() . '. Please use `' . $deprecatedMethod->getReplaceWith() . '` instead. See: ' . $deprecatedReplaceWithLink; + $warnings[] = $deprecationWarning; } } From 16d731b748a63ca196aab6325b65b1148fa938f2 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Sat, 30 Aug 2025 12:34:27 +0530 Subject: [PATCH 13/25] lower --- app/controllers/general.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index a38e9c9591..dfa682d33d 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1082,14 +1082,14 @@ App::init() $replaceWith = preg_replace('/\./', '#', $replaceWith, 1); } - $sdkNameHeader = $request->getHeader('x-sdk-name', ''); - $sdkPlatformHeader = $request->getHeader('x-sdk-platform', ''); + $sdkNameHeader = strtolower($request->getHeader('x-sdk-name', '')); + $sdkPlatformHeader = strtolower($request->getHeader('x-sdk-platform', '')); $sdkExists = !empty($sdkNameHeader); $sdkName = $sdkExists ? $sdkNameHeader : 'rest'; $sdkPlatform = !empty($sdkPlatformHeader) ? $sdkPlatformHeader : 'server'; - $deprecatedReplaceWithLink = 'https://appwrite.io/docs/references/cloud/' . $sdkPlatform . '-' . strtolower($sdkName) . '/' . $replaceWith; + $deprecatedReplaceWithLink = 'https://appwrite.io/docs/references/cloud/' . $sdkPlatform . '-' . $sdkName . '/' . $replaceWith; $deprecationWarning = ( !$sdkExists From bc18f9d8c7e9e46c3a1624e1e49b258f6c60d0fe Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 1 Sep 2025 12:29:08 +0530 Subject: [PATCH 14/25] temporary remove warnings --- app/controllers/general.php | 60 ++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index dfa682d33d..4f97183dfe 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1064,42 +1064,42 @@ App::init() * Deprecation Warning */ /** @var \Appwrite\SDK\Method $sdk */ - $sdk = $route->getLabel('sdk', false); - $sdkItems = is_array($sdk) ? $sdk : (!empty($sdk) ? [$sdk] : []); - if (!empty($sdkItems) && count($sdkItems) > 0) { - $allDeprecated = true; - foreach ($sdkItems as $sdkItem) { - if (!$sdkItem->isDeprecated()) { - $allDeprecated = false; - break; - } - } - if ($allDeprecated) { - $deprecatedMethod = $sdkItems[0]->getDeprecated(); + // $sdk = $route->getLabel('sdk', false); + // $sdkItems = is_array($sdk) ? $sdk : (!empty($sdk) ? [$sdk] : []); + // if (!empty($sdkItems) && count($sdkItems) > 0) { + // $allDeprecated = true; + // foreach ($sdkItems as $sdkItem) { + // if (!$sdkItem->isDeprecated()) { + // $allDeprecated = false; + // break; + // } + // } + // if ($allDeprecated) { + // $deprecatedMethod = $sdkItems[0]->getDeprecated(); - $replaceWith = $deprecatedMethod->getReplaceWith(); - if ($replaceWith) { - $replaceWith = preg_replace('/\./', '#', $replaceWith, 1); - } + // $replaceWith = $deprecatedMethod->getReplaceWith(); + // if ($replaceWith) { + // $replaceWith = preg_replace('/\./', '#', $replaceWith, 1); + // } - $sdkNameHeader = strtolower($request->getHeader('x-sdk-name', '')); - $sdkPlatformHeader = strtolower($request->getHeader('x-sdk-platform', '')); + // $sdkNameHeader = strtolower($request->getHeader('x-sdk-name', '')); + // $sdkPlatformHeader = strtolower($request->getHeader('x-sdk-platform', '')); - $sdkExists = !empty($sdkNameHeader); - $sdkName = $sdkExists ? $sdkNameHeader : 'rest'; - $sdkPlatform = !empty($sdkPlatformHeader) ? $sdkPlatformHeader : 'server'; + // $sdkExists = !empty($sdkNameHeader); + // $sdkName = $sdkExists ? $sdkNameHeader : 'rest'; + // $sdkPlatform = !empty($sdkPlatformHeader) ? $sdkPlatformHeader : 'server'; - $deprecatedReplaceWithLink = 'https://appwrite.io/docs/references/cloud/' . $sdkPlatform . '-' . $sdkName . '/' . $replaceWith; + // $deprecatedReplaceWithLink = 'https://appwrite.io/docs/references/cloud/' . $sdkPlatform . '-' . $sdkName . '/' . $replaceWith; - $deprecationWarning = ( - !$sdkExists - ? 'Route ' . $route->getPath() - : 'Method `' . $sdkItems[0]->getNamespace() . '.' . $sdkItems[0]->getMethodName() . '`' - ) . ' is deprecated since ' . $deprecatedMethod->getSince() . '. Please use `' . $deprecatedMethod->getReplaceWith() . '` instead. See: ' . $deprecatedReplaceWithLink; + // $deprecationWarning = ( + // !$sdkExists + // ? 'Route ' . $route->getPath() + // : 'Method `' . $sdkItems[0]->getNamespace() . '.' . $sdkItems[0]->getMethodName() . '`' + // ) . ' is deprecated since ' . $deprecatedMethod->getSince() . '. Please use `' . $deprecatedMethod->getReplaceWith() . '` instead. See: ' . $deprecatedReplaceWithLink; - $warnings[] = $deprecationWarning; - } - } + // $warnings[] = $deprecationWarning; + // } + // } if (!empty($warnings)) { $response->addHeader('X-Appwrite-Warning', implode(';', $warnings)); From 7600edb102d51ca7e2856e5ac29bea66acff5b45 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 1 Sep 2025 13:03:41 +0530 Subject: [PATCH 15/25] remove --- app/controllers/general.php | 41 ------------------------------------- 1 file changed, 41 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 4f97183dfe..40ce66b574 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1060,47 +1060,6 @@ App::init() $response->addHeader('Access-Control-Allow-Origin', '*'); } - /** - * Deprecation Warning - */ - /** @var \Appwrite\SDK\Method $sdk */ - // $sdk = $route->getLabel('sdk', false); - // $sdkItems = is_array($sdk) ? $sdk : (!empty($sdk) ? [$sdk] : []); - // if (!empty($sdkItems) && count($sdkItems) > 0) { - // $allDeprecated = true; - // foreach ($sdkItems as $sdkItem) { - // if (!$sdkItem->isDeprecated()) { - // $allDeprecated = false; - // break; - // } - // } - // if ($allDeprecated) { - // $deprecatedMethod = $sdkItems[0]->getDeprecated(); - - // $replaceWith = $deprecatedMethod->getReplaceWith(); - // if ($replaceWith) { - // $replaceWith = preg_replace('/\./', '#', $replaceWith, 1); - // } - - // $sdkNameHeader = strtolower($request->getHeader('x-sdk-name', '')); - // $sdkPlatformHeader = strtolower($request->getHeader('x-sdk-platform', '')); - - // $sdkExists = !empty($sdkNameHeader); - // $sdkName = $sdkExists ? $sdkNameHeader : 'rest'; - // $sdkPlatform = !empty($sdkPlatformHeader) ? $sdkPlatformHeader : 'server'; - - // $deprecatedReplaceWithLink = 'https://appwrite.io/docs/references/cloud/' . $sdkPlatform . '-' . $sdkName . '/' . $replaceWith; - - // $deprecationWarning = ( - // !$sdkExists - // ? 'Route ' . $route->getPath() - // : 'Method `' . $sdkItems[0]->getNamespace() . '.' . $sdkItems[0]->getMethodName() . '`' - // ) . ' is deprecated since ' . $deprecatedMethod->getSince() . '. Please use `' . $deprecatedMethod->getReplaceWith() . '` instead. See: ' . $deprecatedReplaceWithLink; - - // $warnings[] = $deprecationWarning; - // } - // } - if (!empty($warnings)) { $response->addHeader('X-Appwrite-Warning', implode(';', $warnings)); } From a20f71c511becb2132b499de69af1b982c6dc721 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 1 Sep 2025 13:08:17 +0530 Subject: [PATCH 16/25] chore: update exception thrown when svg sanitization fails --- app/config/errors.php | 5 +++++ app/controllers/api/avatars.php | 2 +- src/Appwrite/Extend/Exception.php | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/config/errors.php b/app/config/errors.php index 23df60f4ba..c4617f1cde 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -435,6 +435,11 @@ return [ 'description' => 'The requested favicon could not be found.', 'code' => 404, ], + Exception::AVATAR_SVG_SANITIZATION_FAILED => [ + 'name' => Exception::AVATAR_SVG_SANITIZATION_FAILED, + 'description' => 'SVG sanitization failed.', + 'code' => 400, + ], /** Storage */ Exception::STORAGE_FILE_ALREADY_EXISTS => [ diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 785324739b..90364d997e 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -474,7 +474,7 @@ App::get('/v1/avatars/favicon') $sanitizer->minify(true); $cleanSvg = $sanitizer->sanitize($data); if ($cleanSvg === false) { - throw new \Exception('SVG sanitization failed'); + throw new Exception(Exception::AVATAR_SVG_SANITIZATION_FAILED); } $response ->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 8eded2dbe0..9849352e56 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -134,6 +134,7 @@ class Exception extends \Exception public const AVATAR_IMAGE_NOT_FOUND = 'avatar_image_not_found'; public const AVATAR_REMOTE_URL_FAILED = 'avatar_remote_url_failed'; public const AVATAR_ICON_NOT_FOUND = 'avatar_icon_not_found'; + public const AVATAR_SVG_SANITIZATION_FAILED = 'avatar_svg_sanitization_failed'; /** Storage */ public const STORAGE_FILE_ALREADY_EXISTS = 'storage_file_already_exists'; From 97366e7fbd4de055e833e5bd9758d259165c6ef6 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 1 Sep 2025 19:39:13 +1200 Subject: [PATCH 17/25] Allow null for targets/topics/users --- app/controllers/api/messaging.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index abc1f9bc52..d22c5cb2c2 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -3011,7 +3011,7 @@ App::post('/v1/messaging/messages/email') ->inject('project') ->inject('queueForMessaging') ->inject('response') - ->action(function (string $messageId, string $subject, string $content, array $topics, array $users, array $targets, array $cc, array $bcc, array $attachments, bool $draft, bool $html, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForPlatform, Document $project, Messaging $queueForMessaging, Response $response) { + ->action(function (string $messageId, string $subject, string $content, ?array $topics, ?array $users, ?array $targets, ?array $cc, ?array $bcc, ?array $attachments, bool $draft, bool $html, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForPlatform, Document $project, Messaging $queueForMessaging, Response $response) { $messageId = $messageId == 'unique()' ? ID::unique() : $messageId; @@ -3184,7 +3184,7 @@ App::post('/v1/messaging/messages/sms') ->inject('project') ->inject('queueForMessaging') ->inject('response') - ->action(function (string $messageId, string $content, array $topics, array $users, array $targets, bool $draft, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForPlatform, Document $project, Messaging $queueForMessaging, Response $response) { + ->action(function (string $messageId, string $content, ?array $topics, ?array $users, ?array $targets, bool $draft, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForPlatform, Document $project, Messaging $queueForMessaging, Response $response) { $messageId = $messageId == 'unique()' ? ID::unique() : $messageId; @@ -3319,7 +3319,7 @@ App::post('/v1/messaging/messages/push') ->inject('project') ->inject('queueForMessaging') ->inject('response') - ->action(function (string $messageId, string $title, string $body, array $topics, array $users, array $targets, ?array $data, string $action, string $image, string $icon, string $sound, string $color, string $tag, int $badge, bool $draft, ?string $scheduledAt, bool $contentAvailable, bool $critical, string $priority, Event $queueForEvents, Database $dbForProject, Database $dbForPlatform, Document $project, Messaging $queueForMessaging, Response $response) { + ->action(function (string $messageId, string $title, string $body, ?array $topics, ?array $users, ?array $targets, ?array $data, string $action, string $image, string $icon, string $sound, string $color, string $tag, int $badge, bool $draft, ?string $scheduledAt, bool $contentAvailable, bool $critical, string $priority, Event $queueForEvents, Database $dbForProject, Database $dbForPlatform, Document $project, Messaging $queueForMessaging, Response $response) { $messageId = $messageId == 'unique()' ? ID::unique() : $messageId; From 364eacc3e2784a3a7cee8f5daa6928b92b461682 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 1 Sep 2025 20:17:49 +1200 Subject: [PATCH 18/25] Remove dump --- .../Databases/Http/Databases/Collections/Documents/Update.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 556d14219f..8382bdd5e9 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -231,8 +231,6 @@ class Update extends Action ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); - \var_dump($newDocument); - try { $document = $dbForProject->withRequestTimestamp( $requestTimestamp, From fb8270733e59d09b0aaf881ef5cc0f4dd1758c01 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 1 Sep 2025 22:09:30 +1200 Subject: [PATCH 19/25] Allow running tests with specific response format --- .github/workflows/tests.yml | 28 ++++++++++++++++++++++++---- tests/e2e/Client.php | 14 ++++++++++++++ tests/e2e/Scopes/Scope.php | 6 ++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 164599f911..cebdc02163 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,15 @@ env: IMAGE: appwrite-dev CACHE_KEY: appwrite-dev-${{ github.event.pull_request.head.sha }} -on: [ pull_request ] +on: + pull_request: + workflow_dispatch: + inputs: + response_format: + description: 'Response format version to test (e.g., 1.5.0, 1.4.0)' + required: false + type: string + default: '' jobs: check_database_changes: @@ -100,7 +108,10 @@ jobs: run: docker compose exec -T appwrite vars - name: Run Unit Tests - run: docker compose exec appwrite test /usr/src/code/tests/unit + run: | + docker compose exec \ + -e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \ + appwrite test /usr/src/code/tests/unit e2e_general_test: name: E2E General Test @@ -132,7 +143,10 @@ jobs: done - name: Run General Tests - run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/General --debug + run: | + docker compose exec -T \ + -e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \ + appwrite test /usr/src/code/tests/e2e/General --debug - name: Failure Logs if: failure() @@ -208,6 +222,7 @@ jobs: docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ + -e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \ appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys,screenshots - name: Failure Logs @@ -296,6 +311,7 @@ jobs: docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ + -e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \ appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys,screenshots - name: Failure Logs @@ -337,6 +353,7 @@ jobs: docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ + -e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \ appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys - name: Failure Logs @@ -392,6 +409,7 @@ jobs: docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ + -e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \ appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys - name: Failure Logs @@ -434,6 +452,7 @@ jobs: docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ + -e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \ appwrite test /usr/src/code/tests/e2e/Services/Sites --debug --group=screenshots - name: Failure Logs @@ -490,6 +509,7 @@ jobs: docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ + -e _APP_E2E_RESPONSE_FORMAT="${{ github.event.inputs.response_format }}" \ appwrite test /usr/src/code/tests/e2e/Services/Sites --debug --group=screenshots - name: Failure Logs @@ -498,4 +518,4 @@ jobs: echo "=== Appwrite Worker Builds Logs ===" docker compose logs appwrite-worker-builds echo "=== OpenRuntimes Executor Logs ===" - docker compose logs openruntimes-executor + docker compose logs openruntimes-executor \ No newline at end of file diff --git a/tests/e2e/Client.php b/tests/e2e/Client.php index e411b68454..6b81713654 100644 --- a/tests/e2e/Client.php +++ b/tests/e2e/Client.php @@ -108,6 +108,20 @@ class Client return $this; } + /** + * Set Response Format + * + * @param string $value + * + * @return self $this + */ + public function setResponseFormat(string $value): self + { + $this->addHeader('X-Appwrite-Response-Format', $value); + + return $this; + } + /** * @param bool $status true * @return self $this diff --git a/tests/e2e/Scopes/Scope.php b/tests/e2e/Scopes/Scope.php index 2dbeae961e..c967ab8bb4 100644 --- a/tests/e2e/Scopes/Scope.php +++ b/tests/e2e/Scopes/Scope.php @@ -7,6 +7,7 @@ use Appwrite\Tests\Retryable; use PHPUnit\Framework\TestCase; use Tests\E2E\Client; use Utopia\Database\Helpers\ID; +use Utopia\System\System; abstract class Scope extends TestCase { @@ -23,6 +24,11 @@ abstract class Scope extends TestCase { $this->client = new Client(); $this->client->setEndpoint($this->endpoint); + + $format = System::getEnv('_APP_E2E_RESPONSE_FORMAT'); + if (!empty($format)) { + $this->client->setResponseFormat($format); + } } protected function tearDown(): void From 6e5fe9c33ff61a008635771a15182e23366f1fe9 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Mon, 1 Sep 2025 22:41:30 +1200 Subject: [PATCH 20/25] Check verison --- tests/e2e/Scopes/Scope.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/e2e/Scopes/Scope.php b/tests/e2e/Scopes/Scope.php index c967ab8bb4..5b7f1a8771 100644 --- a/tests/e2e/Scopes/Scope.php +++ b/tests/e2e/Scopes/Scope.php @@ -27,6 +27,12 @@ abstract class Scope extends TestCase $format = System::getEnv('_APP_E2E_RESPONSE_FORMAT'); if (!empty($format)) { + if ( + !\preg_match('/^\d+\.\d+\.\d+$/', $format) || + !\version_compare($format, APP_VERSION_STABLE, '<=') + ) { + throw new \Exception('E2E response format must be ' . APP_VERSION_STABLE . ' or lower.'); + } $this->client->setResponseFormat($format); } } From 48c54bf5e82e5a9556ffaed8930e761377734ff7 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Mon, 1 Sep 2025 15:46:38 +0100 Subject: [PATCH 21/25] chore: make webhooks publisher overridable --- app/controllers/shared/api.php | 6 +++--- app/init/resources.php | 17 ++++++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 7742aac18f..6dcb99b56f 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -29,7 +29,6 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; -use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Publisher; use Utopia\System\System; use Utopia\Telemetry\Adapter as Telemetry; @@ -416,6 +415,7 @@ App::init() ->inject('user') ->inject('publisher') ->inject('publisherFunctions') + ->inject('publisherWebhooks') ->inject('queueForEvents') ->inject('queueForMessaging') ->inject('queueForAudits') @@ -431,7 +431,7 @@ App::init() ->inject('plan') ->inject('devKey') ->inject('telemetry') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, BrokerPool $publisherFunctions, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry) use ($usageDatabaseListener, $eventDatabaseListener) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Publisher $publisherFunctions, Publisher $publisherWebhooks, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry) use ($usageDatabaseListener, $eventDatabaseListener) { $route = $utopia->getRoute(); @@ -544,7 +544,7 @@ App::init() // from overwriting the events that are supposed to be triggered in the shutdown hook. $queueForEventsClone = new Event($publisher); $queueForFunctions = new Func($publisherFunctions); - $queueForWebhooks = new Webhook($publisher); + $queueForWebhooks = new Webhook($publisherWebhooks); $queueForRealtime = new Realtime(); $dbForProject diff --git a/app/init/resources.php b/app/init/resources.php index 380087cf43..d4f0433447 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -84,25 +84,28 @@ App::setResource('localeCodes', function () { App::setResource('publisher', function (Group $pools) { return new BrokerPool(publisher: $pools->get('publisher')); }, ['pools']); -App::setResource('publisherDatabases', function (BrokerPool $publisher) { +App::setResource('publisherDatabases', function (Publisher $publisher) { return $publisher; }, ['publisher']); -App::setResource('publisherFunctions', function (BrokerPool $publisher) { +App::setResource('publisherFunctions', function (Publisher $publisher) { return $publisher; }, ['publisher']); -App::setResource('publisherMigrations', function (BrokerPool $publisher) { +App::setResource('publisherMigrations', function (Publisher $publisher) { return $publisher; }, ['publisher']); -App::setResource('publisherStatsUsage', function (BrokerPool $publisher) { +App::setResource('publisherStatsUsage', function (Publisher $publisher) { return $publisher; }, ['publisher']); -App::setResource('publisherMails', function (BrokerPool $publisher) { +App::setResource('publisherMails', function (Publisher $publisher) { return $publisher; }, ['publisher']); -App::setResource('publisherDeletes', function (BrokerPool $publisher) { +App::setResource('publisherDeletes', function (Publisher $publisher) { return $publisher; }, ['publisher']); -App::setResource('publisherMessaging', function (BrokerPool $publisher) { +App::setResource('publisherMessaging', function (Publisher $publisher) { + return $publisher; +}, ['publisher']); +App::setResource('publisherWebhooks', function (Publisher $publisher) { return $publisher; }, ['publisher']); App::setResource('queueForMessaging', function (Publisher $publisher) { From 6b8a4a03c36ef8fc95ce0add8efaa6dc1865f24a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 2 Sep 2025 11:11:31 +0200 Subject: [PATCH 22/25] Simplify PR --- app/http.php | 2 +- app/realtime.php | 5 +-- .../Platform/Modules/Compute/Base.php | 43 ++++++++++++++++++ src/Appwrite/Platform/Workers/Functions.php | 4 +- src/Appwrite/SDK/Method.php | 5 +-- src/Appwrite/Utopia/Response.php | 5 +-- src/Appwrite/Utopia/Response/Model.php | 45 +------------------ .../Response/Model/AttributeRelationship.php | 3 +- .../Utopia/Response/Model/ColumnIndex.php | 3 +- .../Response/Model/ColumnRelationship.php | 3 +- .../Utopia/Response/Model/Deployment.php | 3 +- .../Utopia/Response/Model/Execution.php | 3 +- .../Utopia/Response/Model/Migration.php | 3 +- .../Utopia/Response/Model/Project.php | 3 +- .../Utopia/Response/Model/ResourceToken.php | 3 +- src/Appwrite/Utopia/Response/Model/Table.php | 3 +- src/Appwrite/Utopia/Response/Model/Team.php | 3 +- src/Appwrite/Utopia/Response/Model/User.php | 3 +- .../Utopia/Response/Model/Variable.php | 3 +- 19 files changed, 63 insertions(+), 82 deletions(-) diff --git a/app/http.php b/app/http.php index 1949afbfff..30f4013821 100644 --- a/app/http.php +++ b/app/http.php @@ -426,7 +426,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool App::setResource('swooleResponse', fn () => $swooleResponse); $request = new Request($swooleRequest); - $response = new Response($swooleResponse, $request); + $response = new Response($swooleResponse); if (Files::isFileLoaded($request->getURI())) { $time = (60 * 60 * 24 * 365 * 2); // 45 days cache diff --git a/app/realtime.php b/app/realtime.php index 12559dd186..bb0d4da78c 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -504,7 +504,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $register, $stats, &$realtime, $logError) { $app = new App('UTC'); $request = new Request($request); - $response = new Response(new SwooleResponse(), $request); + $response = new Response(new SwooleResponse()); Console::info("Connection open (user: {$connection})"); @@ -625,8 +625,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $server->onMessage(function (int $connection, string $message) use ($server, $register, $realtime, $containerId) { try { - $request = new Request(new SwooleRequest()); - $response = new Response(new SwooleResponse(), $request); + $response = new Response(new SwooleResponse()); $projectId = $realtime->connections[$connection]['projectId']; $database = getConsoleDB(); diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index e58f2b8664..5522dc1765 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -4,12 +4,14 @@ namespace Appwrite\Platform\Modules\Compute; use Appwrite\Event\Build; use Appwrite\Extend\Exception; +use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Platform\Action; use Utopia\Swoole\Request; @@ -19,6 +21,47 @@ use Utopia\VCS\Exception\RepositoryNotFound; class Base extends Action { + /** + * Helper to apply select queries + * + * Method to respect request select queries in response format, + * to prevent default rule values from being applied on not-selected attributes + * + * @param Request $request + * @param Document $document + * @return void + */ + public function applySelectQueries(Request $request, Response $response, Document $document): void + { + $queries = $request->getParam('queries', []); + + $queries = Query::parseQueries($queries); + $selectQueries = Query::groupByType($queries)['selections'] ?? []; + + // No select queries means no filtering out + if (empty($selectQueries)) { + return; + } + + $attributes = []; + foreach ($selectQueries as $query) { + foreach ($query->getValues() as $attribute) { + $attributes[] = $attribute; + } + } + + // TODO: Apply for all models? pass model as param? + foreach ($response->getRules() as $ruleName => $rule) { + if (\str_starts_with($ruleName, '$')) { + continue; + } + + if (!\in_array($ruleName, $attributes)) { + $response->removeRule($ruleName); + } + } + } + public function redeployVcsFunction(Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Build $queueForBuilds, Document $template, GitHub $github, bool $activate, string $referenceType = 'branch', string $reference = ''): Document { $deploymentId = ID::unique(); diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index 6e349f3cce..df1833ad33 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -8,11 +8,9 @@ use Appwrite\Event\Func; use Appwrite\Event\Realtime; use Appwrite\Event\StatsUsage; use Appwrite\Event\Webhook; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response\Model\Execution; use Exception; use Executor\Executor; -use Swoole\Http\Request as SwooleRequest; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -617,7 +615,7 @@ class Functions extends Action $execution = $dbForProject->updateDocument('executions', $executionId, $execution); $executionModel = new Execution(); - $realtimeExecution = $executionModel->filter(new Document($execution->getArrayCopy()), new Request(new SwooleRequest())); + $realtimeExecution = $executionModel->filter(new Document($execution->getArrayCopy())); $realtimeExecution = $realtimeExecution->getArrayCopy(\array_keys($executionModel->getRules())); $queueForEvents diff --git a/src/Appwrite/SDK/Method.php b/src/Appwrite/SDK/Method.php index 81c6dcc89e..8d11b07198 100644 --- a/src/Appwrite/SDK/Method.php +++ b/src/Appwrite/SDK/Method.php @@ -3,9 +3,7 @@ namespace Appwrite\SDK; use Appwrite\SDK\Response as SDKResponse; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Swoole\Http\Request as HttpRequest; use Swoole\Http\Response as HttpResponse; class Method @@ -103,8 +101,7 @@ class Method protected function validateResponseModel(string|array $responseModel): void { - $request = new Request(new HttpRequest()); - $response = new Response(new HttpResponse(), $request); + $response = new Response(new HttpResponse()); if (!\is_array($responseModel)) { $responseModel = [$responseModel]; diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 92156654cd..e0ca38587d 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -403,9 +403,8 @@ class Response extends SwooleResponse * Response constructor. * * @param SwooleHTTPResponse $response Native response to be passed to parent constructor - * @param Request $request Relevant request object, useful for some response filters */ - public function __construct(SwooleHTTPResponse $response, protected Request $request) + public function __construct(SwooleHTTPResponse $response) { $this // General @@ -720,7 +719,7 @@ class Response extends SwooleResponse $model = $this->getModel($model); $output = []; - $data = $model->filter($data, $this->request); + $data = $model->filter($data); if ($model->isAny()) { $this->payload = $data->getArrayCopy(); diff --git a/src/Appwrite/Utopia/Response/Model.php b/src/Appwrite/Utopia/Response/Model.php index 95f26b3ca2..3ae40bd479 100644 --- a/src/Appwrite/Utopia/Response/Model.php +++ b/src/Appwrite/Utopia/Response/Model.php @@ -2,9 +2,7 @@ namespace Appwrite\Utopia\Response; -use Appwrite\Utopia\Request; use Utopia\Database\Document; -use Utopia\Database\Query; abstract class Model { @@ -47,11 +45,10 @@ abstract class Model /** * Filter Document Structure * @param Document $document Document to apply filter on - * @param Request $request Relevant request object, useful for select query filtering * * @return Document */ - public function filter(Document $document, Request $request): Document + public function filter(Document $document): Document { return $document; } @@ -190,44 +187,4 @@ abstract class Model { return $this->public; } - - /** - * Apply Select Queries - * - * Helper method to respect request select queries, - * to prevent default rule values from being applied on not-selected attributes - * - * @param Document $document - * @param Request $request - * @return void - */ - public function applySelectQueries(Document $document, Request $request): void - { - $queries = $request->getParam('queries', []); - - $queries = Query::parseQueries($queries); - $selectQueries = Query::groupByType($queries)['selections'] ?? []; - - // No select queries means no filtering out - if (empty($selectQueries)) { - return; - } - - $attributes = []; - foreach ($selectQueries as $query) { - foreach ($query->getValues() as $attribute) { - $attributes[] = $attribute; - } - } - - foreach ($this->getRules() as $ruleName => $rule) { - if (\str_starts_with($ruleName, '$')) { - continue; - } - - if (!\in_array($ruleName, $attributes)) { - $this->removeRule($ruleName); - } - } - } } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeRelationship.php b/src/Appwrite/Utopia/Response/Model/AttributeRelationship.php index b4abb0d554..d88fbd1530 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeRelationship.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeRelationship.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Response\Model; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Database\Document; @@ -81,7 +80,7 @@ class AttributeRelationship extends Attribute * * @return Document */ - public function filter(Document $document, Request $request): Document + public function filter(Document $document): Document { $options = $document->getAttribute('options'); if (!\is_null($options)) { diff --git a/src/Appwrite/Utopia/Response/Model/ColumnIndex.php b/src/Appwrite/Utopia/Response/Model/ColumnIndex.php index b345c96c02..547312d677 100644 --- a/src/Appwrite/Utopia/Response/Model/ColumnIndex.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnIndex.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Response\Model; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; @@ -94,7 +93,7 @@ class ColumnIndex extends Model return Response::MODEL_COLUMN_INDEX; } - public function filter(Document $document, Request $request): Document + public function filter(Document $document): Document { $columns = $document->getAttribute('attributes', []); $document diff --git a/src/Appwrite/Utopia/Response/Model/ColumnRelationship.php b/src/Appwrite/Utopia/Response/Model/ColumnRelationship.php index 33e7c1d4db..877982365b 100644 --- a/src/Appwrite/Utopia/Response/Model/ColumnRelationship.php +++ b/src/Appwrite/Utopia/Response/Model/ColumnRelationship.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Response\Model; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Database\Document; @@ -81,7 +80,7 @@ class ColumnRelationship extends Column * * @return Document */ - public function filter(Document $document, Request $request): Document + public function filter(Document $document): Document { $options = $document->getAttribute('options'); if (!\is_null($options)) { diff --git a/src/Appwrite/Utopia/Response/Model/Deployment.php b/src/Appwrite/Utopia/Response/Model/Deployment.php index 9b26c7ec7e..26194ad08d 100644 --- a/src/Appwrite/Utopia/Response/Model/Deployment.php +++ b/src/Appwrite/Utopia/Response/Model/Deployment.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Response\Model; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; @@ -192,7 +191,7 @@ class Deployment extends Model return 'Deployment'; } - public function filter(Document $document, Request $request): Document + public function filter(Document $document): Document { $this->applySelectQueries($document, $request); return $document; diff --git a/src/Appwrite/Utopia/Response/Model/Execution.php b/src/Appwrite/Utopia/Response/Model/Execution.php index 58d6f49b76..39d2203bf9 100644 --- a/src/Appwrite/Utopia/Response/Model/Execution.php +++ b/src/Appwrite/Utopia/Response/Model/Execution.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Response\Model; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\DateTime; @@ -157,7 +156,7 @@ class Execution extends Model * * @return Document */ - public function filter(Document $document, Request $request): Document + public function filter(Document $document): Document { $document->removeAttribute('resourceType'); $document->setAttribute('functionId', $document->getAttribute('resourceId', '')); diff --git a/src/Appwrite/Utopia/Response/Model/Migration.php b/src/Appwrite/Utopia/Response/Model/Migration.php index 85890e69cd..76e00b3097 100644 --- a/src/Appwrite/Utopia/Response/Model/Migration.php +++ b/src/Appwrite/Utopia/Response/Model/Migration.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Response\Model; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; @@ -110,7 +109,7 @@ class Migration extends Model return Response::MODEL_MIGRATION; } - public function filter(Document $document, Request $request): Document + public function filter(Document $document): Document { $errors = $document->getAttribute('errors', []); if (empty($errors)) { diff --git a/src/Appwrite/Utopia/Response/Model/Project.php b/src/Appwrite/Utopia/Response/Model/Project.php index 1c1c0bce62..abe67e7e86 100644 --- a/src/Appwrite/Utopia/Response/Model/Project.php +++ b/src/Appwrite/Utopia/Response/Model/Project.php @@ -3,7 +3,6 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Auth\Auth; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Config\Config; @@ -341,7 +340,7 @@ class Project extends Model * * @return Document */ - public function filter(Document $document, Request $request): Document + public function filter(Document $document): Document { // SMTP $smtp = $document->getAttribute('smtp', []); diff --git a/src/Appwrite/Utopia/Response/Model/ResourceToken.php b/src/Appwrite/Utopia/Response/Model/ResourceToken.php index 1f6a30aa15..87ab66ab5d 100644 --- a/src/Appwrite/Utopia/Response/Model/ResourceToken.php +++ b/src/Appwrite/Utopia/Response/Model/ResourceToken.php @@ -3,7 +3,6 @@ namespace Appwrite\Utopia\Response\Model; use Ahc\Jwt\JWT; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; @@ -60,7 +59,7 @@ class ResourceToken extends Model ; } - public function filter(Document $document, Request $request): Document + public function filter(Document $document): Document { $expire = $document->getAttribute('expire'); diff --git a/src/Appwrite/Utopia/Response/Model/Table.php b/src/Appwrite/Utopia/Response/Model/Table.php index 6fe6e6d42a..722edcd4cf 100644 --- a/src/Appwrite/Utopia/Response/Model/Table.php +++ b/src/Appwrite/Utopia/Response/Model/Table.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Response\Model; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; @@ -112,7 +111,7 @@ class Table extends Model /** * Process Document before returning it to the client for backwards compatibility! */ - public function filter(Document $document, Request $request): Document + public function filter(Document $document): Document { $columns = $document->getAttribute('attributes', []); if (!empty($columns) && \is_array($columns)) { diff --git a/src/Appwrite/Utopia/Response/Model/Team.php b/src/Appwrite/Utopia/Response/Model/Team.php index 845603b7bd..d080a82bb1 100644 --- a/src/Appwrite/Utopia/Response/Model/Team.php +++ b/src/Appwrite/Utopia/Response/Model/Team.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Response\Model; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; @@ -56,7 +55,7 @@ class Team extends Model * * @return Document */ - public function filter(Document $document, Request $request): Document + public function filter(Document $document): Document { $prefs = $document->getAttribute('prefs'); if ($prefs instanceof Document) { diff --git a/src/Appwrite/Utopia/Response/Model/User.php b/src/Appwrite/Utopia/Response/Model/User.php index e2a971179e..672b8885a0 100644 --- a/src/Appwrite/Utopia/Response/Model/User.php +++ b/src/Appwrite/Utopia/Response/Model/User.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Response\Model; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; @@ -148,7 +147,7 @@ class User extends Model * * @return string */ - public function filter(Document $document, Request $request): Document + public function filter(Document $document): Document { $prefs = $document->getAttribute('prefs'); if ($prefs instanceof Document) { diff --git a/src/Appwrite/Utopia/Response/Model/Variable.php b/src/Appwrite/Utopia/Response/Model/Variable.php index 2a8fbfad72..22f76e44d4 100644 --- a/src/Appwrite/Utopia/Response/Model/Variable.php +++ b/src/Appwrite/Utopia/Response/Model/Variable.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Response\Model; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Database\Document; @@ -70,7 +69,7 @@ class Variable extends Model * @param Document $document * @return Document */ - public function filter(Document $document, Request $request): Document + public function filter(Document $document): Document { $secret = $document->getAttribute('secret'); if ($secret === true) { From b1c1acf36b1330b69af240aa9d75c9ff4d143476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 2 Sep 2025 11:16:47 +0200 Subject: [PATCH 23/25] Finish refactor for internal select queries --- src/Appwrite/Platform/Modules/Compute/Base.php | 8 ++++---- .../Modules/Functions/Http/Deployments/XList.php | 7 ++++++- .../Modules/Sites/Http/Deployments/XList.php | 16 +++++++++++++--- src/Appwrite/Utopia/Response/Model.php | 2 +- .../Utopia/Response/Model/Deployment.php | 7 ------- src/Appwrite/Utopia/Response/Model/Document.php | 7 +++---- src/Appwrite/Utopia/Response/Model/Row.php | 7 +++---- tests/unit/GraphQL/BuilderTest.php | 5 +---- tests/unit/Utopia/ResponseTest.php | 5 +---- 9 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index 5522dc1765..d499f4b4aa 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -31,7 +31,7 @@ class Base extends Action * @param Document $document * @return void */ - public function applySelectQueries(Request $request, Response $response, Document $document): void + public function applySelectQueries(Request $request, Response $response, string $model): void { $queries = $request->getParam('queries', []); @@ -50,14 +50,14 @@ class Base extends Action } } - // TODO: Apply for all models? pass model as param? - foreach ($response->getRules() as $ruleName => $rule) { + $responseModel = $response->getModel($model); + foreach ($responseModel->getRules() as $ruleName => $rule) { if (\str_starts_with($ruleName, '$')) { continue; } if (!\in_array($ruleName, $attributes)) { - $response->removeRule($ruleName); + $responseModel->removeRule($ruleName); } } } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/XList.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/XList.php index 996df299d0..2850c5b279 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/XList.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/XList.php @@ -3,10 +3,12 @@ namespace Appwrite\Platform\Modules\Functions\Http\Deployments; use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Compute\Base; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\Queries\Deployments; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Document; @@ -19,7 +21,7 @@ use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Validator\Text; -class XList extends Action +class XList extends Base { use HTTP; @@ -55,6 +57,7 @@ class XList extends Action ->param('functionId', '', new UID(), 'Function ID.') ->param('queries', [], new Deployments(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Deployments::ALLOWED_ATTRIBUTES), true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) + ->inject('request') ->inject('response') ->inject('dbForProject') ->callback($this->action(...)); @@ -64,6 +67,7 @@ class XList extends Action string $functionId, array $queries, string $search, + Request $request, Response $response, Database $dbForProject ) { @@ -121,6 +125,7 @@ class XList extends Action throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + $this->applySelectQueries($request, $response, Response::MODEL_DEPLOYMENT); $response->dynamic(new Document([ 'deployments' => $results, 'total' => $total, diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/XList.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/XList.php index a1a79ec155..436cd69b52 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/XList.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/XList.php @@ -3,10 +3,12 @@ namespace Appwrite\Platform\Modules\Sites\Http\Deployments; use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Compute\Base; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\Queries\Deployments; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Document; @@ -19,7 +21,7 @@ use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Validator\Text; -class XList extends Action +class XList extends Base { use HTTP; @@ -55,13 +57,20 @@ class XList extends Action ->param('siteId', '', new UID(), 'Site ID.') ->param('queries', [], new Deployments(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Deployments::ALLOWED_ATTRIBUTES), true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) + ->inject('request') ->inject('response') ->inject('dbForProject') ->callback($this->action(...)); } - public function action(string $siteId, array $queries, string $search, Response $response, Database $dbForProject) - { + public function action( + string $siteId, + array $queries, + string $search, + Request $request, + Response $response, + Database $dbForProject + ) { $site = $dbForProject->getDocument('sites', $siteId); if ($site->isEmpty()) { @@ -116,6 +125,7 @@ class XList extends Action throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } + $this->applySelectQueries($request, $response, Response::MODEL_DEPLOYMENT); $response->dynamic(new Document([ 'deployments' => $results, 'total' => $total, diff --git a/src/Appwrite/Utopia/Response/Model.php b/src/Appwrite/Utopia/Response/Model.php index 3ae40bd479..80c2c4d620 100644 --- a/src/Appwrite/Utopia/Response/Model.php +++ b/src/Appwrite/Utopia/Response/Model.php @@ -106,7 +106,7 @@ abstract class Model * @param string $key * @return Model */ - protected function removeRule(string $key): self + public function removeRule(string $key): self { if (isset($this->rules[$key])) { unset($this->rules[$key]); diff --git a/src/Appwrite/Utopia/Response/Model/Deployment.php b/src/Appwrite/Utopia/Response/Model/Deployment.php index 26194ad08d..55c1589af0 100644 --- a/src/Appwrite/Utopia/Response/Model/Deployment.php +++ b/src/Appwrite/Utopia/Response/Model/Deployment.php @@ -4,7 +4,6 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -use Utopia\Database\Document; class Deployment extends Model { @@ -191,12 +190,6 @@ class Deployment extends Model return 'Deployment'; } - public function filter(Document $document): Document - { - $this->applySelectQueries($document, $request); - return $document; - } - /** * Get Type * diff --git a/src/Appwrite/Utopia/Response/Model/Document.php b/src/Appwrite/Utopia/Response/Model/Document.php index b2a8239fbd..5bad504a63 100644 --- a/src/Appwrite/Utopia/Response/Model/Document.php +++ b/src/Appwrite/Utopia/Response/Model/Document.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Response\Model; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Database\Document as DatabaseDocument; @@ -79,7 +78,7 @@ class Document extends Any ]); } - public function filter(DatabaseDocument $document, Request $request): DatabaseDocument + public function filter(DatabaseDocument $document): DatabaseDocument { $document->removeAttribute('$collection'); $document->removeAttribute('$tenant'); @@ -89,11 +88,11 @@ class Document extends Any if (\is_array($attribute)) { foreach ($attribute as $subAttribute) { if ($subAttribute instanceof DatabaseDocument) { - $this->filter($subAttribute, $request); + $this->filter($subAttribute); } } } elseif ($attribute instanceof DatabaseDocument) { - $this->filter($attribute, $request); + $this->filter($attribute); } } diff --git a/src/Appwrite/Utopia/Response/Model/Row.php b/src/Appwrite/Utopia/Response/Model/Row.php index b3a29c50ea..14a9ec9cda 100644 --- a/src/Appwrite/Utopia/Response/Model/Row.php +++ b/src/Appwrite/Utopia/Response/Model/Row.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Response\Model; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Database\Document as DatabaseDocument; @@ -79,7 +78,7 @@ class Row extends Any ]); } - public function filter(DatabaseDocument $document, Request $request): DatabaseDocument + public function filter(DatabaseDocument $document): DatabaseDocument { $document->removeAttribute('$collection'); $document->removeAttribute('$tenant'); @@ -89,11 +88,11 @@ class Row extends Any if (\is_array($column)) { foreach ($column as $subAttribute) { if ($subAttribute instanceof DatabaseDocument) { - $this->filter($subAttribute, $request); + $this->filter($subAttribute); } } } elseif ($column instanceof DatabaseDocument) { - $this->filter($column, $request); + $this->filter($column); } } diff --git a/tests/unit/GraphQL/BuilderTest.php b/tests/unit/GraphQL/BuilderTest.php index 54366b093f..3dd1bcadc7 100644 --- a/tests/unit/GraphQL/BuilderTest.php +++ b/tests/unit/GraphQL/BuilderTest.php @@ -3,10 +3,8 @@ namespace Tests\Unit\GraphQL; use Appwrite\GraphQL\Types\Mapper; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use PHPUnit\Framework\TestCase; -use Swoole\Http\Request as SwooleRequest; use Swoole\Http\Response as SwooleResponse; class BuilderTest extends TestCase @@ -15,8 +13,7 @@ class BuilderTest extends TestCase public function setUp(): void { - $request = new Request(new SwooleRequest()); - $this->response = new Response(new SwooleResponse(), $request); + $this->response = new Response(new SwooleResponse()); Mapper::init($this->response->getModels()); } diff --git a/tests/unit/Utopia/ResponseTest.php b/tests/unit/Utopia/ResponseTest.php index 58cd485da3..452119fafb 100644 --- a/tests/unit/Utopia/ResponseTest.php +++ b/tests/unit/Utopia/ResponseTest.php @@ -2,11 +2,9 @@ namespace Tests\Unit\Utopia; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Exception; use PHPUnit\Framework\TestCase; -use Swoole\Http\Request as SwooleRequest; use Swoole\Http\Response as SwooleResponse; use Tests\Unit\Utopia\Response\Filters\First; use Tests\Unit\Utopia\Response\Filters\Second; @@ -18,8 +16,7 @@ class ResponseTest extends TestCase public function setUp(): void { - $request = new Request(new SwooleRequest()); - $this->response = new Response(new SwooleResponse(), $request); + $this->response = new Response(new SwooleResponse()); $this->response->setModel(new Single()); $this->response->setModel(new Lists()); $this->response->setModel(new Nested()); From d21244ff41851626099b3d5721562aeb01c91b82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 2 Sep 2025 11:42:35 +0200 Subject: [PATCH 24/25] PR review changes --- .../Platform/Modules/Compute/Base.php | 43 ------------------- 1 file changed, 43 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index d499f4b4aa..e58f2b8664 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -4,14 +4,12 @@ namespace Appwrite\Platform\Modules\Compute; use Appwrite\Event\Build; use Appwrite\Extend\Exception; -use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; -use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Platform\Action; use Utopia\Swoole\Request; @@ -21,47 +19,6 @@ use Utopia\VCS\Exception\RepositoryNotFound; class Base extends Action { - /** - * Helper to apply select queries - * - * Method to respect request select queries in response format, - * to prevent default rule values from being applied on not-selected attributes - * - * @param Request $request - * @param Document $document - * @return void - */ - public function applySelectQueries(Request $request, Response $response, string $model): void - { - $queries = $request->getParam('queries', []); - - $queries = Query::parseQueries($queries); - $selectQueries = Query::groupByType($queries)['selections'] ?? []; - - // No select queries means no filtering out - if (empty($selectQueries)) { - return; - } - - $attributes = []; - foreach ($selectQueries as $query) { - foreach ($query->getValues() as $attribute) { - $attributes[] = $attribute; - } - } - - $responseModel = $response->getModel($model); - foreach ($responseModel->getRules() as $ruleName => $rule) { - if (\str_starts_with($ruleName, '$')) { - continue; - } - - if (!\in_array($ruleName, $attributes)) { - $responseModel->removeRule($ruleName); - } - } - } - public function redeployVcsFunction(Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Build $queueForBuilds, Document $template, GitHub $github, bool $activate, string $referenceType = 'branch', string $reference = ''): Document { $deploymentId = ID::unique(); From 14194ec972e9abc32e945d152d7e38c0d34f77b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 2 Sep 2025 11:50:02 +0200 Subject: [PATCH 25/25] Fix missing vendor changes --- src/Appwrite/Platform/Action.php | 43 +++++++++++++++++++ .../Platform/Modules/Compute/Base.php | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Action.php b/src/Appwrite/Platform/Action.php index 056a33b320..5699a67ff2 100644 --- a/src/Appwrite/Platform/Action.php +++ b/src/Appwrite/Platform/Action.php @@ -2,6 +2,8 @@ namespace Appwrite\Platform; +use Appwrite\Utopia\Request; +use Appwrite\Utopia\Response; use Swoole\Coroutine as Co; use Utopia\CLI\Console; use Utopia\Database\Database; @@ -157,4 +159,45 @@ class Action extends UtopiaAction Console::info("[" . DateTime::now() . "] " . $method . ' ' . $type . ' ' . $project->getSequence() . ' ' . $project->getId() . ' ' . $collectionId . ' ' . $log); } } + + + /** + * Helper to apply (request) select queries to response model. + * + * This prevents default values of rules to be presnet for not-selected attributes + * + * @param Request $request + * @param Document $document + * @return void + */ + public function applySelectQueries(Request $request, Response $response, string $model): void + { + $queries = $request->getParam('queries', []); + + $queries = Query::parseQueries($queries); + $selectQueries = Query::groupByType($queries)['selections'] ?? []; + + // No select queries means no filtering out + if (empty($selectQueries)) { + return; + } + + $attributes = []; + foreach ($selectQueries as $query) { + foreach ($query->getValues() as $attribute) { + $attributes[] = $attribute; + } + } + + $responseModel = $response->getModel($model); + foreach ($responseModel->getRules() as $ruleName => $rule) { + if (\str_starts_with($ruleName, '$')) { + continue; + } + + if (!\in_array($ruleName, $attributes)) { + $responseModel->removeRule($ruleName); + } + } + } } diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index e58f2b8664..92805fbaf8 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -4,6 +4,7 @@ namespace Appwrite\Platform\Modules\Compute; use Appwrite\Event\Build; use Appwrite\Extend\Exception; +use Appwrite\Platform\Action; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate; @@ -11,7 +12,6 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; -use Utopia\Platform\Action; use Utopia\Swoole\Request; use Utopia\System\System; use Utopia\VCS\Adapter\Git\GitHub;