mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
205
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7aec766946 | ||
|
|
efc237f4bd | ||
|
|
5aa3696b8b | ||
|
|
0353eb133c | ||
|
|
c68f520a93 | ||
|
|
75ff2dbb88 | ||
|
|
97d4c18df3 | ||
|
|
aecc23d70c | ||
|
|
e081af706f | ||
|
|
6016a5d0c1 | ||
|
|
5a7a54c69c | ||
|
|
89537774e7 | ||
|
|
dd9d7174d5 | ||
|
|
cdbe99672c | ||
|
|
b9127e224c | ||
|
|
24aa66cc2e | ||
|
|
66cd5d73db | ||
|
|
c45803b942 | ||
|
|
4f88a5a781 | ||
|
|
97fcd6971f | ||
|
|
812062268e | ||
|
|
37753bbf8b | ||
|
|
2aeee1cce9 | ||
|
|
d1cef030a0 | ||
|
|
4aaab4254d | ||
|
|
2461bcf15e | ||
|
|
b68edfc583 | ||
|
|
4671fd9cc8 | ||
|
|
7ac0cbfde7 | ||
|
|
3720c0f8de | ||
|
|
edbae1215b | ||
|
|
db4ae4a7b2 | ||
|
|
0051bf5e26 | ||
|
|
b1fadcf65e | ||
|
|
32d4804d96 | ||
|
|
f1dd0a0ac6 | ||
|
|
09ccd755ed | ||
|
|
fbc69333c4 | ||
|
|
e738608006 | ||
|
|
96b1272945 | ||
|
|
3bffbf3363 | ||
|
|
2a6e426853 | ||
|
|
fd8d401d02 | ||
|
|
c522983030 | ||
|
|
377b94a671 | ||
|
|
8e12bc2303 | ||
|
|
f96a1bf8c0 | ||
|
|
c1c0fd6947 | ||
|
|
02726d3072 | ||
|
|
22578be96a | ||
|
|
2169443b8d | ||
|
|
82cce40b17 | ||
|
|
30d62a2497 | ||
|
|
952ce4f0d6 | ||
|
|
3eea414d4c | ||
|
|
7abf035f8e | ||
|
|
0960fa6618 | ||
|
|
7bab35e2e6 | ||
|
|
af8fd5fc6c | ||
|
|
2d36f162cc | ||
|
|
1ef7daebf7 | ||
|
|
f814d94480 | ||
|
|
1dacf0afed | ||
|
|
5c20e8eb56 | ||
|
|
46dcf02b8a | ||
|
|
309831cd24 | ||
|
|
70c81c4986 | ||
|
|
52ab20134f | ||
|
|
c8097ae1d2 | ||
|
|
270233962a | ||
|
|
4b7d206af3 | ||
|
|
6751e186b8 | ||
|
|
496d3c61c2 | ||
|
|
24fea43b8a | ||
|
|
7a4ae03b6f | ||
|
|
31d17fb0c3 | ||
|
|
fcecfa6bfc | ||
|
|
81b003f677 | ||
|
|
20cbdd687e | ||
|
|
7c242703e0 | ||
|
|
fc113fda95 | ||
|
|
e3f9321462 | ||
|
|
f97d272085 | ||
|
|
1a3c4d7b6c | ||
|
|
a770e6c289 | ||
|
|
2633654859 | ||
|
|
c1b9f75d47 | ||
|
|
7fd25eb380 | ||
|
|
0e4dbcca22 | ||
|
|
aa3b6b462d | ||
|
|
a0797868fd | ||
|
|
997aafc646 | ||
|
|
a456ae5acb | ||
|
|
21af384227 | ||
|
|
3574d89012 | ||
|
|
b0e47fbb2c | ||
|
|
1762b9d2ec | ||
|
|
79d8ada95d | ||
|
|
affe208d1a | ||
|
|
0a53c677b1 | ||
|
|
0e709f1f97 | ||
|
|
0c514d5696 | ||
|
|
da45edcd46 | ||
|
|
396b152bcf | ||
|
|
d811fb00d9 | ||
|
|
0520eea383 | ||
|
|
8051b6f64e | ||
|
|
406fd1b94a | ||
|
|
6a70a1958f | ||
|
|
b222f4e4bb | ||
|
|
66babf9dae | ||
|
|
0adacef23f | ||
|
|
bcd69e34ce | ||
|
|
6775b9505a | ||
|
|
4967887797 | ||
|
|
971e1270d7 | ||
|
|
be6c0a63f1 | ||
|
|
5e5f5ef49f | ||
|
|
bafb326ef7 | ||
|
|
f030631e6e | ||
|
|
1b8462d2e2 | ||
|
|
08f0a43465 | ||
|
|
87380a2b01 | ||
|
|
0748a08fdc | ||
|
|
2e4efb5d2f | ||
|
|
730a967104 | ||
|
|
95af73a479 | ||
|
|
d7619f7057 | ||
|
|
1ec142fe07 | ||
|
|
1c73d54153 | ||
|
|
1daef72ca9 | ||
|
|
c5b415af59 | ||
|
|
b1915fa20c | ||
|
|
82032ac60a | ||
|
|
27121a7f1e | ||
|
|
bef969738e | ||
|
|
bea6ccbe92 | ||
|
|
c126dba3b5 | ||
|
|
c72e69371b | ||
|
|
a3c6e45e92 | ||
|
|
db068f31ba | ||
|
|
50673c3223 | ||
|
|
7d5d97e53b | ||
|
|
0b1136c678 | ||
|
|
5d59e17b8c | ||
|
|
7de871c3d9 | ||
|
|
2c1a92951e | ||
|
|
5991b1f966 | ||
|
|
4e24b117f5 | ||
|
|
edaa3764c2 | ||
|
|
7db91c2ab0 | ||
|
|
acd8ff839e | ||
|
|
7f5857b232 | ||
|
|
139474d3fb | ||
|
|
30469128d3 | ||
|
|
64f4e081b5 | ||
|
|
c80ff1b541 | ||
|
|
edcee5167a | ||
|
|
1aea7bd6f2 | ||
|
|
a9233717b6 | ||
|
|
ccb34dc401 | ||
|
|
761a4bb0c7 | ||
|
|
11f0e8f5df | ||
|
|
df26136dd8 | ||
|
|
aab780476e | ||
|
|
1ae2d09e4b | ||
|
|
a6a2fa77df | ||
|
|
a79f46ed78 | ||
|
|
638f7431b1 | ||
|
|
fa3c9a5b76 | ||
|
|
c388fea5d0 | ||
|
|
a407d2ea84 | ||
|
|
da56ef39f8 | ||
|
|
275b9a1186 | ||
|
|
2f514ae55e | ||
|
|
38517b8004 | ||
|
|
8758e2e5a0 | ||
|
|
dacc6a97d0 | ||
|
|
13fc2222f8 | ||
|
|
900d8f248e | ||
|
|
de572c3aa9 | ||
|
|
d4fd5e79d9 | ||
|
|
87247826e0 | ||
|
|
05f4d05cf3 | ||
|
|
d12d6acd62 | ||
|
|
133eac66e7 | ||
|
|
53a6fef69e | ||
|
|
0c4f540325 | ||
|
|
19e22e1da7 | ||
|
|
23c1e4fb79 | ||
|
|
904b2da8ce | ||
|
|
ee129e28f1 | ||
|
|
c50ba30f1b | ||
|
|
265ccdb03a | ||
|
|
3b93e925d4 | ||
|
|
10b38c7c6d | ||
|
|
adf6b02462 | ||
|
|
f47c484d8d | ||
|
|
6e1d2008f8 | ||
|
|
38fb9f2814 | ||
|
|
8d37e843e0 | ||
|
|
975510c6a0 | ||
|
|
a971976587 | ||
|
|
9bee2f5a25 | ||
|
|
21e830146b |
@@ -30,6 +30,7 @@ use Appwrite\Usage\Context;
|
||||
use Appwrite\Utopia\Database\Documents\User;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Identities;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use libphonenumber\NumberParseException;
|
||||
@@ -53,10 +54,7 @@ use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Emails\Email;
|
||||
use Utopia\Emails\Validator\Email as EmailValidator;
|
||||
@@ -513,6 +511,8 @@ Http::post('/v1/account')
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'authenticators' => null,
|
||||
'challenges' => null,
|
||||
'targets' => null,
|
||||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
'accessedAt' => DateTime::now(),
|
||||
'emailCanonical' => $emailMetadata['emailCanonical'],
|
||||
@@ -1234,6 +1234,8 @@ Http::post('/v1/account/sessions/anonymous')
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'authenticators' => null,
|
||||
'challenges' => null,
|
||||
'targets' => null,
|
||||
'search' => $userId,
|
||||
'accessedAt' => DateTime::now(),
|
||||
]);
|
||||
@@ -1852,6 +1854,8 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'authenticators' => null,
|
||||
'challenges' => null,
|
||||
'targets' => null,
|
||||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
'accessedAt' => DateTime::now(),
|
||||
'emailCanonical' => $emailMetadata['emailCanonical'],
|
||||
@@ -2348,6 +2352,8 @@ Http::post('/v1/account/tokens/magic-url')
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'authenticators' => null,
|
||||
'challenges' => null,
|
||||
'targets' => null,
|
||||
'search' => implode(' ', [$userId, $email]),
|
||||
'accessedAt' => DateTime::now(),
|
||||
'emailCanonical' => $emailMetadata['emailCanonical'],
|
||||
@@ -2653,6 +2659,9 @@ Http::post('/v1/account/tokens/email')
|
||||
'sessions' => null,
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'authenticators' => null,
|
||||
'challenges' => null,
|
||||
'targets' => null,
|
||||
'search' => implode(' ', [$userId, $email]),
|
||||
'accessedAt' => DateTime::now(),
|
||||
'emailCanonical' => $emailMetadata['emailCanonical'],
|
||||
@@ -3039,6 +3048,9 @@ Http::post('/v1/account/tokens/phone')
|
||||
'sessions' => null,
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'authenticators' => null,
|
||||
'challenges' => null,
|
||||
'targets' => null,
|
||||
'search' => implode(' ', [$userId, $phone]),
|
||||
'accessedAt' => DateTime::now(),
|
||||
'emailCanonical' => null,
|
||||
@@ -3265,7 +3277,7 @@ Http::get('/v1/account/logs')
|
||||
],
|
||||
contentType: ContentType::JSON,
|
||||
))
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', 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('response')
|
||||
->inject('user')
|
||||
|
||||
@@ -22,6 +22,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Providers;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Subscribers;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Targets;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Topics;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Appwrite\Utopia\Response;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\Audit\Audit;
|
||||
@@ -36,10 +37,7 @@ use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Authorization\Input;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\Roles;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Emails\Validator\Email;
|
||||
@@ -1136,7 +1134,7 @@ Http::get('/v1/messaging/providers/:providerId/logs')
|
||||
]
|
||||
))
|
||||
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', 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('response')
|
||||
->inject('dbForProject')
|
||||
@@ -2441,6 +2439,7 @@ Http::post('/v1/messaging/topics')
|
||||
'$id' => $topicId,
|
||||
'name' => $name,
|
||||
'subscribe' => $subscribe,
|
||||
'targets' => null,
|
||||
]);
|
||||
|
||||
try {
|
||||
@@ -2541,7 +2540,7 @@ Http::get('/v1/messaging/topics/:topicId/logs')
|
||||
]
|
||||
))
|
||||
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID.', false, ['dbForProject'])
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', 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('response')
|
||||
->inject('dbForProject')
|
||||
@@ -2957,7 +2956,7 @@ Http::get('/v1/messaging/subscribers/:subscriberId/logs')
|
||||
]
|
||||
))
|
||||
->param('subscriberId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Subscriber ID.', false, ['dbForProject'])
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', 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('response')
|
||||
->inject('dbForProject')
|
||||
@@ -3771,7 +3770,7 @@ Http::get('/v1/messaging/messages/:messageId/logs')
|
||||
],
|
||||
))
|
||||
->param('messageId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Message ID.', false, ['dbForProject'])
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', 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('response')
|
||||
->inject('dbForProject')
|
||||
|
||||
@@ -20,8 +20,9 @@ use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\QueryContext;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Queries\Documents;
|
||||
use Utopia\Database\Validator\Queries\Queries as DocumentsValidator;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Http;
|
||||
@@ -588,9 +589,11 @@ Http::post('/v1/migrations/csv/exports')
|
||||
// Schemaless databases (DocumentsDB, VectorsDB) allow queries on dynamic fields
|
||||
$isSchemaless = in_array($databaseType, [DATABASE_TYPE_DOCUMENTSDB, DATABASE_TYPE_VECTORSDB]);
|
||||
|
||||
$validator = new Documents(
|
||||
attributes: $collection->getAttribute('attributes', []),
|
||||
indexes: $collection->getAttribute('indexes', []),
|
||||
$context = new QueryContext();
|
||||
$context->add($collection);
|
||||
|
||||
$validator = new DocumentsValidator(
|
||||
$context,
|
||||
idAttributeType: $dbForProject->getAdapter()->getIdAttributeType(),
|
||||
supportForAttributes: !$isSchemaless,
|
||||
);
|
||||
@@ -877,9 +880,11 @@ Http::post('/v1/migrations/json/exports')
|
||||
// Schemaless databases (DocumentsDB, VectorsDB) allow queries on dynamic fields
|
||||
$isSchemaless = in_array($databaseType, [DATABASE_TYPE_DOCUMENTSDB, DATABASE_TYPE_VECTORSDB]);
|
||||
|
||||
$validator = new Documents(
|
||||
attributes: $collection->getAttribute('attributes', []),
|
||||
indexes: $collection->getAttribute('indexes', []),
|
||||
$context = new QueryContext();
|
||||
$context->add($collection);
|
||||
|
||||
$validator = new DocumentsValidator(
|
||||
$context,
|
||||
idAttributeType: $dbForProject->getAdapter()->getIdAttributeType(),
|
||||
supportForAttributes: !$isSchemaless,
|
||||
);
|
||||
|
||||
@@ -24,6 +24,7 @@ use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Identities;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Memberships;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Targets;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Users;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
@@ -55,8 +56,6 @@ use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Emails\Email;
|
||||
use Utopia\Emails\Validator\Email as EmailValidator;
|
||||
@@ -186,6 +185,9 @@ function createUser(Hash $hash, string $userId, ?string $email, ?string $passwor
|
||||
'sessions' => null,
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'authenticators' => null,
|
||||
'challenges' => null,
|
||||
'targets' => null,
|
||||
'search' => implode(' ', [$userId, $email, $phone, $name]),
|
||||
'emailCanonical' => $emailMetadata['emailCanonical'],
|
||||
'emailIsCanonical' => $emailMetadata['emailIsCanonical'],
|
||||
@@ -970,7 +972,7 @@ Http::get('/v1/users/:userId/logs')
|
||||
]
|
||||
))
|
||||
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', 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('response')
|
||||
->inject('dbForProject')
|
||||
|
||||
@@ -25,6 +25,7 @@ use Appwrite\Utopia\Request\Filters\V18 as RequestV18;
|
||||
use Appwrite\Utopia\Request\Filters\V19 as RequestV19;
|
||||
use Appwrite\Utopia\Request\Filters\V20 as RequestV20;
|
||||
use Appwrite\Utopia\Request\Filters\V21 as RequestV21;
|
||||
use Appwrite\Utopia\Request\Filters\V22 as RequestV22;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Filters\V16 as ResponseV16;
|
||||
use Appwrite\Utopia\Response\Filters\V17 as ResponseV17;
|
||||
@@ -892,6 +893,9 @@ Http::init()
|
||||
if (version_compare($requestFormat, '1.9.0', '<')) {
|
||||
$request->addFilter(new RequestV21());
|
||||
}
|
||||
if (version_compare($requestFormat, '1.10.0', '<')) {
|
||||
$request->addFilter(new RequestV22());
|
||||
}
|
||||
}
|
||||
|
||||
$localeParam = (string) $request->getParam('locale', $request->getHeader('x-appwrite-locale', ''));
|
||||
|
||||
@@ -47,7 +47,7 @@ const APP_RESOURCE_TOKEN_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_BUSTER = 4321;
|
||||
const APP_VERSION_STABLE = '1.9.0';
|
||||
const APP_VERSION_STABLE = '1.10.0';
|
||||
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
||||
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
|
||||
|
||||
@@ -69,6 +69,10 @@ Database::addFilter(
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
if (empty($document->getSequence()) || empty($document->getAttribute('databaseInternalId'))) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$attributes = $database->getAuthorization()->skip(fn () => $database->find('attributes', [
|
||||
Query::equal('collectionInternalId', [$document->getSequence()]),
|
||||
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
|
||||
@@ -108,6 +112,10 @@ Database::addFilter(
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
if (empty($document->getSequence()) || empty($document->getAttribute('databaseInternalId'))) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $database->getAuthorization()->skip(fn () => $database
|
||||
->find('indexes', [
|
||||
Query::equal('collectionInternalId', [$document->getSequence()]),
|
||||
@@ -123,6 +131,10 @@ Database::addFilter(
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
if (empty($document->getSequence())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $database->getAuthorization()->skip(fn () => $database
|
||||
->find('platforms', [
|
||||
Query::equal('projectInternalId', [$document->getSequence()]),
|
||||
@@ -137,6 +149,10 @@ Database::addFilter(
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
if (empty($document->getSequence())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $database->getAuthorization()->skip(fn () => $database
|
||||
->find('keys', [
|
||||
Query::equal('resourceType', ['projects']),
|
||||
@@ -152,6 +168,10 @@ Database::addFilter(
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
if (empty($document->getSequence())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $database->getAuthorization()->skip(fn () => $database
|
||||
->find('devKeys', [
|
||||
Query::equal('projectInternalId', [$document->getSequence()]),
|
||||
@@ -166,6 +186,10 @@ Database::addFilter(
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
if (empty($document->getSequence())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $database->getAuthorization()->skip(fn () => $database
|
||||
->find('webhooks', [
|
||||
Query::equal('projectInternalId', [$document->getSequence()]),
|
||||
@@ -180,6 +204,10 @@ Database::addFilter(
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
if (empty($document->getSequence())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $database->getAuthorization()->skip(fn () => $database->find('sessions', [
|
||||
Query::equal('userInternalId', [$document->getSequence()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
@@ -193,6 +221,10 @@ Database::addFilter(
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
if (empty($document->getSequence())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $database->getAuthorization()->skip(fn () => $database
|
||||
->find('tokens', [
|
||||
Query::equal('userInternalId', [$document->getSequence()]),
|
||||
@@ -207,6 +239,10 @@ Database::addFilter(
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
if (empty($document->getSequence())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $database->getAuthorization()->skip(fn () => $database
|
||||
->find('challenges', [
|
||||
Query::equal('userInternalId', [$document->getSequence()]),
|
||||
@@ -221,6 +257,10 @@ Database::addFilter(
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
if (empty($document->getSequence())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $database->getAuthorization()->skip(fn () => $database
|
||||
->find('authenticators', [
|
||||
Query::equal('userInternalId', [$document->getSequence()]),
|
||||
@@ -235,6 +275,10 @@ Database::addFilter(
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
if (empty($document->getSequence())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $database->getAuthorization()->skip(fn () => $database
|
||||
->find('memberships', [
|
||||
Query::equal('userInternalId', [$document->getSequence()]),
|
||||
@@ -249,6 +293,10 @@ Database::addFilter(
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
if (empty($document->getSequence())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$resourceType = match ($document->getCollection()) {
|
||||
'functions' => ['function'],
|
||||
'sites' => ['site'],
|
||||
@@ -335,6 +383,10 @@ Database::addFilter(
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
if (empty($document->getSequence())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $database->getAuthorization()->skip(fn () => $database
|
||||
->find('targets', [
|
||||
Query::equal('userInternalId', [$document->getSequence()]),
|
||||
@@ -349,6 +401,10 @@ Database::addFilter(
|
||||
return;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
if (empty($document->getSequence())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$targetIds = $database->getAuthorization()->skip(fn () => \array_map(
|
||||
fn ($document) => $document->getAttribute('targetInternalId'),
|
||||
$database->find('subscribers', [
|
||||
|
||||
@@ -60,6 +60,7 @@ use Utopia\System\System;
|
||||
use Utopia\Telemetry\Adapter as Telemetry;
|
||||
use Utopia\Validator\URL;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub;
|
||||
|
||||
/**
|
||||
* Register per-request resources on the given container.
|
||||
@@ -950,10 +951,17 @@ return function (Container $container): void {
|
||||
|
||||
$container->set('schema', function ($utopia, $dbForProject, $authorization) {
|
||||
|
||||
// $complexity = function (int $complexity, array $args) {
|
||||
// $queries = Query::parseQueries($args['queries'] ?? []);
|
||||
// $query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null;
|
||||
// $limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT;
|
||||
//
|
||||
// return $complexity * $limit;
|
||||
// };
|
||||
|
||||
$complexity = function (int $complexity, array $args) {
|
||||
$queries = Query::parseQueries($args['queries'] ?? []);
|
||||
$query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null;
|
||||
$limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT;
|
||||
$limit = Query::getLimitQuery($queries, APP_LIMIT_LIST_DEFAULT);
|
||||
|
||||
return $complexity * $limit;
|
||||
};
|
||||
|
||||
+2
-2
@@ -61,7 +61,7 @@
|
||||
"utopia-php/compression": "0.1.*",
|
||||
"utopia-php/config": "1.*",
|
||||
"utopia-php/console": "0.1.*",
|
||||
"utopia-php/database": "5.*",
|
||||
"utopia-php/database": "dev-joins8 as 5.3.19",
|
||||
"utopia-php/detector": "0.2.*",
|
||||
"utopia-php/domains": "1.*",
|
||||
"utopia-php/emails": "0.6.*",
|
||||
@@ -73,7 +73,7 @@
|
||||
"utopia-php/locale": "0.8.*",
|
||||
"utopia-php/logger": "0.6.*",
|
||||
"utopia-php/messaging": "0.22.*",
|
||||
"utopia-php/migration": "1.9.*",
|
||||
"utopia-php/migration": "dev-joins as 1.9.3",
|
||||
"utopia-php/platform": "0.12.*",
|
||||
"utopia-php/pools": "1.*",
|
||||
"utopia-php/span": "1.1.*",
|
||||
|
||||
Generated
+45
-29
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "4fb974e9843f6104e40396e7cad4a833",
|
||||
"content-hash": "58f98e378bd79b6a0ec1868320e5a770",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
@@ -3850,16 +3850,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "5.3.19",
|
||||
"version": "dev-joins8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "72ee1614c37e37c7fdd9d4dc87f1f7cdfa1ca691"
|
||||
"reference": "53c74cbdc8196d1226b899a6fe03061f3df35fe2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/72ee1614c37e37c7fdd9d4dc87f1f7cdfa1ca691",
|
||||
"reference": "72ee1614c37e37c7fdd9d4dc87f1f7cdfa1ca691",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/53c74cbdc8196d1226b899a6fe03061f3df35fe2",
|
||||
"reference": "53c74cbdc8196d1226b899a6fe03061f3df35fe2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3903,9 +3903,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/5.3.19"
|
||||
"source": "https://github.com/utopia-php/database/tree/joins8"
|
||||
},
|
||||
"time": "2026-03-31T15:52:08+00:00"
|
||||
"time": "2026-04-06T14:13:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/detector",
|
||||
@@ -4526,16 +4526,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/migration",
|
||||
"version": "1.9.1",
|
||||
"version": "dev-joins",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/migration.git",
|
||||
"reference": "7a86aeadf182b63a9f4ceba7e137588b31c5d2e2"
|
||||
"reference": "c030a2ac04428e91aa48e95c5561f96f54f1bf5e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/7a86aeadf182b63a9f4ceba7e137588b31c5d2e2",
|
||||
"reference": "7a86aeadf182b63a9f4ceba7e137588b31c5d2e2",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/c030a2ac04428e91aa48e95c5561f96f54f1bf5e",
|
||||
"reference": "c030a2ac04428e91aa48e95c5561f96f54f1bf5e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4544,7 +4544,7 @@
|
||||
"ext-openssl": "*",
|
||||
"halaxa/json-machine": "^1.2",
|
||||
"php": ">=8.1",
|
||||
"utopia-php/database": "5.*",
|
||||
"utopia-php/database": "dev-joins8 as 5.3.1",
|
||||
"utopia-php/dsn": "0.2.*",
|
||||
"utopia-php/storage": "1.0.*"
|
||||
},
|
||||
@@ -4575,9 +4575,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/migration/issues",
|
||||
"source": "https://github.com/utopia-php/migration/tree/1.9.1"
|
||||
"source": "https://github.com/utopia-php/migration/tree/joins"
|
||||
},
|
||||
"time": "2026-03-25T07:05:27+00:00"
|
||||
"time": "2026-04-06T14:16:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/mongo",
|
||||
@@ -5225,16 +5225,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/vcs",
|
||||
"version": "3.1.0",
|
||||
"version": "3.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/vcs.git",
|
||||
"reference": "03b76ad5fd01bc50f809915bca6ff0745ea913af"
|
||||
"reference": "44a84ab52b42fc12f812b4d7331286b519d39db3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/03b76ad5fd01bc50f809915bca6ff0745ea913af",
|
||||
"reference": "03b76ad5fd01bc50f809915bca6ff0745ea913af",
|
||||
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/44a84ab52b42fc12f812b4d7331286b519d39db3",
|
||||
"reference": "44a84ab52b42fc12f812b4d7331286b519d39db3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5268,9 +5268,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/vcs/issues",
|
||||
"source": "https://github.com/utopia-php/vcs/tree/3.1.0"
|
||||
"source": "https://github.com/utopia-php/vcs/tree/3.2.0"
|
||||
},
|
||||
"time": "2026-03-24T08:49:14+00:00"
|
||||
"time": "2026-04-08T16:00:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/websocket",
|
||||
@@ -5448,16 +5448,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "1.17.7",
|
||||
"version": "1.17.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "291471d04c3f0e7b9fcc46668a6255a4c0f2947e"
|
||||
"reference": "b7109e13cec89ed56ad80111973b12646e4eb5f7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/291471d04c3f0e7b9fcc46668a6255a4c0f2947e",
|
||||
"reference": "291471d04c3f0e7b9fcc46668a6255a4c0f2947e",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/b7109e13cec89ed56ad80111973b12646e4eb5f7",
|
||||
"reference": "b7109e13cec89ed56ad80111973b12646e4eb5f7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5493,9 +5493,9 @@
|
||||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/1.17.7"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/1.17.8"
|
||||
},
|
||||
"time": "2026-04-08T08:51:05+00:00"
|
||||
"time": "2026-04-09T05:08:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brianium/paratest",
|
||||
@@ -8426,9 +8426,25 @@
|
||||
"time": "2024-11-07T12:36:22+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
"aliases": [
|
||||
{
|
||||
"package": "utopia-php/database",
|
||||
"version": "dev-joins8",
|
||||
"alias": "5.3.19",
|
||||
"alias_normalized": "5.3.19.0"
|
||||
},
|
||||
{
|
||||
"package": "utopia-php/migration",
|
||||
"version": "dev-joins",
|
||||
"alias": "1.9.3",
|
||||
"alias_normalized": "1.9.3.0"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": {},
|
||||
"stability-flags": {
|
||||
"utopia-php/database": 20,
|
||||
"utopia-php/migration": 20
|
||||
},
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
@@ -8449,5 +8465,5 @@
|
||||
"platform-dev": {
|
||||
"ext-fileinfo": "*"
|
||||
},
|
||||
"plugin-api-version": "2.9.0"
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ As the name implies, `param()` is used to define a request parameter.
|
||||
|
||||
```php
|
||||
App::get('/v1/account/logs')
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
```
|
||||
|
||||
### 6. inject
|
||||
|
||||
@@ -60,7 +60,8 @@ class EventProcessor
|
||||
|
||||
while ($sum >= $limit) {
|
||||
$functions = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->find('functions', [
|
||||
Query::select(['$id', 'events']),
|
||||
Query::select('$id'),
|
||||
Query::select('events'),
|
||||
Query::limit($limit),
|
||||
Query::offset($offset),
|
||||
Query::orderAsc('$sequence'),
|
||||
|
||||
@@ -75,7 +75,7 @@ class Realtime extends MessagingAdapter
|
||||
|
||||
$strings = [];
|
||||
if (empty($queryGroup)) {
|
||||
$strings[] = Query::select(['*'])->toString();
|
||||
$strings[] = Query::select('*')->toString();
|
||||
} else {
|
||||
foreach ($queryGroup as $query) {
|
||||
$strings[] = $query->toString();
|
||||
@@ -376,7 +376,7 @@ class Realtime extends MessagingAdapter
|
||||
}
|
||||
$subscriptions[0]['channels'][] = $channel;
|
||||
if (empty($subscriptions[0]['queries'])) {
|
||||
$subscriptions[0]['queries'] = [Query::select(['*'])];
|
||||
$subscriptions[0]['queries'] = [Query::select('*')];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ abstract class Migration
|
||||
'1.8.0' => 'V23',
|
||||
'1.8.1' => 'V23',
|
||||
'1.9.0' => 'V24',
|
||||
'1.10.0' => 'V24',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -113,6 +113,8 @@ class Create extends Action
|
||||
'enabled' => $enabled,
|
||||
'name' => $name,
|
||||
'search' => \implode(' ', [$collectionId, $name]),
|
||||
'attributes' => null,
|
||||
'indexes' => null,
|
||||
]));
|
||||
} catch (DuplicateException) {
|
||||
throw new Exception($this->getDuplicateException(), params: [$collectionId]);
|
||||
|
||||
+2
-4
@@ -10,6 +10,7 @@ use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Deprecated;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\Audit\Audit;
|
||||
@@ -18,9 +19,6 @@ use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Locale\Locale;
|
||||
@@ -67,7 +65,7 @@ class XList extends Action
|
||||
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
|
||||
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
|
||||
->param('documentId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Document ID.', false, ['dbForProject'])
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('getDatabasesDB')
|
||||
|
||||
+14
@@ -104,6 +104,20 @@ class XList extends Action
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
$joins = Query::getJoinQueries($queries, false);
|
||||
foreach ($joins as $join) {
|
||||
$col = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $join->getCollectionId()));
|
||||
|
||||
if ($col->isEmpty() || (!$col->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
|
||||
throw new Exception($this->getParentNotFoundException(), params: [$join->getCollectionId()]);
|
||||
}
|
||||
|
||||
$collectionTableId = 'database_' . $database->getSequence() . '_collection_' . $col->getSequence();
|
||||
|
||||
$join->setCollectionId($collectionTableId);
|
||||
$dbForProject->addJoinCollectionId($collectionTableId);
|
||||
}
|
||||
|
||||
$dbForDatabases = $getDatabasesDB($database);
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
@@ -9,6 +9,7 @@ use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Deprecated;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use DeviceDetector\DeviceDetector as Detector;
|
||||
use MaxMind\Db\Reader;
|
||||
@@ -18,9 +19,6 @@ use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Locale\Locale;
|
||||
@@ -66,7 +64,7 @@ class XList extends Action
|
||||
))
|
||||
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
|
||||
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
|
||||
@@ -8,6 +8,7 @@ use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Deprecated;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use DeviceDetector\DeviceDetector as Detector;
|
||||
use MaxMind\Db\Reader;
|
||||
@@ -17,9 +18,6 @@ use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Locale\Locale;
|
||||
@@ -62,7 +60,7 @@ class XList extends Action
|
||||
),
|
||||
])
|
||||
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
|
||||
+3
-4
@@ -7,9 +7,8 @@ use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
|
||||
@@ -46,7 +45,7 @@ class XList extends DocumentLogXList
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('collectionId', '', new UID(), 'Collection ID.')
|
||||
->param('documentId', '', new UID(), 'Document ID.')
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('getDatabasesDB')
|
||||
|
||||
@@ -7,10 +7,9 @@ use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
|
||||
@@ -46,7 +45,7 @@ class XList extends CollectionLogXList
|
||||
))
|
||||
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
|
||||
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
|
||||
@@ -7,11 +7,9 @@ use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
|
||||
@@ -48,7 +46,7 @@ class XList extends DatabaseLogs
|
||||
),
|
||||
])
|
||||
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
|
||||
@@ -7,6 +7,7 @@ use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use DeviceDetector\DeviceDetector as Detector;
|
||||
use MaxMind\Db\Reader;
|
||||
@@ -16,9 +17,6 @@ use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Locale\Locale;
|
||||
@@ -57,7 +55,7 @@ class XList extends Action
|
||||
),
|
||||
])
|
||||
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
|
||||
@@ -7,10 +7,9 @@ use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
|
||||
@@ -46,7 +45,7 @@ class XList extends CollectionLogXList
|
||||
))
|
||||
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
|
||||
->param('tableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Table ID.', false, ['dbForProject'])
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
|
||||
@@ -7,10 +7,9 @@ use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
|
||||
@@ -47,7 +46,7 @@ class XList extends DocumentLogXList
|
||||
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
|
||||
->param('tableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Table ID.', false, ['dbForProject'])
|
||||
->param('rowId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Row ID.', false, ['dbForProject'])
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('getDatabasesDB')
|
||||
|
||||
@@ -105,6 +105,8 @@ class Create extends CollectionAction
|
||||
'name' => $name,
|
||||
'dimension' => $dimension,
|
||||
'search' => \implode(' ', [$collectionId, $name]),
|
||||
'attributes' => null,
|
||||
'indexes' => null,
|
||||
]));
|
||||
|
||||
} catch (DuplicateException) {
|
||||
|
||||
+3
-4
@@ -7,9 +7,8 @@ use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
|
||||
@@ -46,7 +45,7 @@ class XList extends DocumentLogXList
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('collectionId', '', new UID(), 'Collection ID.')
|
||||
->param('documentId', '', new UID(), 'Document ID.')
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('getDatabasesDB')
|
||||
|
||||
@@ -7,9 +7,8 @@ use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
|
||||
@@ -45,7 +44,7 @@ class XList extends CollectionLogXList
|
||||
))
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('collectionId', '', new UID(), 'Collection ID.')
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
|
||||
@@ -7,10 +7,8 @@ use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
|
||||
|
||||
@@ -47,7 +45,7 @@ class XList extends DatabaseLogs
|
||||
),
|
||||
])
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
|
||||
@@ -248,6 +248,8 @@ class Create extends Base
|
||||
'providerSilentMode' => $providerSilentMode,
|
||||
'buildSpecification' => $buildSpecification,
|
||||
'runtimeSpecification' => $runtimeSpecification,
|
||||
'vars' => null,
|
||||
'varsProject' => null,
|
||||
]));
|
||||
} catch (DuplicateException) {
|
||||
throw new Exception(Exception::FUNCTION_ALREADY_EXISTS);
|
||||
|
||||
@@ -186,10 +186,11 @@ class Create extends Action
|
||||
'legalAddress' => $legalAddress,
|
||||
'legalTaxId' => ID::custom($legalTaxId),
|
||||
'services' => new \stdClass(),
|
||||
'platforms' => null,
|
||||
'oAuthProviders' => [],
|
||||
'platforms' => null,
|
||||
'webhooks' => null,
|
||||
'keys' => null,
|
||||
'devKeys' => null,
|
||||
'auths' => $auths,
|
||||
'accessedAt' => DateTime::now(),
|
||||
'search' => implode(' ', [$projectId, $name]),
|
||||
|
||||
@@ -11,7 +11,6 @@ use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Projects;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Filters\ListSelection;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Order;
|
||||
@@ -112,7 +111,8 @@ class XList extends Action
|
||||
$selectQueries = Query::groupByType($queries)['selections'] ?? [];
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
$projects = $this->find($dbForPlatform, $queries, $selectQueries);
|
||||
$projects = $dbForPlatform->find('projects', $queries);
|
||||
|
||||
$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.");
|
||||
@@ -125,73 +125,4 @@ class XList extends Action
|
||||
'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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +177,8 @@ class Create extends Base
|
||||
'runtimeSpecification' => $runtimeSpecification,
|
||||
'buildRuntime' => $buildRuntime,
|
||||
'adapter' => $adapter,
|
||||
'vars' => null,
|
||||
'varsProject' => null,
|
||||
]);
|
||||
|
||||
try {
|
||||
|
||||
@@ -84,7 +84,7 @@ class Get extends Action
|
||||
$storageStats = $authorization->skip(fn () => $dbForLogs->getDocument(
|
||||
'stats',
|
||||
$statsDocId,
|
||||
[Query::select(['value'])]
|
||||
[Query::select('value')]
|
||||
));
|
||||
|
||||
$totalSize = $storageStats->getAttribute('value', 0);
|
||||
|
||||
@@ -138,7 +138,7 @@ class XList extends Action
|
||||
|
||||
return $dbForLogs->find('stats', [
|
||||
Query::equal('$id', $statsIds),
|
||||
Query::select(['value']),
|
||||
Query::select('value'),
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Appwrite\Utopia\Response;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\Audit\Audit;
|
||||
@@ -15,9 +16,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
@@ -54,7 +52,7 @@ class XList extends Action
|
||||
]
|
||||
))
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', 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('response')
|
||||
->inject('dbForProject')
|
||||
|
||||
@@ -234,6 +234,9 @@ class Create extends Action
|
||||
'sessions' => null,
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'authenticators' => null,
|
||||
'challenges' => null,
|
||||
'targets' => null,
|
||||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
'emailCanonical' => $emailMetadata['emailCanonical'],
|
||||
'emailIsCanonical' => $emailMetadata['emailIsCanonical'],
|
||||
|
||||
@@ -7,6 +7,7 @@ use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Types;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Swoole\Coroutine\WaitGroup;
|
||||
use Utopia\Config\Adapters\Dotenv as ConfigDotenv;
|
||||
@@ -15,9 +16,6 @@ use Utopia\Config\Exceptions\Parse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Detector\Detection\Framework\Analog;
|
||||
use Utopia\Detector\Detection\Framework\Angular;
|
||||
use Utopia\Detector\Detection\Framework\Astro;
|
||||
@@ -96,7 +94,7 @@ class XList extends Action
|
||||
->param('installationId', '', new Text(256), 'Installation Id')
|
||||
->param('type', '', new WhiteList(['runtime', 'framework']), 'Detector type. Must be one of the following: runtime, framework')
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Types([Query::TYPE_LIMIT, Query::TYPE_OFFSET]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('gitHub')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
|
||||
@@ -455,7 +455,10 @@ class Deletes extends Action
|
||||
$queries[] = Query::equal('resourceType', [$resourceType]);
|
||||
}
|
||||
|
||||
$queries[] = Query::select($this->selects);
|
||||
foreach ($this->selects as $select) {
|
||||
$queries[] = Query::select($select);
|
||||
}
|
||||
|
||||
$queries[] = Query::orderAsc();
|
||||
|
||||
$this->deleteByGroup(
|
||||
@@ -492,12 +495,16 @@ class Deletes extends Action
|
||||
);
|
||||
|
||||
$queries = [
|
||||
Query::select([...$this->selects, 'accessedAt']),
|
||||
Query::select('accessedAt'),
|
||||
Query::lessThan('accessedAt', $datetime),
|
||||
Query::orderDesc('accessedAt'),
|
||||
Query::orderDesc(),
|
||||
];
|
||||
|
||||
foreach ($this->selects as $select) {
|
||||
$queries[] = Query::select($select);
|
||||
}
|
||||
|
||||
$this->deleteByGroup(
|
||||
'cache',
|
||||
$queries,
|
||||
@@ -527,27 +534,37 @@ class Deletes extends Action
|
||||
|
||||
$selects = [...$this->selects, 'time'];
|
||||
|
||||
// Delete Usage stats from projectDB
|
||||
$this->deleteByGroup('stats', [
|
||||
Query::select($selects),
|
||||
$queries = [
|
||||
Query::equal('period', ['1h']),
|
||||
Query::lessThan('time', $hourlyUsageRetentionDatetime),
|
||||
Query::orderDesc('time'),
|
||||
Query::orderDesc(),
|
||||
], $dbForProject);
|
||||
];
|
||||
|
||||
foreach ($selects as $select) {
|
||||
$queries[] = Query::select($select);
|
||||
}
|
||||
|
||||
// Delete Usage stats from projectDB
|
||||
$this->deleteByGroup('stats', $queries, $dbForProject);
|
||||
|
||||
if ($project->getId() !== 'console') {
|
||||
/** @var Database $dbForLogs */
|
||||
$dbForLogs = call_user_func($getLogsDB, $project);
|
||||
|
||||
// Delete Usage stats from logsDB
|
||||
$this->deleteByGroup('stats', [
|
||||
Query::select($selects),
|
||||
$queries = [
|
||||
Query::equal('period', ['1h']),
|
||||
Query::lessThan('time', $hourlyUsageRetentionDatetime),
|
||||
Query::orderDesc('time'),
|
||||
Query::orderDesc(),
|
||||
], $dbForLogs);
|
||||
];
|
||||
|
||||
foreach ($selects as $select) {
|
||||
$queries[] = Query::select($select);
|
||||
}
|
||||
|
||||
// Delete Usage stats from logsDB
|
||||
$this->deleteByGroup('stats', $queries, $dbForLogs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -881,13 +898,19 @@ class Deletes extends Action
|
||||
/** @var Database $dbForProject */
|
||||
$dbForProject = $getProjectDB($project);
|
||||
|
||||
// Delete Executions
|
||||
$this->deleteByGroup('executions', [
|
||||
Query::select([...$this->selects, '$createdAt']),
|
||||
$queries = [
|
||||
Query::select('$createdAt'),
|
||||
Query::lessThan('$createdAt', $datetime),
|
||||
Query::orderDesc('$createdAt'),
|
||||
Query::orderDesc(),
|
||||
], $dbForProject);
|
||||
];
|
||||
|
||||
foreach ($this->selects as $select) {
|
||||
$queries[] = Query::select($select);
|
||||
}
|
||||
|
||||
// Delete Executions
|
||||
$this->deleteByGroup('executions', $queries, $dbForProject);
|
||||
|
||||
/* delete based on custom retention, if any */
|
||||
$this->deleteExecutionsByLimit($project, $getProjectDB, $executionsRetentionCount);
|
||||
@@ -920,7 +943,7 @@ class Deletes extends Action
|
||||
$delete = function (Database $dbForProject, string $resourceInternalId, string $resourceType) use ($executionsRetentionCount) {
|
||||
// get the execution at position `N+1`
|
||||
$execution = $dbForProject->findOne('executions', [
|
||||
Query::select(['$createdAt']),
|
||||
Query::select('$createdAt'),
|
||||
Query::equal('resourceInternalId', [$resourceInternalId]),
|
||||
Query::equal('resourceType', [$resourceType]),
|
||||
Query::orderDesc('$createdAt'),
|
||||
@@ -931,14 +954,19 @@ class Deletes extends Action
|
||||
// delete everything older
|
||||
$cutoffTime = $execution->getAttribute('$createdAt');
|
||||
|
||||
$this->deleteByGroup('executions', [
|
||||
Query::select([...$this->selects, '$createdAt']),
|
||||
$queries = [
|
||||
Query::equal('resourceInternalId', [$resourceInternalId]),
|
||||
Query::equal('resourceType', [$resourceType]),
|
||||
Query::lessThan('$createdAt', $cutoffTime),
|
||||
Query::orderDesc('$createdAt'),
|
||||
Query::orderDesc(),
|
||||
], $dbForProject);
|
||||
];
|
||||
|
||||
foreach ([...$this->selects, '$createdAt'] as $select) {
|
||||
$queries[] = Query::select($select);
|
||||
}
|
||||
|
||||
$this->deleteByGroup('executions', $queries, $dbForProject);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -949,7 +977,10 @@ class Deletes extends Action
|
||||
$processResource = function (string $type) use ($dbForProject, $delete) {
|
||||
$this->listByGroup(
|
||||
collection: $type,
|
||||
queries: [Query::select(['$id', '$sequence'])],
|
||||
queries: [
|
||||
Query::select('$id'),
|
||||
Query::select('$sequence')
|
||||
],
|
||||
database: $dbForProject,
|
||||
callback: function (Document $resource) use ($dbForProject, $delete, $type) {
|
||||
$delete($dbForProject, $resource->getSequence(), $type);
|
||||
@@ -977,13 +1008,19 @@ class Deletes extends Action
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$expired = DateTime::addSeconds(new \DateTime(), -1 * $duration);
|
||||
|
||||
// Delete Sessions
|
||||
$this->deleteByGroup('sessions', [
|
||||
Query::select([...$this->selects, '$createdAt']),
|
||||
$queries = [
|
||||
Query::select('$createdAt'),
|
||||
Query::lessThan('$createdAt', $expired),
|
||||
Query::orderDesc('$createdAt'),
|
||||
Query::orderDesc(),
|
||||
], $dbForProject);
|
||||
];
|
||||
|
||||
foreach ($this->selects as $select) {
|
||||
$queries[] = Query::select($select);
|
||||
}
|
||||
|
||||
// Delete Sessions
|
||||
$this->deleteByGroup('sessions', $queries, $dbForProject);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1005,14 +1042,22 @@ class Deletes extends Action
|
||||
|
||||
Console::info("Deleting CSV export files older than " . $oneWeekAgo);
|
||||
|
||||
$this->deleteByGroup('bucket_' . $bucket->getSequence(), [
|
||||
Query::select([...$this->selects, '$createdAt', 'name', 'path']),
|
||||
$queries = [
|
||||
Query::select('$createdAt'),
|
||||
Query::select('name'),
|
||||
Query::select('path'),
|
||||
Query::equal('bucketId', ['default']),
|
||||
Query::createdBefore($oneWeekAgo),
|
||||
Query::endsWith('name', '.csv'),
|
||||
Query::orderDesc('$createdAt'),
|
||||
Query::orderDesc(),
|
||||
], $dbForPlatform, function (Document $file) use ($deviceForFiles) {
|
||||
];
|
||||
|
||||
foreach ($this->selects as $select) {
|
||||
$queries[] = Query::select($select);
|
||||
}
|
||||
|
||||
$this->deleteByGroup('bucket_' . $bucket->getSequence(), $queries, $dbForPlatform, function (Document $file) use ($deviceForFiles) {
|
||||
$path = $file->getAttribute('path');
|
||||
if ($deviceForFiles->exists($path)) {
|
||||
$deviceForFiles->delete($path);
|
||||
@@ -1115,12 +1160,18 @@ class Deletes extends Action
|
||||
* Delete Logs
|
||||
*/
|
||||
Console::info("Deleting logs for site " . $siteId);
|
||||
$this->deleteByGroup('executions', [
|
||||
Query::select($this->selects),
|
||||
|
||||
$queries = [
|
||||
Query::equal('resourceInternalId', [$siteInternalId]),
|
||||
Query::equal('resourceType', ['sites']),
|
||||
Query::orderAsc()
|
||||
], $dbForProject);
|
||||
];
|
||||
|
||||
foreach ($this->selects as $select) {
|
||||
$queries[] = Query::select($select);
|
||||
}
|
||||
|
||||
$this->deleteByGroup('executions', $queries, $dbForProject);
|
||||
|
||||
/**
|
||||
* Delete VCS Repositories and VCS Comments
|
||||
@@ -1200,12 +1251,18 @@ class Deletes extends Action
|
||||
* Delete Executions
|
||||
*/
|
||||
Console::info("Deleting executions for function " . $functionId);
|
||||
$this->deleteByGroup('executions', [
|
||||
Query::select($this->selects),
|
||||
|
||||
$queries = [
|
||||
Query::equal('resourceInternalId', [$functionInternalId]),
|
||||
Query::equal('resourceType', ['functions']),
|
||||
Query::orderAsc()
|
||||
], $dbForProject);
|
||||
];
|
||||
|
||||
foreach ($this->selects as $select) {
|
||||
$queries[] = Query::select($select);
|
||||
}
|
||||
|
||||
$this->deleteByGroup('executions', $queries, $dbForProject);
|
||||
|
||||
/**
|
||||
* Delete VCS Repositories and VCS Comments
|
||||
|
||||
@@ -121,7 +121,8 @@ class Functions extends Action
|
||||
$offset = 0;
|
||||
while ($sum >= $limit) {
|
||||
$functions = $dbForProject->find('functions', [
|
||||
Query::select(['$id', 'events']), // Skip variables subqueries
|
||||
Query::select('$id'), // Skip variables subqueries
|
||||
Query::select('events'),
|
||||
Query::contains('events', $events),
|
||||
Query::limit($limit),
|
||||
Query::offset($offset),
|
||||
|
||||
@@ -44,8 +44,7 @@ class RuntimeQuery extends Query
|
||||
return false;
|
||||
}
|
||||
|
||||
$values = $query->getValues();
|
||||
return count($values) === 1 && $values[0] === '*';
|
||||
return $query->getAttribute() === '*';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,8 +82,7 @@ class RuntimeQuery extends Query
|
||||
// Check for select("*") upfront
|
||||
foreach ($queries as $query) {
|
||||
if ($query->getMethod() === Query::TYPE_SELECT) {
|
||||
$values = $query->getValues();
|
||||
if (count($values) === 1 && $values[0] === '*') {
|
||||
if ($query->getAttribute() === '*') {
|
||||
return ['type' => 'selectAll'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,58 @@ namespace Appwrite\Utopia\Database\Validator\Queries;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
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;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\QueryContext;
|
||||
|
||||
class Base extends Queries
|
||||
class Base extends Types
|
||||
{
|
||||
protected Document $collection;
|
||||
|
||||
protected array $types = [
|
||||
Query::TYPE_LIMIT,
|
||||
Query::TYPE_OFFSET,
|
||||
Query::TYPE_CURSOR_AFTER,
|
||||
Query::TYPE_CURSOR_BEFORE,
|
||||
Query::TYPE_ORDER_ASC,
|
||||
Query::TYPE_ORDER_DESC,
|
||||
Query::TYPE_ORDER_RANDOM,
|
||||
Query::TYPE_EQUAL,
|
||||
Query::TYPE_NOT_EQUAL,
|
||||
Query::TYPE_LESSER,
|
||||
Query::TYPE_LESSER_EQUAL,
|
||||
Query::TYPE_GREATER,
|
||||
Query::TYPE_GREATER_EQUAL,
|
||||
Query::TYPE_SEARCH,
|
||||
Query::TYPE_NOT_SEARCH,
|
||||
Query::TYPE_IS_NULL,
|
||||
Query::TYPE_IS_NOT_NULL,
|
||||
Query::TYPE_BETWEEN,
|
||||
Query::TYPE_NOT_BETWEEN,
|
||||
Query::TYPE_STARTS_WITH,
|
||||
Query::TYPE_NOT_STARTS_WITH,
|
||||
Query::TYPE_ENDS_WITH,
|
||||
Query::TYPE_NOT_ENDS_WITH,
|
||||
Query::TYPE_CONTAINS,
|
||||
Query::TYPE_NOT_CONTAINS,
|
||||
Query::TYPE_AND,
|
||||
Query::TYPE_OR,
|
||||
Query::TYPE_CROSSES,
|
||||
Query::TYPE_NOT_CROSSES,
|
||||
Query::TYPE_DISTANCE_EQUAL,
|
||||
Query::TYPE_DISTANCE_NOT_EQUAL,
|
||||
Query::TYPE_DISTANCE_GREATER_THAN,
|
||||
Query::TYPE_DISTANCE_LESS_THAN,
|
||||
Query::TYPE_INTERSECTS,
|
||||
Query::TYPE_NOT_INTERSECTS,
|
||||
Query::TYPE_OVERLAPS,
|
||||
Query::TYPE_NOT_OVERLAPS,
|
||||
Query::TYPE_TOUCHES,
|
||||
Query::TYPE_NOT_TOUCHES,
|
||||
Query::TYPE_VECTOR_DOT,
|
||||
Query::TYPE_VECTOR_COSINE,
|
||||
Query::TYPE_VECTOR_EUCLIDEAN,
|
||||
];
|
||||
|
||||
/**
|
||||
* Expression constructor
|
||||
*
|
||||
@@ -36,27 +78,18 @@ class Base extends Queries
|
||||
|
||||
$collection = $collections[$collection];
|
||||
|
||||
$allowedAttributesLookup = [];
|
||||
foreach ($allowedAttributes as $attribute) {
|
||||
$allowedAttributesLookup[$attribute] = true;
|
||||
}
|
||||
$this->collection = new Document([
|
||||
'$id' => $collection['$id'],
|
||||
]);
|
||||
|
||||
$allAttributes = [];
|
||||
$attributes = [];
|
||||
foreach ($collection['attributes'] as $attribute) {
|
||||
$key = $attribute['$id'];
|
||||
|
||||
$attributeDocument = new Document([
|
||||
'key' => $key,
|
||||
$attr = new Document([
|
||||
'key' => $attribute['$id'],
|
||||
'type' => $attribute['type'],
|
||||
'array' => $attribute['array'],
|
||||
]);
|
||||
|
||||
$allAttributes[] = $attributeDocument;
|
||||
|
||||
if (isset($allowedAttributesLookup[$key])) {
|
||||
$attributes[] = $attributeDocument;
|
||||
}
|
||||
$this->collection->setAttribute('attributes', $attr, Document::SET_TYPE_APPEND);
|
||||
}
|
||||
|
||||
$internalAttributes = [
|
||||
@@ -83,23 +116,18 @@ class Base extends Queries
|
||||
];
|
||||
|
||||
foreach ($internalAttributes as $attribute) {
|
||||
$attributes[] = $attribute;
|
||||
$allAttributes[] = $attribute;
|
||||
$this->collection->setAttribute('attributes', $attribute, Document::SET_TYPE_APPEND);
|
||||
$allowedAttributes[] = $attribute['key'];
|
||||
}
|
||||
|
||||
$validators = [
|
||||
new Limit(),
|
||||
new Offset(),
|
||||
new Cursor(),
|
||||
new Filter($attributes, APP_DATABASE_QUERY_MAX_VALUES),
|
||||
new Order($attributes),
|
||||
];
|
||||
|
||||
if ($this->isSelectQueryAllowed()) {
|
||||
$validators[] = new Select($allAttributes);
|
||||
$this->types[] = Query::TYPE_SELECT;
|
||||
}
|
||||
|
||||
parent::__construct($validators);
|
||||
$context = new QueryContext();
|
||||
$context->add($this->collection);
|
||||
|
||||
parent::__construct($this->types, $context, $allowedAttributes);
|
||||
}
|
||||
|
||||
public function isSelectQueryAllowed(): bool
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator\Queries;
|
||||
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\QueryContext;
|
||||
use Utopia\Database\Validator\Queries\Queries as DocumentsValidator;
|
||||
use Utopia\Validator;
|
||||
|
||||
class Types extends Validator
|
||||
{
|
||||
protected string $message = 'Invalid queries';
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
protected array $types;
|
||||
|
||||
protected ?QueryContext $context = null;
|
||||
|
||||
/**
|
||||
* @var array<string>
|
||||
*/
|
||||
private array $allowedAttributes;
|
||||
|
||||
/**
|
||||
* @param array $types
|
||||
* @param QueryContext|null $context
|
||||
* @param array<string> $allowedAttributes
|
||||
*/
|
||||
public function __construct(array $types = [], ?QueryContext $context = null, array $allowedAttributes = [])
|
||||
{
|
||||
$this->types = $types;
|
||||
|
||||
if (is_null($context)) {
|
||||
$context = new QueryContext();
|
||||
}
|
||||
|
||||
$this->context = $context;
|
||||
$this->allowedAttributes = $allowedAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Description.
|
||||
*
|
||||
* Returns validator description
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<Query|string> $value
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value): bool
|
||||
{
|
||||
try {
|
||||
if (!is_array($value)) {
|
||||
throw new \Exception('Queries must be an array');
|
||||
}
|
||||
|
||||
foreach ($value as $query) {
|
||||
if (!$query instanceof Query) {
|
||||
$query = Query::parse($query);
|
||||
}
|
||||
|
||||
if ($query->isNested()) {
|
||||
if (!$this->isValid($query->getValues())) {
|
||||
throw new \Exception('Invalid queries');
|
||||
}
|
||||
}
|
||||
|
||||
if (!in_array($query->getMethod(), $this->types)) {
|
||||
throw new \Exception("Query method {$query->getMethod()} not allowed");
|
||||
}
|
||||
|
||||
if (
|
||||
$query->getMethod() !== Query::TYPE_SELECT &&
|
||||
!empty($query->getAttribute()) &&
|
||||
!in_array($query->getAttribute(), $this->allowedAttributes)
|
||||
) {
|
||||
throw new \Exception("Invalid query: Attribute not found in schema: {$query->getAttribute()}");
|
||||
}
|
||||
}
|
||||
|
||||
$validator = new DocumentsValidator(
|
||||
$this->context,
|
||||
Database::VAR_INTEGER
|
||||
);
|
||||
|
||||
if (!$validator->isValid($value)) {
|
||||
throw new \Exception($validator->getDescription());
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
$this->message = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is array
|
||||
*
|
||||
* Function will return true if object is array.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isArray(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* Returns validator type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return self::TYPE_OBJECT;
|
||||
}
|
||||
}
|
||||
@@ -242,29 +242,58 @@ class V17 extends Filter
|
||||
case Query::TYPE_ENDS_WITH:
|
||||
$attribute = $parsedParams[0] ?? '';
|
||||
if (count($parsedParams) < 2) {
|
||||
return new Query($method, $attribute);
|
||||
return Query::parseQuery([
|
||||
'method' => $method,
|
||||
'attribute' => $attribute,
|
||||
]);
|
||||
}
|
||||
return new Query($method, $attribute, \is_array($parsedParams[1]) ? $parsedParams[1] : [$parsedParams[1]]);
|
||||
|
||||
return Query::parseQuery([
|
||||
'method' => $method,
|
||||
'attribute' => $attribute,
|
||||
'values' => \is_array($parsedParams[1]) ? $parsedParams[1] : [$parsedParams[1]],
|
||||
]);
|
||||
|
||||
case Query::TYPE_BETWEEN:
|
||||
return new Query($method, $parsedParams[0], [$parsedParams[1], $parsedParams[2]]);
|
||||
return Query::parseQuery([
|
||||
'method' => $method,
|
||||
'attribute' => $parsedParams[0],
|
||||
'values' => [$parsedParams[1], $parsedParams[2]],
|
||||
]);
|
||||
|
||||
|
||||
case Query::TYPE_SELECT:
|
||||
return new Query($method, values: $parsedParams[0]);
|
||||
return Query::parseQuery([
|
||||
'method' => $method,
|
||||
'values' => $parsedParams[0],
|
||||
]);
|
||||
|
||||
case Query::TYPE_ORDER_ASC:
|
||||
case Query::TYPE_ORDER_DESC:
|
||||
return new Query($method, $parsedParams[0] ?? '');
|
||||
return Query::parseQuery([
|
||||
'method' => $method,
|
||||
'attribute' => $parsedParams[0] ?? '',
|
||||
]);
|
||||
|
||||
case Query::TYPE_LIMIT:
|
||||
case Query::TYPE_OFFSET:
|
||||
case Query::TYPE_CURSOR_AFTER:
|
||||
case Query::TYPE_CURSOR_BEFORE:
|
||||
if (count($parsedParams) > 0) {
|
||||
return new Query($method, values: [$parsedParams[0]]);
|
||||
return Query::parseQuery([
|
||||
'method' => $method,
|
||||
'values' => [$parsedParams[0]],
|
||||
]);
|
||||
}
|
||||
return new Query($method);
|
||||
|
||||
return Query::parseQuery([
|
||||
'method' => $method,
|
||||
]);
|
||||
|
||||
default:
|
||||
return new Query($method);
|
||||
return Query::parseQuery([
|
||||
'method' => $method,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,10 @@ class V20 extends Filter
|
||||
);
|
||||
|
||||
// Add wildcard + relationship(s) selects
|
||||
$parsed[] = Query::select($selects);
|
||||
$parsed[] = Query::parseQuery([
|
||||
'method' => Query::TYPE_SELECT,
|
||||
'values' => $selects,
|
||||
]);
|
||||
}
|
||||
|
||||
$resolvedQueries = [];
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Request\Filters;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Request\Filter;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Query;
|
||||
|
||||
class V22 extends Filter
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $internalAttributes = [
|
||||
'$id',
|
||||
'$sequence',
|
||||
'$permissions',
|
||||
'$createdAt',
|
||||
'$updatedAt',
|
||||
];
|
||||
|
||||
public function parse(array $content, string $model): array
|
||||
{
|
||||
if (!isset($content['queries'])) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
return $this->convertSelectQueries($content);
|
||||
}
|
||||
|
||||
private function convertSelectQueries(array $content): array
|
||||
{
|
||||
try {
|
||||
$parsed = Query::parseQueries($content['queries']);
|
||||
} catch (QueryException $e) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
$queries = [];
|
||||
$selects = [];
|
||||
|
||||
foreach ($parsed as $query) {
|
||||
if ($query->getMethod() === 'select') {
|
||||
foreach ($query->getValues() as $val) {
|
||||
$selects[] = $val;
|
||||
}
|
||||
} else {
|
||||
$queries[] = $query;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($selects)) {
|
||||
$queries = array_merge($queries, $this->expandSelects($selects));
|
||||
}
|
||||
|
||||
// Convert all queries to string
|
||||
$resolvedQueries = [];
|
||||
foreach ($queries as $query) {
|
||||
$resolvedQueries[] = $query->toString();
|
||||
}
|
||||
|
||||
$content['queries'] = $resolvedQueries;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function expandSelects(array $selects): array
|
||||
{
|
||||
$expanded = [];
|
||||
$addedInternalForLevel = [];
|
||||
|
||||
foreach ($selects as $select) {
|
||||
$expanded[] = Query::select($select);
|
||||
|
||||
if ($select === '*') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parts = explode('.', $select);
|
||||
$prefix = implode('.', array_slice($parts, 0, -1)); // empty string for top level
|
||||
|
||||
if (!isset($addedInternalForLevel[$prefix])) {
|
||||
foreach ($this->internalAttributes as $attr) {
|
||||
$expanded[] = Query::select($prefix ? "$prefix.$attr" : $attr);
|
||||
}
|
||||
$addedInternalForLevel[$prefix] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $expanded;
|
||||
}
|
||||
}
|
||||
@@ -20,12 +20,11 @@ class ListSelection extends Filter
|
||||
|
||||
$selections = [];
|
||||
foreach ($this->selectQueries as $query) {
|
||||
foreach ($query->getValues() as $value) {
|
||||
if ($value === '*') {
|
||||
return $content;
|
||||
}
|
||||
$selections[$value] = true;
|
||||
if ($query->getAttribute() === '*') {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$selections[$query->getAttribute()] = true;
|
||||
}
|
||||
|
||||
return $this->handleList($content, $this->itemsKey, function (array $item) use ($selections) {
|
||||
|
||||
@@ -1211,7 +1211,7 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), [
|
||||
'queries' => [Query::select(['key'])->toString()],
|
||||
'queries' => [Query::select('key')->toString()],
|
||||
]);
|
||||
$this->assertEquals(Exception::GENERAL_ARGUMENT_INVALID, $response['body']['type']);
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
@@ -2500,7 +2500,7 @@ trait DatabasesBase
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]), [
|
||||
'queries' => [
|
||||
Query::select(['key'])->toString(),
|
||||
Query::select('key')->toString(),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(Exception::GENERAL_ARGUMENT_INVALID, $response['body']['type']);
|
||||
@@ -2541,6 +2541,7 @@ trait DatabasesBase
|
||||
'title' => 'Spider-Man: Far From Home',
|
||||
'releaseYear' => 2019,
|
||||
'birthDay' => null,
|
||||
'duration' => null,
|
||||
'actors' => [
|
||||
'Tom Holland',
|
||||
'Zendaya Maree Stoermer',
|
||||
@@ -2600,6 +2601,7 @@ trait DatabasesBase
|
||||
$this->assertEquals($databaseId, $document1['body']['$databaseId']);
|
||||
$this->assertEquals($document1['body']['title'], 'Captain America');
|
||||
$this->assertEquals($document1['body']['releaseYear'], 1944);
|
||||
$this->assertArrayNotHasKey('duration', $document1['body']);
|
||||
$this->assertIsArray($document1['body']['$permissions']);
|
||||
$this->assertCount(3, $document1['body']['$permissions']);
|
||||
$this->assertCount(2, $document1['body']['actors']);
|
||||
@@ -2917,7 +2919,8 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['fullName', 'library.*'])->toString(),
|
||||
Query::select('fullName')->toString(),
|
||||
Query::select('library.*')->toString(),
|
||||
Query::equal('library', ['library1'])->toString(),
|
||||
],
|
||||
]);
|
||||
@@ -2955,7 +2958,8 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['fullName', 'library.*'])->toString(),
|
||||
Query::select('fullName')->toString(),
|
||||
Query::select('library.*')->toString(),
|
||||
Query::equal('library', ['library1'])->toString(),
|
||||
],
|
||||
]);
|
||||
@@ -2993,7 +2997,8 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['fullName', 'library.*'])->toString()
|
||||
Query::select('fullName')->toString(),
|
||||
Query::select('library.*')->toString(),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals(2, $documents['body']['total']);
|
||||
@@ -3154,7 +3159,9 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['fullName', 'library.*'])->toString()
|
||||
Query::select('$permissions')->toString(),
|
||||
Query::select('fullName')->toString(),
|
||||
Query::select('library.*')->toString(),
|
||||
],
|
||||
]);
|
||||
$this->assertGreaterThanOrEqual(1, $documents['body']['total']);
|
||||
@@ -3333,7 +3340,9 @@ trait DatabasesBase
|
||||
// Filter to setup documents only, since other tests may have created additional docs in this collection.
|
||||
$baseQueries = [
|
||||
Query::equal('$id', $docIds)->toString(),
|
||||
Query::select(['title', 'releaseYear', '$id'])->toString(),
|
||||
Query::select('title')->toString(),
|
||||
Query::select('releaseYear')->toString(),
|
||||
Query::select('$id')->toString(),
|
||||
Query::orderAsc('releaseYear')->toString(),
|
||||
];
|
||||
|
||||
@@ -3402,7 +3411,7 @@ trait DatabasesBase
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::equal('$id', $docIds)->toString(),
|
||||
Query::select(['title'])->toString(),
|
||||
Query::select('title')->toString(),
|
||||
Query::orderAsc('releaseYear')->toString(),
|
||||
],
|
||||
'ttl' => 10,
|
||||
@@ -3424,7 +3433,9 @@ trait DatabasesBase
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::equal('$id', $docIds)->toString(),
|
||||
Query::select(['title', 'releaseYear', '$id'])->toString(),
|
||||
Query::select('title')->toString(),
|
||||
Query::select('releaseYear')->toString(),
|
||||
Query::select('$id')->toString(),
|
||||
Query::orderAsc('releaseYear')->toString(),
|
||||
],
|
||||
]);
|
||||
@@ -3467,7 +3478,8 @@ trait DatabasesBase
|
||||
// Use different select queries from testListDocumentsWithCache to avoid cache key collision.
|
||||
$queries = [
|
||||
Query::equal('$id', $docIds)->toString(),
|
||||
Query::select(['title', '$id'])->toString(),
|
||||
Query::select('title')->toString(),
|
||||
Query::select('$id')->toString(),
|
||||
Query::orderAsc('$createdAt')->toString(),
|
||||
];
|
||||
|
||||
@@ -3587,7 +3599,9 @@ trait DatabasesBase
|
||||
// Use different select queries from other cache tests to avoid cache key collision.
|
||||
$queries = [
|
||||
Query::equal('$id', $docIds)->toString(),
|
||||
Query::select(['title', 'tagline', '$id'])->toString(),
|
||||
Query::select('title')->toString(),
|
||||
Query::select('tagline')->toString(),
|
||||
Query::select('$id')->toString(),
|
||||
Query::orderAsc('$createdAt')->toString(),
|
||||
];
|
||||
|
||||
@@ -3728,11 +3742,36 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['title', 'releaseYear', '$id', '$sequence'])->toString(),
|
||||
Query::select('title')->toString(),
|
||||
Query::select('releaseYear')->toString(),
|
||||
Query::select('$id')->toString(),
|
||||
Query::select('$sequence')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Use specific X-Appwrite-Response-Format 1.8.0
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, $this->getRecordUrl($databaseId, $document[$this->getContainerIdResponseKey()]), array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'X-Appwrite-Response-Format' => '1.8.0',
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
'{"method":"select","values":["title"]}',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertArrayHasKey('$sequence', $response['body'][$this->getRecordResource()][0]);
|
||||
$this->assertArrayHasKey('$id', $response['body'][$this->getRecordResource()][0]);
|
||||
$this->assertArrayHasKey('$createdAt', $response['body'][$this->getRecordResource()][0]);
|
||||
$this->assertArrayHasKey('$updatedAt', $response['body'][$this->getRecordResource()][0]);
|
||||
$this->assertArrayHasKey('$permissions', $response['body'][$this->getRecordResource()][0]);
|
||||
$this->assertArrayHasKey('title', $response['body'][$this->getRecordResource()][0]);
|
||||
$this->assertArrayNotHasKey('birthDay', $response['body'][$this->getRecordResource()][0]);
|
||||
}
|
||||
|
||||
public function testQueryBySequenceType(): void
|
||||
@@ -6463,6 +6502,7 @@ trait DatabasesBase
|
||||
],
|
||||
'libraryName' => 'Library 1',
|
||||
],
|
||||
'fullName' => 'Jake',
|
||||
],
|
||||
'permissions' => [
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
@@ -6510,7 +6550,8 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['fullName', 'library.*'])->toString(),
|
||||
Query::select('fullName')->toString(),
|
||||
Query::select('library.*')->toString(),
|
||||
Query::equal('library', ['library1'])->toString(),
|
||||
],
|
||||
]);
|
||||
@@ -6524,7 +6565,7 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['library.*'])->toString(),
|
||||
Query::select('library.*')->toString(),
|
||||
Query::equal('library.libraryName', ['Library 1'])->toString(),
|
||||
],
|
||||
]);
|
||||
@@ -6674,7 +6715,8 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['*', 'libraries.*'])->toString()
|
||||
Query::select('*')->toString(),
|
||||
Query::select('libraries.*')->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -6688,7 +6730,7 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['person_one_to_many.$id'])->toString()
|
||||
Query::select('person_one_to_many.$id')->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -6843,7 +6885,9 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['*', 'artist.name', 'artist.$permissions'])->toString()
|
||||
Query::select('*')->toString(),
|
||||
Query::select('artist.name')->toString(),
|
||||
Query::select('artist.$permissions')->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -6859,7 +6903,10 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['*', 'albums.$id', 'albums.name', 'albums.$permissions'])->toString()
|
||||
Query::select('*')->toString(),
|
||||
Query::select('albums.$id')->toString(),
|
||||
Query::select('albums.name')->toString(),
|
||||
Query::select('albums.$permissions')->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -7001,7 +7048,9 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['*', 'players.name', 'players.$permissions'])->toString()
|
||||
Query::select('*')->toString(),
|
||||
Query::select('players.name')->toString(),
|
||||
Query::select('players.$permissions')->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -7019,7 +7068,10 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['*', 'sports.$id', 'sports.name', 'sports.$permissions'])->toString()
|
||||
Query::select('*')->toString(),
|
||||
Query::select('sports.$id')->toString(),
|
||||
Query::select('sports.name')->toString(),
|
||||
Query::select('sports.$permissions')->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
@@ -7047,7 +7099,8 @@ trait DatabasesBase
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::isNotNull('$id')->toString(),
|
||||
Query::select(['*', 'libraries.*'])->toString(),
|
||||
Query::select('*')->toString(),
|
||||
Query::select('libraries.*')->toString(),
|
||||
Query::startsWith('fullName', 'Stevie')->toString(),
|
||||
Query::endsWith('fullName', 'Wonder')->toString(),
|
||||
Query::between('$createdAt', '1975-12-06', '2050-12-01')->toString(),
|
||||
@@ -7067,7 +7120,7 @@ trait DatabasesBase
|
||||
'queries' => [
|
||||
Query::isNotNull('$id')->toString(),
|
||||
Query::isNull('fullName')->toString(),
|
||||
Query::select(['fullName'])->toString(),
|
||||
Query::select('fullName')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
@@ -7094,7 +7147,7 @@ trait DatabasesBase
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::equal('fullName', ['Stevie Wonder'])->toString(),
|
||||
Query::select(['fullName'])->toString(),
|
||||
Query::select('fullName')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
@@ -7108,7 +7161,8 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['libraries.*', '$id'])->toString(),
|
||||
Query::select('libraries.*')->toString(),
|
||||
Query::select('$id')->toString(),
|
||||
],
|
||||
]);
|
||||
$document = $response['body'][$this->getRecordResource()][0];
|
||||
@@ -7122,7 +7176,8 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['fullName', '$id'])->toString(),
|
||||
Query::select('fullName')->toString(),
|
||||
Query::select('$id')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
@@ -7245,7 +7300,8 @@ trait DatabasesBase
|
||||
], $this->getHeaders()),
|
||||
[
|
||||
'queries' => [
|
||||
Query::select(['first_name', 'last_name'])->toString(),
|
||||
Query::select('first_name')->toString(),
|
||||
Query::select('last_name')->toString(),
|
||||
Query::or([
|
||||
Query::equal('first_name', ['Donald']),
|
||||
Query::equal('last_name', ['Bush'])
|
||||
@@ -8789,7 +8845,10 @@ trait DatabasesBase
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [Query::select(['name', 'pointAttr'])->toString()]
|
||||
'queries' => [
|
||||
Query::select('name')->toString(),
|
||||
Query::select('pointAttr')->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
@@ -8844,7 +8903,8 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['name', 'pointAttr'])->toString(),
|
||||
Query::select('name')->toString(),
|
||||
Query::select('pointAttr')->toString(),
|
||||
Query::orderAsc('name')->toString(),
|
||||
Query::limit(1)->toString()
|
||||
]
|
||||
@@ -8998,7 +9058,8 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['name', 'location.coordinates'])->toString()
|
||||
Query::select('name')->toString(),
|
||||
Query::select('location.coordinates')->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $fetched['headers']['status-code']);
|
||||
@@ -9137,7 +9198,8 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['point', 'person.$id'])->toString()
|
||||
Query::select('point')->toString(),
|
||||
Query::select('person.$id')->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $visitDoc['headers']['status-code']);
|
||||
@@ -9268,7 +9330,7 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['stores.$id'])->toString()
|
||||
Query::select('stores.$id')->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $city['headers']['status-code']);
|
||||
@@ -9398,7 +9460,7 @@ trait DatabasesBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['drivers.$id'])->toString()
|
||||
Query::select('drivers.$id')->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(200, $zone['headers']['status-code']);
|
||||
@@ -10106,7 +10168,8 @@ trait DatabasesBase
|
||||
], $this->getHeaders()),
|
||||
[
|
||||
'queries' => [
|
||||
Query::select(['title', 'genre'])->toString(),
|
||||
Query::select('title')->toString(),
|
||||
Query::select('genre')->toString(),
|
||||
Query::notContains('title', ['Spider'])->toString(),
|
||||
Query::limit(999)->toString(),
|
||||
Query::offset(0)->toString()
|
||||
|
||||
@@ -1090,7 +1090,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
|
||||
$deployments = $this->listDeployments($functionId, [
|
||||
'queries' => [
|
||||
Query::select(['status'])->toString(),
|
||||
Query::select('status')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
@@ -1105,7 +1105,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
// Extra select query check, for attribute not allowed by filter queries
|
||||
$deployments = $this->listDeployments($functionId, [
|
||||
'queries' => [
|
||||
Query::select(['buildLogs'])->toString(),
|
||||
Query::select('buildLogs')->toString(),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals($deployments['headers']['status-code'], 200);
|
||||
|
||||
@@ -506,6 +506,46 @@ class ProjectsConsoleClientTest extends Scope
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test old version select queries are failing
|
||||
*/
|
||||
$list = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
'{"method":"select","values":["name", "$createdAt"]}',
|
||||
]
|
||||
]);
|
||||
$this->assertEquals(400, $list['headers']['status-code']);
|
||||
$this->assertEquals('Invalid `queries` param: Invalid query: Select queries requires an attribute', $list['body']['message']);
|
||||
|
||||
/**
|
||||
* Check Filter V22.php is activated
|
||||
*/
|
||||
$list = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-response-format' => '1.8.0', // APP_VERSION_STABLE
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
'{"method":"select","values":["name", "$createdAt"]}',
|
||||
Query::orderDesc()->toString(),
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $list['headers']['status-code']);
|
||||
|
||||
$project = $list['body']['projects'][0];
|
||||
|
||||
$this->assertEquals('Project Test 2', $project['name']);
|
||||
$this->assertArrayHasKey('$id', $project);
|
||||
$this->assertArrayHasKey('$createdAt', $project);
|
||||
$this->assertArrayHasKey('$updatedAt', $project);
|
||||
$this->assertArrayHasKey('name', $project);
|
||||
$this->assertArrayNotHasKey('platforms', $project);
|
||||
$this->assertArrayNotHasKey('$sequence', $project);
|
||||
}
|
||||
|
||||
#[Group('projectsCRUD')]
|
||||
@@ -543,7 +583,8 @@ class ProjectsConsoleClientTest extends Scope
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['$id', 'name'])->toString(),
|
||||
Query::select('$id')->toString(),
|
||||
Query::select('name')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
@@ -572,7 +613,12 @@ class ProjectsConsoleClientTest extends Scope
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['$id', 'name', 'teamId', 'description', '$createdAt', '$updatedAt'])->toString(),
|
||||
Query::select('$id')->toString(),
|
||||
Query::select('name')->toString(),
|
||||
Query::select('teamId')->toString(),
|
||||
Query::select('description')->toString(),
|
||||
Query::select('$createdAt')->toString(),
|
||||
Query::select('$updatedAt')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
@@ -603,7 +649,9 @@ class ProjectsConsoleClientTest extends Scope
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['$id', 'name', 'teamId'])->toString(),
|
||||
Query::select('$id')->toString(),
|
||||
Query::select('name')->toString(),
|
||||
Query::select('teamId')->toString(),
|
||||
Query::equal('name', ['Query Select Test Project'])->toString(),
|
||||
],
|
||||
]);
|
||||
@@ -634,7 +682,8 @@ class ProjectsConsoleClientTest extends Scope
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['$id', 'name'])->toString(),
|
||||
Query::select('$id')->toString(),
|
||||
Query::select('name')->toString(),
|
||||
Query::limit(2)->toString(),
|
||||
],
|
||||
]);
|
||||
@@ -664,7 +713,9 @@ class ProjectsConsoleClientTest extends Scope
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['$id', 'name', 'platforms'])->toString(),
|
||||
Query::select('$id')->toString(),
|
||||
Query::select('name')->toString(),
|
||||
Query::select('platforms')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
@@ -693,7 +744,10 @@ class ProjectsConsoleClientTest extends Scope
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['$id', 'name', 'webhooks', 'keys'])->toString(),
|
||||
Query::select('$id')->toString(),
|
||||
Query::select('name')->toString(),
|
||||
Query::select('webhooks')->toString(),
|
||||
Query::select('keys')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
@@ -722,7 +776,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['*'])->toString(),
|
||||
Query::select('*')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
@@ -752,7 +806,8 @@ class ProjectsConsoleClientTest extends Scope
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['$id', 'invalidAttribute'])->toString(),
|
||||
Query::select('$id')->toString(),
|
||||
Query::select('invalidAttribute')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ trait RealtimeBase
|
||||
/**
|
||||
* Query param encoding rules:
|
||||
* - $queries === null -> only send channels (no per-channel query params) for backward compatibility.
|
||||
* - $queries === [] -> explicit "select all" subscription: send Query::select(['*']) as a single group.
|
||||
* - $queries === [] -> explicit "select all" subscription: send Query::select('*') as a single group.
|
||||
* - non-empty $queries -> treat as a single subscription group for the first channel:
|
||||
* AND logic within the group; OR logic across multiple groups (if we ever add them).
|
||||
*
|
||||
@@ -49,7 +49,7 @@ trait RealtimeBase
|
||||
|
||||
if ($queries === []) {
|
||||
// Explicit select("*") group - single query in slot 0
|
||||
$queryValue = \Utopia\Database\Query::select(['*'])->toString();
|
||||
$queryValue = \Utopia\Database\Query::select('*')->toString();
|
||||
$queryString .= "&" . urlencode($channel) . "[" . $slot . "][]=" . urlencode($queryValue);
|
||||
} else {
|
||||
// Single subscription group for this channel - multiple queries in slot 0
|
||||
|
||||
@@ -2427,7 +2427,7 @@ class RealtimeCustomClientQueryTest extends Scope
|
||||
|
||||
// Test NEW SDK behavior: project=Query array in query param, project ID in header
|
||||
// The reserved param logic should use Query array as subscription queries for project channel
|
||||
$queryArray = [Query::select(['*'])->toString()];
|
||||
$queryArray = [Query::select('*')->toString()];
|
||||
$clientNewSdk = $this->getWebsocketWithCustomQuery(
|
||||
[
|
||||
'channels' => ['project'],
|
||||
@@ -2503,7 +2503,7 @@ class RealtimeCustomClientQueryTest extends Scope
|
||||
$client->close();
|
||||
|
||||
// Test: project channel with queries, project ID only in header
|
||||
$queryArray = [Query::select(['*'])->toString()];
|
||||
$queryArray = [Query::select('*')->toString()];
|
||||
$clientWithQuery = $this->getWebsocketWithCustomQuery(
|
||||
[
|
||||
'channels' => ['project'],
|
||||
|
||||
@@ -1080,7 +1080,7 @@ class SitesCustomServerTest extends Scope
|
||||
|
||||
$deployments = $this->listDeployments($siteId, [
|
||||
'queries' => [
|
||||
Query::select(['status'])->toString(),
|
||||
Query::select('status')->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
@@ -1093,7 +1093,7 @@ class SitesCustomServerTest extends Scope
|
||||
// Extra select query check, for attribute not allowed by filter queries
|
||||
$deployments = $this->listDeployments($siteId, [
|
||||
'queries' => [
|
||||
Query::select(['buildLogs'])->toString(),
|
||||
Query::select('buildLogs')->toString(),
|
||||
],
|
||||
]);
|
||||
$this->assertEquals($deployments['headers']['status-code'], 200);
|
||||
|
||||
@@ -760,7 +760,7 @@ trait UsersBase
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::select(['name'])->toString()
|
||||
Query::select('name')->toString()
|
||||
]
|
||||
]);
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
|
||||
@@ -26,8 +26,13 @@ final class Eventually extends Constraint
|
||||
return true;
|
||||
} catch (Critical $exception) {
|
||||
throw $exception;
|
||||
} catch (\Exception $exception) {
|
||||
} catch (\Throwable $exception) {
|
||||
$lastException = $exception;
|
||||
} finally {
|
||||
if (ob_get_level() > 0) {
|
||||
ob_flush();
|
||||
}
|
||||
flush();
|
||||
}
|
||||
|
||||
usleep($this->waitMs * 1000);
|
||||
|
||||
@@ -638,41 +638,27 @@ class RuntimeQueryTest extends TestCase
|
||||
// TYPE_SELECT tests - select("*") means "listen to all events"
|
||||
public function testSelectAllIsAllowed(): void
|
||||
{
|
||||
$query = Query::select(['*']);
|
||||
$query = Query::select('*');
|
||||
$this->assertTrue(RuntimeQuery::isSelectAll($query));
|
||||
}
|
||||
|
||||
public function testSelectSpecificFieldsNotAllowed(): void
|
||||
{
|
||||
$query = Query::select(['name', 'age']);
|
||||
$this->assertFalse(RuntimeQuery::isSelectAll($query));
|
||||
}
|
||||
|
||||
public function testSelectSingleFieldNotAllowed(): void
|
||||
{
|
||||
$query = Query::select(['name']);
|
||||
$query = Query::select('name');
|
||||
$this->assertFalse(RuntimeQuery::isSelectAll($query));
|
||||
}
|
||||
|
||||
public function testValidateSelectQueryWithWildcard(): void
|
||||
{
|
||||
$query = Query::select(['*']);
|
||||
$query = Query::select('*');
|
||||
// Should not throw
|
||||
RuntimeQuery::validateSelectQuery($query);
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testValidateSelectQueryWithSpecificFields(): void
|
||||
{
|
||||
$query = Query::select(['name', 'age']);
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Only select("*") is allowed in Realtime queries');
|
||||
RuntimeQuery::validateSelectQuery($query);
|
||||
}
|
||||
|
||||
public function testValidateSelectQueryWithSingleField(): void
|
||||
{
|
||||
$query = Query::select(['name']);
|
||||
$query = Query::select('name');
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Only select("*") is allowed in Realtime queries');
|
||||
RuntimeQuery::validateSelectQuery($query);
|
||||
@@ -700,7 +686,7 @@ class RuntimeQueryTest extends TestCase
|
||||
// Filter tests with select("*")
|
||||
public function testFilterWithSelectAllReturnsPayload(): void
|
||||
{
|
||||
$query = Query::select(['*']);
|
||||
$query = Query::select('*');
|
||||
$payload = ['name' => 'John', 'age' => 30];
|
||||
$result = $this->compileAndFilter([$query], $payload);
|
||||
$this->assertEquals($payload, $result);
|
||||
@@ -710,7 +696,7 @@ class RuntimeQueryTest extends TestCase
|
||||
{
|
||||
// If select("*") is present, it should return payload regardless of other queries
|
||||
$queries = [
|
||||
Query::select(['*']),
|
||||
Query::select('*'),
|
||||
Query::equal('name', ['Jane']), // This would normally fail
|
||||
];
|
||||
$payload = ['name' => 'John', 'age' => 30];
|
||||
@@ -721,7 +707,7 @@ class RuntimeQueryTest extends TestCase
|
||||
|
||||
public function testFilterWithSelectAllOnEmptyPayload(): void
|
||||
{
|
||||
$query = Query::select(['*']);
|
||||
$query = Query::select('*');
|
||||
$payload = [];
|
||||
$result = $this->compileAndFilter([$query], $payload);
|
||||
$this->assertEquals($payload, $result);
|
||||
|
||||
Reference in New Issue
Block a user