Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/Appwrite/Platform/Action.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,11 @@ protected function foreachDocument(Database $database, string $collection, array
}
}

public function disableSubqueries()
public function disableSubqueries(array $filters = []): void
{
$filters = $this->filters;
if (empty($filters)) {
$filters = $this->filters;
}

foreach ($filters as $filter) {
Database::addFilter(
Expand Down Expand Up @@ -189,6 +191,11 @@ public function applySelectQueries(Request $request, Response $response, string
}
}

// found a wildcard, return!
if (\in_array('*', $attributes)) {
return;
}

$responseModel = $response->getModel($model);
foreach ($responseModel->getRules() as $ruleName => $rule) {
if (\str_starts_with($ruleName, '$')) {
Expand Down
88 changes: 84 additions & 4 deletions src/Appwrite/Platform/Modules/Projects/Http/Projects/XList.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@
namespace Appwrite\Platform\Modules\Projects\Http\Projects;

use Appwrite\Extend\Exception;
use Appwrite\Platform\Action;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Database\Validator\Queries\Projects;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Order;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Query;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Validator;
use Utopia\Validator\Boolean;
Expand All @@ -24,6 +26,10 @@
class XList extends Action
{
use HTTP;

// cached mapping of columns to their subQuery filters
private static ?array $attributeToSubQueryFilters = null;

public static function getName()
{
return 'listProjects';
Expand Down Expand Up @@ -61,12 +67,13 @@ public function __construct()
->param('queries', [], $this->getQueriesValidator(), '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(', ', Projects::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('request')
->inject('response')
->inject('dbForPlatform')
->callback($this->action(...));
}

public function action(array $queries, string $search, bool $includeTotal, Response $response, Database $dbForPlatform)
public function action(array $queries, string $search, bool $includeTotal, Request $request, Response $response, Database $dbForPlatform)
{
try {
$queries = Query::parseQueries($queries);
Expand Down Expand Up @@ -103,16 +110,89 @@ public function action(array $queries, string $search, bool $includeTotal, Respo
$cursor->setValue($cursorDocument);
}

$filterQueries = Query::groupByType($queries)['filters'];
try {
$projects = $dbForPlatform->find('projects', $queries);
$selectQueries = Query::groupByType($queries)['selections'] ?? [];
$filterQueries = Query::groupByType($queries)['filters'];

$projects = $this->find($dbForPlatform, $queries, $selectQueries);
$total = $includeTotal ? $dbForPlatform->count('projects', $filterQueries, APP_LIMIT_COUNT) : 0;
} catch (Order $e) {
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_PROJECT);
$response->dynamic(new Document([
'projects' => $projects,
'total' => $total,
]), Response::MODEL_PROJECT_LIST);
}

// Build mapping of columns to their subQuery filters
private static function getAttributeToSubQueryFilters(): array
{
if (self::$attributeToSubQueryFilters !== null) {
return self::$attributeToSubQueryFilters;
}

self::$attributeToSubQueryFilters = [];

$collections = Config::getParam('collections', []);
$projectAttributes = $collections['platform']['projects']['attributes'] ?? [];

foreach ($projectAttributes as $attribute) {
$attributeId = $attribute['$id'] ?? null;
$filters = $attribute['filters'] ?? [];

if ($attributeId === null || empty($filters)) {
continue;
}

// extract only subQuery filters
$subQueryFilters = \array_filter($filters, function ($filter) {
return \str_starts_with($filter, 'subQuery');
});

if (!empty($subQueryFilters)) {
self::$attributeToSubQueryFilters[$attributeId] = \array_values($subQueryFilters);
}
}

return self::$attributeToSubQueryFilters;
}

private function find(Database $dbForPlatform, array $queries, array $selectQueries): array
{
if (empty($selectQueries)) {
return $dbForPlatform->find('projects', $queries);
}

$selectedAttributes = [];
foreach ($selectQueries as $query) {
foreach ($query->getValues() as $value) {
$selectedAttributes[] = $value;
}
}

if (\in_array('*', $selectedAttributes)) {
return $dbForPlatform->find('projects', $queries);
}

$filtersToSkipMap = [];
$selectedAttributesMap = \array_flip($selectedAttributes);
$attributeToSubQueryFilters = self::getAttributeToSubQueryFilters();

foreach ($attributeToSubQueryFilters as $attributeName => $subQueryFilters) {
if (!isset($selectedAttributesMap[$attributeName])) {
foreach ($subQueryFilters as $filter) {
$filtersToSkipMap[$filter] = true;
}
}
}

$filtersToSkip = \array_keys($filtersToSkipMap);

return empty($filtersToSkip)
? $dbForPlatform->find('projects', $queries)
: $dbForPlatform->skipFilters(fn () => $dbForPlatform->find('projects', $queries), $filtersToSkip);
}
}
5 changes: 5 additions & 0 deletions src/Appwrite/Utopia/Database/Validator/Queries/Projects.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@ public function __construct()
{
parent::__construct('projects', self::ALLOWED_ATTRIBUTES);
}

public function isSelectQueryAllowed(): bool
{
return true;
}
}
42 changes: 36 additions & 6 deletions src/Appwrite/Utopia/Response/Model/Project.php
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,20 @@ public function getType(): string
*/
public function filter(Document $document): Document
{
$this->expandSmtpFields($document);
$this->expandServiceFields($document);
$this->expandAuthFields($document);
$this->expandOAuthProviders($document);

return $document;
}

private function expandSmtpFields(Document $document): void
{
if (!$document->isSet('smtp')) {
return;
}

// SMTP
$smtp = $document->getAttribute('smtp', []);
$document->setAttribute('smtpEnabled', $smtp['enabled'] ?? false);
Expand All @@ -352,8 +366,14 @@ public function filter(Document $document): Document
$document->setAttribute('smtpUsername', $smtp['username'] ?? '');
$document->setAttribute('smtpPassword', $smtp['password'] ?? '');
$document->setAttribute('smtpSecure', $smtp['secure'] ?? '');
}

private function expandServiceFields(Document $document): void
{
if (!$document->isSet('services')) {
return;
}

// Services
$values = $document->getAttribute('services', []);
$services = Config::getParam('services', []);

Expand All @@ -365,8 +385,14 @@ public function filter(Document $document): Document
$value = $values[$key] ?? true;
$document->setAttribute('serviceStatusFor' . ucfirst($key), $value);
}
}

private function expandAuthFields(Document $document): void
{
if (!$document->isSet('auths')) {
return;
}

// Auth
$authValues = $document->getAttribute('auths', []);
$auth = Config::getParam('auth', []);

Expand All @@ -383,13 +409,19 @@ public function filter(Document $document): Document
$document->setAttribute('authMembershipsMfa', $authValues['membershipsMfa'] ?? true);
$document->setAttribute('authInvalidateSessions', $authValues['invalidateSessions'] ?? false);

foreach ($auth as $index => $method) {
foreach ($auth as $method) {
$key = $method['key'];
$value = $authValues[$key] ?? true;
$document->setAttribute('auth' . ucfirst($key), $value);
}
}

private function expandOAuthProviders(Document $document): void
{
if (!$document->isSet('oAuthProviders')) {
return;
}

// OAuth Providers
$providers = Config::getParam('oAuthProviders', []);
$providerValues = $document->getAttribute('oAuthProviders', []);
$projectProviders = [];
Expand All @@ -410,7 +442,5 @@ public function filter(Document $document): Document
}

$document->setAttribute('oAuthProviders', $projectProviders);

return $document;
}
}
Loading