Add request to filters, to allow deployment model to respect select queries

This commit is contained in:
Matej Bačo 2025-08-27 11:20:14 +02:00
parent cdd85c0b16
commit f2dd03e597
22 changed files with 106 additions and 28 deletions

View file

@ -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

View file

@ -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();

View file

@ -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

View file

@ -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];

View file

@ -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();

View file

@ -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);
}
}
}
}

View file

@ -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)) {

View file

@ -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

View file

@ -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)) {

View file

@ -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
*

View file

@ -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);
}
}

View file

@ -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', ''));

View file

@ -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)) {

View file

@ -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', []);

View file

@ -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');

View file

@ -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);
}
}

View file

@ -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)) {

View file

@ -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) {

View file

@ -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) {

View file

@ -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) {

View file

@ -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());
}

View file

@ -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());