diff --git a/app/controllers/general.php b/app/controllers/general.php index dbc07f950b..1f8258b86d 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -830,7 +830,8 @@ App::init() $request->addFilter(new RequestV18()); } if (version_compare($requestFormat, '1.7.0', '<')) { - $request->addFilter(new RequestV19()); + $dbForProject = $getProjectDB($project); + $request->addFilter(new RequestV19($dbForProject, $route)); } } diff --git a/src/Appwrite/Utopia/Request/Filter.php b/src/Appwrite/Utopia/Request/Filter.php index 59346c7e17..8d97424b29 100644 --- a/src/Appwrite/Utopia/Request/Filter.php +++ b/src/Appwrite/Utopia/Request/Filter.php @@ -2,8 +2,20 @@ namespace Appwrite\Utopia\Request; +use Utopia\Database\Database; +use Utopia\Route; + abstract class Filter { + private ?Route $route; + private ?Database $dbForProject; + + public function __construct(Database $dbForProject = null, Route $route = null) + { + $this->route = $route; + $this->dbForProject = $dbForProject; + } + /** * Parse params to another format. * @@ -13,4 +25,15 @@ abstract class Filter * @return array */ abstract public function parse(array $content, string $model): array; + + public function getRoute(): ?Route + { + return $this->route; + } + + public function getDbForProject(): ?Database + { + + return $this->dbForProject; + } } diff --git a/src/Appwrite/Utopia/Request/Filters/V19.php b/src/Appwrite/Utopia/Request/Filters/V19.php index 52d8436499..3060461fe0 100644 --- a/src/Appwrite/Utopia/Request/Filters/V19.php +++ b/src/Appwrite/Utopia/Request/Filters/V19.php @@ -4,7 +4,9 @@ namespace Appwrite\Utopia\Request\Filters; use Appwrite\Query as AppwriteQuery; use Appwrite\Utopia\Request\Filter; +use Utopia\Database\Database; use Utopia\Database\Query as UtopiaQuery; +use Utopia\Database\Validator\Authorization; class V19 extends Filter { @@ -18,27 +20,84 @@ class V19 extends Filter protected function manageSelectQueries(array $content, string $model): array { - if (!str_contains($model, 'databases.')) { + $isDatabaseModel = \str_starts_with($model, 'databases.'); + $isTargetOperation = \in_array($model, ['databases.listDocuments', 'databases.getDocument']); + + if (! $isDatabaseModel || ! $isTargetOperation) { return $content; } - if ($model !== 'databases.listDocuments' && $model !== 'databases.getDocument') { - return $content; - } - - if (!isset($content['queries'])) { + $hasWildcard = false; + if (! isset($content['queries'])) { + $hasWildcard = true; $content['queries'] = [AppwriteQuery::select(['*'])]; - return $content; } $parsed = UtopiaQuery::parseQueries($content['queries']); $selections = UtopiaQuery::groupByType($parsed)['selections'] ?? []; - if (empty($selections)) { - $parsed[] = AppwriteQuery::select(['*']); - $content['queries'] = $parsed; + if (! $hasWildcard) { + // check if any select includes a wildcard as we added one above + $hasWildcard = \array_reduce($selections, fn (bool $carry, $select) => + $carry || \in_array('*', $select->getValues(), true), false); } + if ($hasWildcard && $model === 'databases.listDocuments') { + $relatedKeys = $this->getRelatedCollectionKeys(); + + if (! empty($relatedKeys)) { + $selects = \array_values(\array_unique(\array_merge(['*'], $relatedKeys))); + + // remove previous select queries + $parsed = \array_filter( + $parsed, + fn ($query) => + $query->getMethod() !== UtopiaQuery::TYPE_SELECT + ); + + // add wildcard + relationship(s) selects + $parsed[] = AppwriteQuery::select($selects); + } + } + + $content['queries'] = $parsed; + return $content; } + + private function getRelatedCollectionKeys(): array + { + $route = $this->getRoute(); + $dbForProject = $this->getDbForProject(); + + if ($dbForProject === null || $route === null) { + return []; + } + + $params = $route->getParamsValues(); + $databaseId = $params['databaseId'] ?? ''; + $collectionId = $params['collectionId'] ?? ''; + + if (empty($databaseId) || empty($collectionId)) { + return []; + } + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + + $collection = $dbForProject->getDocument( + 'database_' . $database->getInternalId(), + $collectionId + ); + + $attributes = $collection->getAttribute('attributes', []); + + return \array_values(\array_map( + fn ($attr) => $attr['key'] . '.*', + \array_filter( + $attributes, + fn ($attr) => + ($attr['type'] ?? null) === Database::VAR_RELATIONSHIP + ) + )); + } }