Compare commits

...
Author SHA1 Message Date
fogelito 3b86d23151 Message 2026-02-12 12:13:14 +02:00
fogelito 71a4e515cc check queries 2026-02-12 12:12:33 +02:00
fogelito 1116370745 remove var dump 2026-02-12 09:07:23 +02:00
fogelito 6e498beb03 Merge branch 'joins2' of https://github.com/appwrite/appwrite into joins-feature 2026-02-11 15:44:34 +02:00
fogelito f97d272085 Update 1.8.x 2026-02-11 15:43:39 +02:00
fogelito 1a3c4d7b6c Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2
# Conflicts:
#	composer.json
#	composer.lock
2026-02-11 15:22:11 +02:00
fogelito 77c69ce793 add tests 2026-02-11 15:20:47 +02:00
fogelito b41eb48611 test select queries 2026-02-11 12:13:47 +02:00
fogelito 53174fb3e0 Merge branch 'joins2' of https://github.com/appwrite/appwrite into joins-feature 2026-02-11 09:54:56 +02:00
fogelito a770e6c289 framework 0.33.38 2026-02-11 09:53:53 +02:00
fogelito 2633654859 Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2 2026-02-11 09:14:48 +02:00
fogelito be676cf349 Merge branch 'joins2' of https://github.com/appwrite/appwrite into joins-feature 2026-02-11 08:58:40 +02:00
fogelito c1b9f75d47 pull 1.8.x 2026-02-11 08:56:30 +02:00
fogelito 7fd25eb380 Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2 2026-02-11 08:56:07 +02:00
fogelito f3b79f96ab lock 2026-02-11 08:49:44 +02:00
fogelito 45ce82c5c6 addJoinCollectionId 2026-02-10 16:56:38 +02:00
fogelito f76b9a8700 Merge branch 'joins2' of https://github.com/appwrite/appwrite into joins-feature
# Conflicts:
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php
2026-02-10 12:28:41 +02:00
fogelito 0e4dbcca22 pull 1.8.x 2026-02-10 12:26:51 +02:00
fogelito aa3b6b462d Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2
# Conflicts:
#	composer.lock
2026-02-10 12:26:31 +02:00
fogelito a0797868fd addJoinCollection 2026-02-10 12:24:49 +02:00
fogelito 997aafc646 $join->setCollection 2026-02-10 12:20:36 +02:00
fogelito a456ae5acb Update database lib 2026-02-10 12:11:31 +02:00
fogelito c2e7daaf1e feature 2026-02-10 12:10:28 +02:00
fogelito 922950929a Insert Data 2026-02-08 17:02:32 +02:00
fogelito 21af384227 Update migration lib 2026-02-08 14:26:19 +02:00
fogelito 3574d89012 fix constructSubscriptions 2026-02-08 12:02:18 +02:00
fogelito b0e47fbb2c fix compile 2026-02-08 11:53:56 +02:00
fogelito 1762b9d2ec fix VCS 2026-02-08 11:11:37 +02:00
fogelito 79d8ada95d Pull 1.8.x 2026-02-08 10:59:45 +02:00
fogelito affe208d1a Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2
# Conflicts:
#	app/controllers/api/vcs.php
#	composer.lock
2026-02-08 10:56:27 +02:00
Jake BarnbyandGitHub 0a53c677b1 Merge branch '1.8.x' into joins2 2026-02-05 07:29:47 +00:00
fogelito 0e709f1f97 pull 1.8.x 2026-02-03 09:21:53 +02:00
fogelito 0c514d5696 Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2 2026-02-03 09:20:58 +02:00
fogelito da45edcd46 Merge branch 'chore-upgrade-cache' of https://github.com/appwrite/appwrite into joins2
# Conflicts:
#	composer.lock
2026-02-03 08:42:44 +02:00
fogelito 396b152bcf Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2 2026-02-03 08:36:29 +02:00
fogelito d811fb00d9 fix select queries 2026-02-02 16:04:50 +02:00
fogelito 0520eea383 Merge branch 'chore-upgrade-cache' of https://github.com/appwrite/appwrite into joins2 2026-02-02 15:43:56 +02:00
fogelito 8051b6f64e lock 2026-02-02 15:23:58 +02:00
fogelito 406fd1b94a Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2
# Conflicts:
#	composer.lock
2026-02-02 15:23:38 +02:00
fogelito 6a70a1958f database 5 2026-02-02 15:13:45 +02:00
fogelito b222f4e4bb Merge branch 'chore-upgrade-cache' of https://github.com/appwrite/appwrite into joins2
# Conflicts:
#	composer.json
2026-02-02 15:13:07 +02:00
fogelito 66babf9dae Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2
# Conflicts:
#	composer.lock
2026-02-02 13:37:49 +02:00
fogelito 0adacef23f Run migrations tests 2026-01-28 11:11:28 +02:00
fogelito bcd69e34ce lock 2026-01-28 10:30:52 +02:00
fogelito 6775b9505a inline 2026-01-28 08:58:45 +02:00
fogelito 4967887797 lock 2026-01-28 08:57:13 +02:00
fogelito 971e1270d7 Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2 2026-01-28 08:50:42 +02:00
fogelito be6c0a63f1 Update lock 2026-01-28 08:50:21 +02:00
fogelito 5e5f5ef49f Run tests 2026-01-27 16:41:05 +02:00
fogelito bafb326ef7 Comments 2026-01-26 15:25:46 +02:00
fogelito f030631e6e Pull changes 2026-01-26 10:56:52 +02:00
fogelito 1b8462d2e2 Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2
# Conflicts:
#	composer.lock
2026-01-26 10:28:43 +02:00
fogelito 08f0a43465 Run tests 2026-01-26 09:42:37 +02:00
fogelito 87380a2b01 X-Appwrite-Response-Format 1.8.0 2026-01-25 18:04:00 +02:00
fogelito 0748a08fdc Fix tests 2026-01-25 13:41:28 +02:00
fogelito 2e4efb5d2f Fix test with sequence 2026-01-25 13:30:12 +02:00
fogelito 730a967104 Fix sequence when no key 2026-01-25 11:58:58 +02:00
fogelito 95af73a479 Pull 1.8.x 2026-01-25 09:13:05 +02:00
fogelito d7619f7057 Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2
# Conflicts:
#	composer.lock
2026-01-25 09:12:36 +02:00
fogelito 1ec142fe07 getQueriesValidator 2026-01-22 17:33:55 +02:00
fogelito 1c73d54153 revert 2026-01-22 17:32:39 +02:00
fogelito 1daef72ca9 allow nulls 2026-01-22 17:23:46 +02:00
fogelito c5b415af59 add method 2026-01-22 17:05:19 +02:00
fogelito b1915fa20c static 2026-01-22 16:15:52 +02:00
fogelito 82032ac60a validator 2026-01-22 15:56:11 +02:00
fogelito 27121a7f1e revert getQueriesValidator 2026-01-22 15:33:22 +02:00
fogelito bef969738e revert getQueriesValidator 2026-01-22 15:32:02 +02:00
fogelito bea6ccbe92 Pull changes 2026-01-21 15:59:36 +02:00
fogelito c126dba3b5 Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2 2026-01-21 15:58:07 +02:00
fogelito c72e69371b Filters 2026-01-21 15:50:19 +02:00
fogelito a3c6e45e92 convertSelectQueries 2026-01-21 12:37:44 +02:00
fogelito db068f31ba linter 2026-01-20 13:49:13 +02:00
fogelito 50673c3223 Filters 2026-01-20 13:42:22 +02:00
fogelito 7d5d97e53b Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2 2026-01-19 14:41:15 +02:00
fogelito 0b1136c678 lock 2026-01-19 13:25:46 +02:00
fogelito 5d59e17b8c lock 2026-01-19 13:12:20 +02:00
fogelito 7de871c3d9 Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2
# Conflicts:
#	composer.lock
2026-01-19 13:11:58 +02:00
fogelito 2c1a92951e getLimitQuery 2026-01-19 12:40:36 +02:00
fogelito 5991b1f966 Update getCursorQueries 2026-01-18 16:53:02 +02:00
fogelito 4e24b117f5 fix schema resource 2026-01-18 16:30:40 +02:00
fogelito edaa3764c2 Update lock 2026-01-18 15:48:22 +02:00
fogelito 7db91c2ab0 update migrations 2026-01-18 15:32:47 +02:00
fogelito acd8ff839e lock 2026-01-18 10:23:23 +02:00
fogelito 7f5857b232 "utopia-php/pools": "0.8.*", 2026-01-18 10:02:00 +02:00
fogelito 139474d3fb composer.json 2026-01-18 09:24:01 +02:00
fogelito 30469128d3 deletes worker 2026-01-18 09:13:19 +02:00
fogelito 64f4e081b5 Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins3
# Conflicts:
#	src/Appwrite/Platform/Workers/Deletes.php
2026-01-18 09:06:11 +02:00
fogelito c80ff1b541 Revert skipFilters 2026-01-15 17:12:28 +02:00
fogelito edcee5167a lock file 2026-01-15 16:54:22 +02:00
fogelito 1aea7bd6f2 lock file 2026-01-15 16:53:39 +02:00
fogelito a9233717b6 Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins3
# Conflicts:
#	composer.lock
2026-01-15 16:39:00 +02:00
fogelito ccb34dc401 Update composer.lock 2026-01-15 16:38:35 +02:00
fogelito 761a4bb0c7 more select queries 2026-01-13 16:50:31 +02:00
fogelito 11f0e8f5df fix selects 2026-01-13 16:43:21 +02:00
fogelito df26136dd8 fix subqueries 2026-01-13 13:01:37 +02:00
fogelito aab780476e fix projects filters 2026-01-13 11:20:06 +02:00
fogelito 1ae2d09e4b fix tests 2026-01-13 10:35:55 +02:00
fogelito a6a2fa77df Merge branch '1.8.x' of https://github.com/appwrite/appwrite into joins2 2026-01-13 10:14:03 +02:00
fogelito a79f46ed78 pull-auth-auth-refactor 2026-01-13 10:08:05 +02:00
fogelito 638f7431b1 Merge branch 'feat-auth-instance' of https://github.com/appwrite/appwrite into joins2
# Conflicts:
#	composer.json
#	composer.lock
#	src/Appwrite/Platform/Action.php
#	src/Appwrite/Platform/Workers/Deletes.php
2026-01-13 09:56:37 +02:00
fogelito fa3c9a5b76 Remove comment 2026-01-12 16:24:42 +02:00
fogelito c388fea5d0 fix Filters 2026-01-12 16:19:48 +02:00
fogelito a407d2ea84 formatting 2026-01-12 15:42:01 +02:00
fogelito da56ef39f8 remove static 2026-01-12 15:39:36 +02:00
fogelito 275b9a1186 coderabbit comments 2026-01-12 15:35:19 +02:00
fogelito 2f514ae55e Fix message 2026-01-12 15:10:52 +02:00
fogelito 38517b8004 Fix message 2026-01-12 15:02:30 +02:00
fogelito 8758e2e5a0 Run 2026-01-12 12:43:53 +02:00
fogelito dacc6a97d0 disbaled internals 2026-01-12 08:28:50 +02:00
fogelito 13fc2222f8 Update message 2026-01-11 16:37:05 +02:00
fogelito 900d8f248e allowedAttributes 2026-01-11 16:22:19 +02:00
fogelito de572c3aa9 composer 2026-01-11 15:06:55 +02:00
fogelito d4fd5e79d9 lock 2026-01-11 14:54:41 +02:00
fogelito 87247826e0 lock 2026-01-11 14:54:25 +02:00
fogelito 05f4d05cf3 lock 2026-01-11 14:42:46 +02:00
fogelito d12d6acd62 revert 2026-01-11 14:42:16 +02:00
fogelito 133eac66e7 upgrade fetch 2026-01-11 14:35:32 +02:00
fogelito 53a6fef69e lock file 2026-01-11 14:15:34 +02:00
fogelito 0c4f540325 Merge branch 'main' of https://github.com/appwrite/appwrite into joins2 2026-01-11 14:02:57 +02:00
fogelito 19e22e1da7 Types 2025-12-25 18:25:14 +02:00
fogelito 23c1e4fb79 remove var dump 2025-12-25 13:10:30 +02:00
fogelito 904b2da8ce Remove dbg 2025-12-25 12:02:54 +02:00
fogelito ee129e28f1 Remove dbg 2025-12-25 12:02:02 +02:00
fogelito c50ba30f1b fomratting 2025-12-25 12:00:33 +02:00
fogelito 265ccdb03a TablesDB tests 2025-12-25 11:03:19 +02:00
fogelito 3b93e925d4 database test 2025-12-25 10:56:58 +02:00
fogelito 10b38c7c6d enums 2025-12-23 16:42:37 +02:00
fogelito adf6b02462 create document fix reference 2025-12-23 12:18:15 +02:00
fogelito f47c484d8d csv queries 2025-12-23 09:35:28 +02:00
fogelito 6e1d2008f8 Fix filter V17 2025-12-22 15:44:10 +02:00
fogelito 38fb9f2814 select query 2025-12-22 12:47:49 +02:00
fogelito 8d37e843e0 Query select 2025-12-22 12:25:14 +02:00
fogelito 975510c6a0 select queries 2025-12-21 17:42:57 +02:00
fogelito a971976587 format 2025-12-21 17:01:17 +02:00
fogelito 9bee2f5a25 Types validator 2025-12-21 16:26:55 +02:00
fogelito 21e830146b composer.lock 2025-12-21 10:47:32 +02:00
51 changed files with 1182 additions and 356 deletions
+2 -4
View File
@@ -31,6 +31,7 @@ use Appwrite\URL\URL as URLParser;
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;
@@ -54,10 +55,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\Http\Http;
@@ -3058,7 +3056,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')
+5 -7
View File
@@ -23,6 +23,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;
@@ -37,10 +38,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\Http\Http;
@@ -1136,7 +1134,7 @@ Http::get('/v1/messaging/providers/:providerId/logs')
]
))
->param('providerId', '', new UID(), 'Provider 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')
@@ -2540,7 +2538,7 @@ Http::get('/v1/messaging/topics/:topicId/logs')
]
))
->param('topicId', '', new UID(), 'Topic 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')
@@ -2955,7 +2953,7 @@ Http::get('/v1/messaging/subscribers/:subscriberId/logs')
]
))
->param('subscriberId', '', new UID(), 'Subscriber 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')
@@ -3753,7 +3751,7 @@ Http::get('/v1/messaging/messages/:messageId/logs')
],
))
->param('messageId', '', new UID(), 'Message 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')
+7 -4
View File
@@ -17,8 +17,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\V2 as DocumentsValidator;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\UID;
use Utopia\Http\Http;
@@ -547,9 +548,11 @@ Http::post('/v1/migrations/csv/exports')
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$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(),
);
+2 -2
View File
@@ -21,6 +21,7 @@ use Appwrite\Utopia\Database\Documents\User;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries\Memberships;
use Appwrite\Utopia\Database\Validator\Queries\Teams;
use Appwrite\Utopia\Database\Validator\Queries\Types;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use libphonenumber\NumberParseException;
@@ -48,7 +49,6 @@ use Utopia\Database\Validator\Key;
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\Http\Http;
@@ -1436,7 +1436,7 @@ Http::get('/v1/teams/:teamId/logs')
]
))
->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')
+2 -3
View File
@@ -25,6 +25,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;
@@ -56,8 +57,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\Http\Http;
@@ -933,7 +932,7 @@ Http::get('/v1/users/:userId/logs')
]
))
->param('userId', '', new UID(), 'User 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')
+4
View File
@@ -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;
@@ -931,6 +932,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', ''));
+1 -1
View File
@@ -46,7 +46,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.8.1';
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';
+56
View File
@@ -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')]),
@@ -104,6 +108,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()]),
@@ -119,6 +127,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()]),
@@ -133,6 +145,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']),
@@ -148,6 +164,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()]),
@@ -162,6 +182,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()]),
@@ -176,6 +200,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),
@@ -189,6 +217,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()]),
@@ -203,6 +235,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()]),
@@ -217,6 +253,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()]),
@@ -231,6 +271,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()]),
@@ -245,6 +289,10 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
if (empty($document->getSequence())) {
return [];
}
$resourceType = match ($document->getCollection()) {
'functions' => ['function'],
'sites' => ['site'],
@@ -331,6 +379,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()]),
@@ -345,6 +397,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', [
+1 -2
View File
@@ -1144,8 +1144,7 @@ Http::setResource('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;
$limit = Query::getLimitQuery($queries, APP_LIMIT_LIST_DEFAULT);
return $complexity * $limit;
};
+2 -2
View File
@@ -52,7 +52,7 @@
"utopia-php/cache": "1.0.*",
"utopia-php/cli": "0.22.*",
"utopia-php/config": "1.*",
"utopia-php/database": "5.*",
"utopia-php/database": "dev-joins8 as 5.0.0",
"utopia-php/detector": "0.2.*",
"utopia-php/domains": "1.*",
"utopia-php/emails": "0.6.*",
@@ -64,7 +64,7 @@
"utopia-php/locale": "0.8.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.20.*",
"utopia-php/migration": "1.5.*",
"utopia-php/migration": "dev-joins as 1.5.0",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "1.*",
"utopia-php/preloader": "0.2.*",
Generated
+47 -31
View File
@@ -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": "367aaf665ff75a13d5d7c8b3bedb89a9",
"content-hash": "f8cc09a687dfc6a38921e80b7a3dc6de",
"packages": [
{
"name": "adhocore/jwt",
@@ -3797,16 +3797,16 @@
},
{
"name": "utopia-php/database",
"version": "5.0.2",
"version": "dev-joins8",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "aa80f86f5bf3f0d8c13abd3213bf1649f542d366"
"reference": "2eb66b29e093810d3f5dde15536398c186093a83"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/aa80f86f5bf3f0d8c13abd3213bf1649f542d366",
"reference": "aa80f86f5bf3f0d8c13abd3213bf1649f542d366",
"url": "https://api.github.com/repos/utopia-php/database/zipball/2eb66b29e093810d3f5dde15536398c186093a83",
"reference": "2eb66b29e093810d3f5dde15536398c186093a83",
"shasum": ""
},
"require": {
@@ -3814,10 +3814,10 @@
"ext-mongodb": "*",
"ext-pdo": "*",
"php": ">=8.4",
"utopia-php/cache": "1.0.*",
"utopia-php/cache": "1.*",
"utopia-php/framework": "0.33.*",
"utopia-php/mongo": "0.11.*",
"utopia-php/pools": "1.0.*"
"utopia-php/mongo": "1.*",
"utopia-php/pools": "1.*"
},
"require-dev": {
"fakerphp/faker": "1.23.*",
@@ -3849,9 +3849,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/5.0.2"
"source": "https://github.com/utopia-php/database/tree/joins8"
},
"time": "2026-02-08T05:23:42+00:00"
"time": "2026-02-12T09:52:35+00:00"
},
{
"name": "utopia-php/detector",
@@ -4464,16 +4464,16 @@
},
{
"name": "utopia-php/migration",
"version": "1.5.2",
"version": "dev-joins",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "93904948f6dd07491821615fd9b9acbcaadec12e"
"reference": "ac60b6d3a341159116b58533c36d7410f212229a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/93904948f6dd07491821615fd9b9acbcaadec12e",
"reference": "93904948f6dd07491821615fd9b9acbcaadec12e",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/ac60b6d3a341159116b58533c36d7410f212229a",
"reference": "ac60b6d3a341159116b58533c36d7410f212229a",
"shasum": ""
},
"require": {
@@ -4482,7 +4482,7 @@
"ext-openssl": "*",
"halaxa/json-machine": "^1.2",
"php": ">=8.1",
"utopia-php/database": "5.*",
"utopia-php/database": "dev-joins8 as 5.0.0",
"utopia-php/dsn": "0.2.*",
"utopia-php/storage": "0.18.*"
},
@@ -4513,22 +4513,22 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/1.5.2"
"source": "https://github.com/utopia-php/migration/tree/joins"
},
"time": "2026-02-11T06:19:35+00:00"
"time": "2026-02-11T13:25:03+00:00"
},
{
"name": "utopia-php/mongo",
"version": "0.11.0",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/mongo.git",
"reference": "34bc0cda8ea368cde68702a6fffe2c3ac625398e"
"reference": "45bedf36c2c946ec7a0a3e59b9f12f772de0b01d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/mongo/zipball/34bc0cda8ea368cde68702a6fffe2c3ac625398e",
"reference": "34bc0cda8ea368cde68702a6fffe2c3ac625398e",
"url": "https://api.github.com/repos/utopia-php/mongo/zipball/45bedf36c2c946ec7a0a3e59b9f12f772de0b01d",
"reference": "45bedf36c2c946ec7a0a3e59b9f12f772de0b01d",
"shasum": ""
},
"require": {
@@ -4574,9 +4574,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/mongo/issues",
"source": "https://github.com/utopia-php/mongo/tree/0.11.0"
"source": "https://github.com/utopia-php/mongo/tree/1.0.0"
},
"time": "2025-10-20T11:11:23+00:00"
"time": "2026-02-12T05:54:06+00:00"
},
{
"name": "utopia-php/platform",
@@ -5389,16 +5389,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "1.8.28",
"version": "1.8.29",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "4762aa017b28a3a7e4e53da6e193d0c196ec3fd4"
"reference": "eae24f143aa0e352acc8ae8975a0692fd77d2ec3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/4762aa017b28a3a7e4e53da6e193d0c196ec3fd4",
"reference": "4762aa017b28a3a7e4e53da6e193d0c196ec3fd4",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/eae24f143aa0e352acc8ae8975a0692fd77d2ec3",
"reference": "eae24f143aa0e352acc8ae8975a0692fd77d2ec3",
"shasum": ""
},
"require": {
@@ -5434,9 +5434,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.8.28"
"source": "https://github.com/appwrite/sdk-generator/tree/1.8.29"
},
"time": "2026-02-11T10:35:18+00:00"
"time": "2026-02-12T07:22:23+00:00"
},
{
"name": "doctrine/annotations",
@@ -8893,9 +8893,25 @@
"time": "2024-03-07T20:33:40+00:00"
}
],
"aliases": [],
"aliases": [
{
"package": "utopia-php/database",
"version": "dev-joins8",
"alias": "5.0.0",
"alias_normalized": "5.0.0.0"
},
{
"package": "utopia-php/migration",
"version": "dev-joins",
"alias": "1.5.0",
"alias_normalized": "1.5.0.0"
}
],
"minimum-stability": "stable",
"stability-flags": {},
"stability-flags": {
"utopia-php/database": 20,
"utopia-php/migration": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
+1
View File
@@ -99,6 +99,7 @@ services:
- ./src:/usr/src/code/src
- ./dev:/usr/src/code/dev
# - ./vendor/utopia-php/framework:/usr/src/code/vendor/utopia-php/framework
- ./vendor/utopia-php/database:/usr/src/code/vendor/utopia-php/database
depends_on:
- ${_APP_DB_HOST:-mariadb}
- redis
+1 -1
View File
@@ -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
+1 -1
View File
@@ -6,7 +6,7 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
stopOnFailure="true"
>
<extensions>
<extension class="Appwrite\Tests\TestHook" />
+2 -1
View File
@@ -46,7 +46,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'),
+2 -2
View File
@@ -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;
}
@@ -448,12 +448,20 @@ class Create extends Action
}
try {
$dbForProject->withPreserveDates(
fn () => $dbForProject->createDocuments(
$results = [];
$dbForProject->withPreserveDates(function () use (&$results, $dbForProject, $database, $collection, $documents) {
$dbForProject->createDocuments(
'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(),
$documents,
)
);
onNext: function ($doc) use (&$results) {
$results[] = $doc;
}
);
});
$documents = $results;
} catch (DuplicateException) {
throw new Exception($this->getDuplicateException(), params: [$documentId]);
} catch (NotFoundException) {
@@ -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', '', 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('locale')
@@ -99,6 +99,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);
}
$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', '', 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')
@@ -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', '', 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')
@@ -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', '', 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')
@@ -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('tableId', '', new UID(), 'Table 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,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('tableId', '', new UID(), 'Table ID.')
->param('rowId', '', new UID(), 'Row 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')
@@ -12,7 +12,6 @@ use Appwrite\Utopia\Database\Validator\Queries\Projects;
use Appwrite\Utopia\Request;
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;
@@ -109,7 +108,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.");
@@ -122,73 +122,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);
}
}
@@ -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'),
]);
});
@@ -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;
@@ -97,7 +95,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')
+85 -31
View File
@@ -353,7 +353,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(
@@ -390,12 +393,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,
@@ -426,27 +433,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);
}
}
@@ -753,13 +770,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);
@@ -792,7 +815,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'),
@@ -803,14 +826,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);
}
};
@@ -849,13 +877,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);
}
/**
@@ -877,14 +911,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);
@@ -988,12 +1030,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
@@ -1074,12 +1122,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
+2 -1
View File
@@ -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\V2 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;
}
}
+37 -8
View File
@@ -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,
]);
}
}
+4 -1
View File
@@ -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) {
@@ -83,7 +83,7 @@ class Document extends Any
$document->removeAttribute('$collection');
$document->removeAttribute('$tenant');
if (!$document->isEmpty()) {
if ($document->offsetExists('$sequence')) {
$document->setAttribute('$sequence', (int)$document->getAttribute('$sequence', 0));
}
+4 -1
View File
@@ -82,7 +82,10 @@ class Row extends Any
{
$document->removeAttribute('$collection');
$document->removeAttribute('$tenant');
$document->setAttribute('$sequence', (int)$document->getAttribute('$sequence', 0));
if ($document->offsetExists('$sequence')) {
$document->setAttribute('$sequence', (int)$document->getAttribute('$sequence', 0));
}
foreach ($document->getAttributes() as $column) {
if (\is_array($column)) {
+8
View File
@@ -185,6 +185,14 @@ class Client
$responseHeaders = [];
$cookies = [];
if (isset($params['queries'])) {
foreach ($params['queries'] as $value) {
if (!is_string($value)) {
throw new Exception('Queries must be converted to strings');
}
}
}
$query = match ($headers['content-type']) {
'application/json' => json_encode($params),
'multipart/form-data' => $this->flatten($params),
@@ -411,7 +411,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']);
@@ -1576,7 +1576,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']);
@@ -1619,6 +1619,7 @@ trait DatabasesBase
'title' => 'Spider-Man: Far From Home',
'releaseYear' => 2019,
'birthDay' => null,
'duration' => null,
'actors' => [
'Tom Holland',
'Zendaya Maree Stoermer',
@@ -1677,6 +1678,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']);
@@ -1976,7 +1978,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(),
],
]);
@@ -2014,7 +2017,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(),
],
]);
@@ -2052,7 +2056,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']);
@@ -2211,7 +2216,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']);
@@ -2422,30 +2429,59 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['title', 'releaseYear', '$id'])->toString(),
Query::select('title')->toString(),
Query::select('releaseYear')->toString(),
Query::select('$id')->toString(),
Query::select('$sequence')->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($document['title'], $response['body']['title']);
$this->assertEquals($document['releaseYear'], $response['body']['releaseYear']);
$this->assertEquals($document['$sequence'], $response['body']['$sequence']);
$this->assertArrayNotHasKey('birthDay', $response['body']);
$sequence = $response['body']['$sequence'];
$this->assertArrayHasKey('$sequence', $response['body']);
$sequence = (string)$response['body']['$sequence'];
// Query by sequence
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$collectionId'] . '/documents/' . $document['$id'], array_merge([
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$collectionId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('$sequence', [$sequence])
Query::equal('$sequence', [$sequence])->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($document['title'], $response['body']['title']);
$this->assertEquals($document['releaseYear'], $response['body']['releaseYear']);
$this->assertTrue(array_key_exists('$sequence', $response['body']));
$this->assertEquals(1, $response['body']['total']);
$this->assertEquals($document['title'], $response['body']['documents'][0]['title']);
$this->assertEquals($document['releaseYear'], $response['body']['documents'][0]['releaseYear']);
$this->assertEquals($document['$sequence'], $response['body']['documents'][0]['$sequence']);
/**
* Use specific X-Appwrite-Response-Format 1.8.0
*/
$response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$collectionId'] . '/documents', 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('title', $response['body']['documents'][0]);
$this->assertArrayNotHasKey('birthDay', $response['body']['documents'][0]);
$this->assertArrayHasKey('$sequence', $response['body']['documents'][0]);
$this->assertArrayHasKey('$id', $response['body']['documents'][0]);
$this->assertArrayHasKey('$createdAt', $response['body']['documents'][0]);
$this->assertArrayHasKey('$updatedAt', $response['body']['documents'][0]);
$this->assertArrayHasKey('$permissions', $response['body']['documents'][0]);
}
/**
@@ -5122,7 +5158,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(),
],
]);
@@ -5136,7 +5173,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(),
],
]);
@@ -5294,7 +5331,8 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['*', 'libraries.*'])->toString()
Query::select('*')->toString(),
Query::select('libraries.*')->toString()
]
]);
@@ -5308,7 +5346,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()
]
]);
@@ -5462,7 +5500,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()
]
]);
@@ -5478,7 +5518,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()
]
]);
@@ -5623,7 +5666,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()
]
]);
@@ -5641,7 +5686,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()
]
]);
@@ -5671,7 +5719,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(),
@@ -5691,7 +5740,7 @@ trait DatabasesBase
'queries' => [
Query::isNotNull('$id')->toString(),
Query::isNull('fullName')->toString(),
Query::select(['fullName'])->toString(),
Query::select('fullName')->toString(),
],
]);
@@ -5714,7 +5763,7 @@ trait DatabasesBase
], $this->getHeaders()), [
'queries' => [
Query::equal('fullName', ['Stevie Wonder'])->toString(),
Query::select(['fullName'])->toString(),
Query::select('fullName')->toString(),
],
]);
@@ -5728,7 +5777,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']['documents'][0];
@@ -5742,7 +5792,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(),
],
]);
@@ -5872,7 +5923,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'])
@@ -7355,7 +7407,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']);
@@ -7410,7 +7465,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()
]
@@ -7559,7 +7615,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']);
@@ -7693,7 +7750,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']);
@@ -7818,7 +7876,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']);
@@ -7943,7 +8001,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']);
@@ -94,6 +94,34 @@ class DatabasesConsoleClientTest extends Scope
$this->assertEquals(201, $tvShows['headers']['status-code']);
$this->assertEquals($tvShows['body']['name'], 'TvShows');
/**
* Check select queries are disabled in Base class
*/
$list = $this->client->call(Client::METHOD_GET, '/databases', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select('name')->toString(),
]
]);
$this->assertEquals(400, $list['headers']['status-code']);
$this->assertEquals('Invalid `queries` param: Query method select not allowed', $list['body']['message']);
/**
* Check allowed attribute filters
*/
$list = $this->client->call(Client::METHOD_GET, '/databases', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('enabled', [true])->toString(),
]
]);
$this->assertEquals(400, $list['headers']['status-code']);
$this->assertEquals('Invalid `queries` param: Invalid query: Attribute not found in schema: enabled', $list['body']['message']);
return ['moviesId' => $movies['body']['$id'], 'databaseId' => $databaseId, 'tvShowsId' => $tvShows['body']['$id']];
}
@@ -3830,7 +3830,7 @@ class DatabasesCustomServerTest extends Scope
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'queries' => [
Query::select(['new_level_2.*'])->toString()
Query::select('new_level_2.*')->toString()
]
]);
@@ -3944,7 +3944,7 @@ class DatabasesCustomServerTest extends Scope
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'queries' => [
Query::select(['new_level_2.*'])->toString()
Query::select('new_level_2.*')->toString()
]
]);
@@ -4058,7 +4058,7 @@ class DatabasesCustomServerTest extends Scope
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'queries' => [
Query::select(['new_level_2.*'])->toString()
Query::select('new_level_2.*')->toString()
]
]);
@@ -4073,7 +4073,8 @@ class DatabasesCustomServerTest extends Scope
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'queries' => [
Query::select(['*', 'level1.*'])->toString()
Query::select('*')->toString(),
Query::select('level1.*')->toString()
]
]);
@@ -4176,7 +4177,7 @@ class DatabasesCustomServerTest extends Scope
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'queries' => [
Query::select(['new_level_2.*'])->toString()
Query::select('new_level_2.*')->toString()
]
]);
@@ -4191,7 +4192,8 @@ class DatabasesCustomServerTest extends Scope
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'queries' => [
Query::select(['*', 'level1.*'])->toString()
Query::select('*')->toString(),
Query::select('level1.*')->toString()
]
]);
@@ -411,7 +411,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']);
@@ -1534,7 +1534,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']);
@@ -1577,6 +1577,7 @@ trait DatabasesBase
'title' => 'Spider-Man: Far From Home',
'releaseYear' => 2019,
'birthDay' => null,
'duration' => null,
'actors' => [
'Tom Holland',
'Zendaya Maree Stoermer',
@@ -1951,7 +1952,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(),
],
]);
@@ -1989,7 +1991,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(),
],
]);
@@ -2027,7 +2030,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()
],
]);
@@ -2187,7 +2191,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, $rows['body']['total']);
@@ -2350,14 +2356,59 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['title', 'releaseYear', '$id'])->toString(),
Query::select('title')->toString(),
Query::select('releaseYear')->toString(),
Query::select('$id')->toString(),
Query::select('$sequence')->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($row['title'], $response['body']['title']);
$this->assertEquals($row['releaseYear'], $response['body']['releaseYear']);
$this->assertEquals($row['$sequence'], $response['body']['$sequence']);
$this->assertArrayNotHasKey('birthDay', $response['body']);
$this->assertArrayHasKey('$sequence', $response['body']);
$sequence = (string)$response['body']['$sequence'];
// Query by sequence
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $row['$tableId'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::equal('$sequence', [$sequence])->toString(),
],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, $response['body']['total']);
$this->assertEquals($row['title'], $response['body']['rows'][0]['title']);
$this->assertEquals($row['releaseYear'], $response['body']['rows'][0]['releaseYear']);
$this->assertEquals($row['$sequence'], $response['body']['rows'][0]['$sequence']);
/**
* Use specific X-Appwrite-Response-Format 1.8.0
*/
$response = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $row['$tableId'] . '/rows', 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('title', $response['body']['rows'][0]);
$this->assertArrayNotHasKey('birthDay', $response['body']['rows'][0]);
$this->assertArrayHasKey('$sequence', $response['body']['rows'][0]);
$this->assertArrayHasKey('$id', $response['body']['rows'][0]);
$this->assertArrayHasKey('$createdAt', $response['body']['rows'][0]);
$this->assertArrayHasKey('$updatedAt', $response['body']['rows'][0]);
$this->assertArrayHasKey('$permissions', $response['body']['rows'][0]);
}
/**
@@ -5038,7 +5089,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(),
],
]);
@@ -5052,7 +5104,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(),
],
]);
@@ -5210,7 +5262,8 @@ trait DatabasesBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['*', 'libraries.*'])->toString()
Query::select('*')->toString(),
Query::select('libraries.*')->toString()
]
]);
@@ -5224,7 +5277,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()
]
]);
@@ -5378,7 +5431,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()
]
]);
@@ -5394,7 +5449,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()
]
]);
@@ -5539,7 +5597,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()
]
]);
@@ -5557,7 +5617,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()
]
]);
@@ -5587,7 +5650,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(),
@@ -5607,7 +5671,7 @@ trait DatabasesBase
'queries' => [
Query::isNotNull('$id')->toString(),
Query::isNull('fullName')->toString(),
Query::select(['fullName'])->toString(),
Query::select('fullName')->toString(),
],
]);
@@ -5630,7 +5694,7 @@ trait DatabasesBase
], $this->getHeaders()), [
'queries' => [
Query::equal('fullName', ['Stevie Wonder'])->toString(),
Query::select(['fullName'])->toString(),
Query::select('fullName')->toString(),
],
]);
@@ -5644,7 +5708,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(),
],
]);
$row = $response['body']['rows'][0];
@@ -5658,7 +5723,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(),
],
]);
@@ -5788,7 +5854,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'])
@@ -5925,7 +5992,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()
@@ -9065,7 +9133,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']);
$this->assertCount(3, $response['body']['rows']);
@@ -9115,7 +9186,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()
]
@@ -16,6 +16,270 @@ class DatabasesConsoleClientTest extends Scope
use ProjectCustom;
use SideConsole;
public function testJoins(): void
{
// todo: Move to Base class
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'databaseId' => 'joins',
'name' => 'Joins'
]);
$this->assertNotEmpty($database['body']['$id']);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('Joins', $database['body']['name']);
$this->assertEquals('tablesdb', $database['body']['type']);
$databaseId = $database['body']['$id'];
$users = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => 'users',
'name' => 'users',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $users['headers']['status-code']);
$this->assertEquals($users['body']['name'], 'users');
$sessions = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'tableId' => 'sessions',
'name' => 'sessions',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::user($this->getUser()['$id'])),
],
]);
$this->assertEquals(201, $sessions['headers']['status-code']);
$this->assertEquals($sessions['body']['name'], 'sessions');
/**
* Create columns users
*/
$username = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $users['body']['$id'] . '/columns/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'username',
'size' => 256,
'required' => false,
]);
$this->assertEquals('username', $username['body']['key']);
$age = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $users['body']['$id'] . '/columns/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'age',
'required' => false,
'min' => 0,
'max' => 100,
]);
$this->assertEquals('age', $age['body']['key']);
/**
* Create columns sessions
*/
$userId = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $sessions['body']['$id'] . '/columns/integer', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'userId',
'required' => false,
]);
$this->assertEquals('userId', $userId['body']['key']);
sleep(2);
$abraham = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $users['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'username' => 'Abraham',
'age' => 50,
],
'permissions' => [
//Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $abraham['headers']['status-code']);
$this->assertEquals('Abraham', $abraham['body']['username']);
$this->assertEquals(50, $abraham['body']['age']);
$this->assertEquals(1, $abraham['body']['$sequence']);
$bill = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $users['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'username' => 'Bill',
'age' => 40,
],
'permissions' => [
//Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $bill['headers']['status-code']);
$this->assertEquals('Bill', $bill['body']['username']);
$this->assertEquals(40, $bill['body']['age']);
$this->assertEquals(2, $bill['body']['$sequence']);
$session1 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $sessions['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'userId' => $abraham['body']['$sequence'],
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals($abraham['body']['$sequence'], $session1['body']['userId']);
$this->assertEquals(201, $session1['headers']['status-code']);
$session2 = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $sessions['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'rowId' => ID::unique(),
'data' => [
'userId' => $bill['body']['$sequence'],
],
'permissions' => [
Permission::read(Role::user($this->getUser()['$id'])),
]
]);
$this->assertEquals(201, $session2['headers']['status-code']);
$this->assertEquals($bill['body']['$sequence'], $session2['body']['userId']);
/**
* Simple join query
*/
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $users['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::join($sessions['body']['$id'], 'B', [
Query::relationEqual('', '$sequence', 'B', 'userId')
])->toString(),
Query::orderDesc('username', '')->toString(),
],
]);
$this->assertEquals(2, $rows['body']['total']);
$this->assertEquals('Bill', $rows['body']['rows'][0]['username']);
$this->assertEquals(40, $rows['body']['rows'][0]['age']);
$this->assertEquals('Abraham', $rows['body']['rows'][1]['username']);
$this->assertArrayHasKey('$id', $rows['body']['rows'][0]);
$this->assertArrayHasKey('$sequence', $rows['body']['rows'][0]);
$this->assertArrayHasKey('$createdAt', $rows['body']['rows'][0]);
$this->assertArrayHasKey('$updatedAt', $rows['body']['rows'][0]);
$this->assertArrayHasKey('$permissions', $rows['body']['rows'][0]);
$this->assertArrayNotHasKey('userId', $rows['body']['rows'][0]);
/**
* Simple join query with select queries
*/
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $users['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select('username', '')->toString(),
Query::select('userId', 'B')->toString(),
Query::join($sessions['body']['$id'], 'B', [
Query::relationEqual('', '$sequence', 'B', 'userId')
])->toString(),
Query::orderDesc('username', '')->toString(),
],
]);
$this->assertEquals(2, $rows['body']['total']);
$this->assertEquals('Bill', $rows['body']['rows'][0]['username']);
$this->assertEquals('Abraham', $rows['body']['rows'][1]['username']);
$this->assertEquals($bill['body']['$sequence'], $rows['body']['rows'][0]['userId']);
$this->assertEquals($abraham['body']['$sequence'], $rows['body']['rows'][1]['userId']);
$this->assertArrayNotHasKey('age', $rows['body']['rows'][0]);
$this->assertArrayNotHasKey('$id', $rows['body']['rows'][0]);
$this->assertArrayNotHasKey('$sequence', $rows['body']['rows'][0]);
$this->assertArrayNotHasKey('$createdAt', $rows['body']['rows'][0]);
$this->assertArrayNotHasKey('$updatedAt', $rows['body']['rows'][0]);
$this->assertArrayNotHasKey('$permissions', $rows['body']['rows'][0]);
/**
* Join query + equal query
*/
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $users['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select('*', 'B')->toString(),
Query::equal('userId', [$bill['body']['$sequence']], 'B')->toString(),
Query::join($sessions['body']['$id'], 'B', [
Query::relationEqual('', '$sequence', 'B', 'userId')
])->toString(),
],
]);
$this->assertEquals(1, $rows['body']['total']);
$this->assertEquals($bill['body']['$sequence'], $rows['body']['rows'][0]['userId']);
$this->assertArrayNotHasKey('username', $rows['body']['rows'][0]);
/**
* Right join with on equal query in ON clause
*/
$rows = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $users['body']['$id'] . '/rows', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select('username', '')->toString(),
Query::select('userId', 'B')->toString(),
Query::equal('username', ['dsdsds'])->toString(),
Query::rightJoin($sessions['body']['$id'], 'B', [
Query::relationEqual('', '$sequence', 'B', 'userId'),
Query::equal('userId', [$bill['body']['$sequence']], 'B')
])->toString(),
Query::orderAsc('userId')
],
]);
var_dump($rows);
$this->assertEquals(2, $rows['body']['total']);
$this->assertEquals(null, $rows['body']['rows'][0]['username']);
$this->assertEquals($abraham['body']['$sequence'], $rows['body']['rows'][0]['userId']);
$this->assertEquals('Bill', $rows['body']['rows'][1]['username']);
$this->assertEquals($bill['body']['$sequence'], $rows['body']['rows'][1]['userId']);
$this->assertEquals('shmuel123', 'fogel');
}
public function testCreateTable(): array
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([
@@ -3759,7 +3759,7 @@ class DatabasesCustomServerTest extends Scope
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'queries' => [
Query::select(['new_level_2.*'])->toString()
Query::select('new_level_2.*')->toString()
]
]);
@@ -3873,7 +3873,7 @@ class DatabasesCustomServerTest extends Scope
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'queries' => [
Query::select(['new_level_2.*'])->toString()
Query::select('new_level_2.*')->toString()
]
]);
@@ -3987,7 +3987,7 @@ class DatabasesCustomServerTest extends Scope
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'queries' => [
Query::select(['new_level_2.*'])->toString()
Query::select('new_level_2.*')->toString()
]
]);
@@ -4002,7 +4002,8 @@ class DatabasesCustomServerTest extends Scope
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'queries' => [
Query::select(['*', 'level1.*'])->toString()
Query::select('*')->toString(),
Query::select('level1.*')->toString()
]
]);
@@ -4105,7 +4106,7 @@ class DatabasesCustomServerTest extends Scope
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'queries' => [
Query::select(['new_level_2.*'])->toString()
Query::select('new_level_2.*')->toString()
]
]);
@@ -4120,7 +4121,8 @@ class DatabasesCustomServerTest extends Scope
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'queries' => [
Query::select(['*', 'level1.*'])->toString()
Query::select('*')->toString(),
Query::select('level1.*')->toString()
]
]);
@@ -873,7 +873,7 @@ class FunctionsCustomServerTest extends Scope
$deployments = $this->listDeployments($functionId, [
'queries' => [
Query::select(['status'])->toString(),
Query::select('status')->toString(),
],
]);
@@ -886,7 +886,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);
@@ -433,6 +433,42 @@ 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"]}',
]
]);
$this->assertEquals(200, $list['headers']['status-code']);
$this->assertEquals(5, $list['body']['total']);
$project = $list['body']['projects'][0];
$this->assertEquals('Project Test', $project['name']);
$this->assertArrayHasKey('$id', $project);
$this->assertArrayHasKey('name', $project);
$this->assertArrayNotHasKey('platforms', $project);
return $data;
}
@@ -473,7 +509,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(),
],
]);
@@ -502,7 +539,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(),
],
]);
@@ -533,7 +575,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(),
],
]);
@@ -564,7 +608,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(),
],
]);
@@ -594,7 +639,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(),
],
]);
@@ -623,7 +670,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(),
],
]);
@@ -652,7 +702,7 @@ class ProjectsConsoleClientTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
Query::select(['*'])->toString(),
Query::select('*')->toString(),
],
]);
@@ -682,7 +732,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(),
],
]);
@@ -1058,7 +1058,7 @@ class SitesCustomServerTest extends Scope
$deployments = $this->listDeployments($siteId, [
'queries' => [
Query::select(['status'])->toString(),
Query::select('status')->toString(),
],
]);
@@ -1071,7 +1071,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);
+1 -1
View File
@@ -453,7 +453,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);
@@ -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);