1
0
Fork 0
mirror of synced 2024-06-11 07:14:51 +12:00

Update database query syntax

This commit is contained in:
Steven 2022-08-11 23:53:52 +00:00
parent 4d9c37f2db
commit 610ec1a6a4
25 changed files with 552 additions and 423 deletions

View file

@ -6,8 +6,11 @@ use Appwrite\Auth\Phone;
use Appwrite\Auth\Validator\Password;
use Appwrite\Auth\Validator\Phone as ValidatorPhone;
use Appwrite\Detector\Detector;
use Appwrite\Event\Audit;
use Appwrite\Event\Event;
use Appwrite\Event\Mail;
use Appwrite\Event\Phone as EventPhone;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\Host;
use Appwrite\Network\Validator\URL;
@ -15,13 +18,11 @@ use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Stats\Stats;
use Appwrite\Template\Template;
use Appwrite\URL\URL as URLParser;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Database\Validator\CustomId;
use MaxMind\Db\Reader;
use Utopia\App;
use Appwrite\Event\Audit;
use Appwrite\Event\Phone as EventPhone;
use Utopia\Audit\Audit as EventAudit;
use Utopia\Config\Config;
use Utopia\Database\Database;
@ -32,7 +33,6 @@ use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\UID;
use Utopia\Locale\Locale;
use Appwrite\Extend\Exception;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Range;
@ -165,7 +165,8 @@ App::post('/v1/account/sessions/email')
$protocol = $request->getProtocol();
$profile = $dbForProject->findOne('users', [
new Query('email', Query::TYPE_EQUAL, [$email])]);
Query::equal('email', [$email]),
]);
if (!$profile || !Auth::passwordVerify($password, $profile->getAttribute('password'))) {
throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS); // Wrong password or username
@ -448,8 +449,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
}
$user = ($user->isEmpty()) ? $dbForProject->findOne('sessions', [ // Get user by provider id
new Query('provider', QUERY::TYPE_EQUAL, [$provider]),
new Query('providerUid', QUERY::TYPE_EQUAL, [$oauth2ID]),
Query::equal('provider', [$provider]),
Query::equal('providerUid', [$oauth2ID]),
]) : $user;
if ($user === false || $user->isEmpty()) { // No user logged in or with OAuth2 provider ID, create new one or connect with account with same email
@ -462,7 +463,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$isVerified = $oauth2->isEmailVerified($accessToken);
$user = $dbForProject->findOne('users', [
new Query('email', Query::TYPE_EQUAL, [$email])]);
Query::equal('email', [$email]),
]);
if ($user === false || $user->isEmpty()) { // Last option -> create the user, generate random password
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
@ -628,7 +630,7 @@ App::post('/v1/account/sessions/magic-url')
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$user = $dbForProject->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]);
$user = $dbForProject->findOne('users', [Query::equal('email', [$email])]);
if (!$user) {
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
@ -869,7 +871,7 @@ App::post('/v1/account/sessions/phone')
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$user = $dbForProject->findOne('users', [new Query('phone', Query::TYPE_EQUAL, [$number])]);
$user = $dbForProject->findOne('users', [Query::equal('phone', [$number])]);
if (!$user) {
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
@ -1954,7 +1956,7 @@ App::post('/v1/account/recovery')
$email = \strtolower($email);
$profile = $dbForProject->findOne('users', [
new Query('email', Query::TYPE_EQUAL, [$email])
Query::equal('email', [$email]),
]);
if (!$profile) {

View file

@ -21,7 +21,7 @@ use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Permissions;
use Utopia\Database\Validator\QueryValidator;
use Utopia\Database\Validator\Query as QueryValidator;
use Utopia\Database\Validator\Structure;
use Utopia\Database\Validator\UID;
use Utopia\Database\Exception\Authorization as AuthorizationException;
@ -248,31 +248,37 @@ App::get('/v1/databases')
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursor', '', new UID(), 'ID of the collection used as the starting point for the query, excluding the collection itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->param('orderType', Database::ORDER_ASC, new WhiteList([Database::ORDER_ASC, Database::ORDER_DESC], true), 'Order result by ' . Database::ORDER_ASC . ' or ' . Database::ORDER_DESC . ' order.', true)
->inject('response')
->inject('dbForProject')
->inject('usage')
->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject, Stats $usage) {
$filterQueries = [];
if (!empty($search)) {
$filterQueries[] = Query::search('search', $search);
}
$queries = [];
$queries[] = Query::limit($limit);
$queries[] = Query::offset($offset);
$queries[] = $orderType === 'ASC' ? Query::orderAsc('') : Query::orderDesc('');
if (!empty($cursor)) {
$cursorDocument = $dbForProject->getDocument('databases', $cursor);
if ($cursorDocument->isEmpty()) {
throw new Exception("Collection '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
}
$queries = [];
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
}
$usage->setParam('databases.read', 1);
$response->dynamic(new Document([
'databases' => $dbForProject->find('databases', $queries, $limit, $offset, [], [$orderType], $cursorDocument ?? null, $cursorDirection),
'total' => $dbForProject->count('databases', $queries, APP_LIMIT_COUNT),
'databases' => $dbForProject->find('databases', \array_merge($filterQueries, $queries)),
'total' => $dbForProject->count('databases', $filterQueries, APP_LIMIT_COUNT),
]), Response::MODEL_DATABASE_LIST);
});
@ -574,7 +580,7 @@ App::get('/v1/databases/:databaseId/collections')
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursor', '', new UID(), 'ID of the collection used as the starting point for the query, excluding the collection itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->param('orderType', Database::ORDER_ASC, new WhiteList([Database::ORDER_ASC, Database::ORDER_DESC], true), 'Order result by ' . Database::ORDER_ASC . ' or ' . Database::ORDER_DESC . ' order.', true)
->inject('response')
->inject('dbForProject')
->inject('usage')
@ -586,18 +592,24 @@ App::get('/v1/databases/:databaseId/collections')
throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND);
}
if (!empty($cursor)) {
$cursorCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $cursor);
$filterQueries = [];
if ($cursorCollection->isEmpty()) {
throw new Exception("Collection '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
if (!empty($search)) {
$filterQueries[] = Query::search('search', $search);
}
$queries = [];
$queries[] = Query::limit($limit);
$queries[] = Query::offset($offset);
$queries[] = $orderType === 'ASC' ? Query::orderAsc('') : Query::orderDesc('');
if (!empty($cursor)) {
$cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $cursor);
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
if ($cursorDocument->isEmpty()) {
throw new Exception("Collection '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
}
$usage
@ -605,8 +617,8 @@ App::get('/v1/databases/:databaseId/collections')
->setParam('databases.collections.read', 1);
$response->dynamic(new Document([
'collections' => $dbForProject->find('database_' . $database->getInternalId(), $queries, $limit, $offset, [], [$orderType], $cursorCollection ?? null, $cursorDirection),
'total' => $dbForProject->count('database_' . $database->getInternalId(), $queries, APP_LIMIT_COUNT),
'collections' => $dbForProject->find('database_' . $database->getInternalId(), \array_merge($filterQueries, $queries)),
'total' => $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT),
]), Response::MODEL_COLLECTION_LIST);
});
@ -1295,7 +1307,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'createDatetimeAttribute')
->label('sdk.description', '/docs/references/databases/create-datetime-attribute.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.code', Response::STATUS_CODE_ACCEPTED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_DATETIME)
->param('databaseId', '', new UID(), 'Database ID.')
@ -1322,6 +1334,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti
'filters' => ['datetime']
]), $response, $dbForProject, $database, $audits, $events, $usage);
$response->setStatusCode(Response::STATUS_CODE_ACCEPTED);
$response->dynamic($attribute, Response::MODEL_ATTRIBUTE_DATETIME);
});
@ -1575,8 +1588,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
}
$count = $dbForProject->count('indexes', [
new Query('collectionInternalId', Query::TYPE_EQUAL, [$collection->getInternalId()]),
new Query('databaseInternalId', Query::TYPE_EQUAL, [$db->getInternalId()])
Query::equal('collectionInternalId', [$collection->getInternalId()]),
Query::equal('databaseInternalId', [$db->getInternalId()])
], 61);
$limit = 64 - MariaDB::getNumberOfDefaultIndexes();
@ -1847,8 +1860,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
->setParam('databaseId', $databaseId)
->setParam('collectionId', $collection->getId())
->setParam('indexId', $index->getId())
->setContext('collection', $collection)
->setContext('database', $db)
->setContext('collection', $collection)
->setContext('database', $db)
->setPayload($response->output($index, Response::MODEL_INDEX))
;
@ -2003,7 +2016,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
->param('cursor', '', new UID(), 'ID of the document used as the starting point for the query, excluding the document itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
->param('orderAttributes', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of attributes used to sort results. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' order attributes are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
->param('orderTypes', [], new ArrayList(new WhiteList(['DESC', 'ASC'], true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of order directions for sorting attribtues. Possible values are DESC for descending order, or ASC for ascending order. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' order types are allowed.', true)
->param('orderTypes', [], new ArrayList(new WhiteList([Database::ORDER_DESC, Database::ORDER_ASC], true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of order directions for sorting attribtues. Possible values are DESC for descending order, or ASC for ascending order. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' order types are allowed.', true)
->inject('response')
->inject('dbForProject')
->inject('usage')
@ -2036,31 +2049,17 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
}
}
$queries = \array_map(function ($query) {
$query = Query::parse($query);
if (\count($query->getValues()) > 100) {
throw new Exception("You cannot use more than 100 query values on attribute '{$query->getAttribute()}'", 400, Exception::GENERAL_QUERY_LIMIT_EXCEEDED);
}
return $query;
$filterQueries = \array_map(function ($query) {
return Query::parse($query);
}, $queries);
if (!empty($orderAttributes)) {
$validator = new OrderAttributes($collection->getAttribute('attributes', []), $collection->getAttribute('indexes', []), true);
if (!$validator->isValid($orderAttributes)) {
throw new Exception($validator->getDescription(), 400, Exception::GENERAL_QUERY_INVALID);
}
$otherQueries = [];
$otherQueries[] = Query::limit($limit);
$otherQueries[] = Query::offset($offset);
foreach ($orderTypes as $i => $orderType) {
$otherQueries[] = $orderType === Database::ORDER_DESC ? Query::orderDesc($orderAttributes[$i] ?? '') : Query::orderAsc($orderAttributes[$i] ?? '');
}
if (!empty($queries)) {
$validator = new QueriesValidator(new QueryValidator($collection->getAttribute('attributes', [])), $collection->getAttribute('indexes', []), true);
if (!$validator->isValid($queries)) {
throw new Exception($validator->getDescription(), 400, Exception::GENERAL_QUERY_INVALID);
}
}
$cursorDocument = null;
if (!empty($cursor)) {
$cursorDocument = $collection->getAttribute('permission') === 'collection'
? Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $cursor))
@ -2069,15 +2068,27 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
if ($cursorDocument->isEmpty()) {
throw new Exception("Document '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
$otherQueries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
}
$allQueries = \array_merge($filterQueries, $otherQueries);
if (!empty($allQueries)) {
$attributes = $collection->getAttribute('attributes', []);
$validator = new QueriesValidator(new QueryValidator($attributes), $attributes, $collection->getAttribute('indexes', []), true);
if (!$validator->isValid($allQueries)) {
throw new Exception($validator->getDescription(), 400, Exception::GENERAL_QUERY_INVALID);
}
}
if ($collection->getAttribute('permission') === 'collection') {
/** @var Document[] $documents */
$documents = Authorization::skip(fn() => $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection));
$total = Authorization::skip(fn() => $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT));
$documents = Authorization::skip(fn () => $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $allQueries));
$total = Authorization::skip(fn () => $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filterQueries, APP_LIMIT_COUNT));
} else {
$documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection);
$total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT);
$documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $allQueries);
$total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
}
/**
@ -2562,9 +2573,11 @@ App::get('/v1/databases/usage')
$period = $periods[$range]['period'];
$requestDocs = $dbForProject->find('stats', [
new Query('period', Query::TYPE_EQUAL, [$period]),
new Query('metric', Query::TYPE_EQUAL, [$metric]),
], $limit, 0, ['time'], [Database::ORDER_DESC]);
Query::equal('period', [$period]),
Query::equal('metric', [$metric]),
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
foreach ($requestDocs as $requestDoc) {
@ -2674,9 +2687,11 @@ App::get('/v1/databases/:databaseId/usage')
$period = $periods[$range]['period'];
$requestDocs = $dbForProject->find('stats', [
new Query('period', Query::TYPE_EQUAL, [$period]),
new Query('metric', Query::TYPE_EQUAL, [$metric]),
], $limit, 0, ['time'], [Database::ORDER_DESC]);
Query::equal('period', [$period]),
Query::equal('metric', [$metric]),
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
foreach ($requestDocs as $requestDoc) {
@ -2787,9 +2802,11 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
$period = $periods[$range]['period'];
$requestDocs = $dbForProject->find('stats', [
new Query('period', Query::TYPE_EQUAL, [$period]),
new Query('metric', Query::TYPE_EQUAL, [$metric]),
], $limit, 0, ['time'], [Database::ORDER_DESC]);
Query::equal('period', [$period]),
Query::equal('metric', [$metric]),
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
foreach ($requestDocs as $requestDoc) {

View file

@ -103,28 +103,34 @@ App::get('/v1/functions')
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this value to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursor', '', new UID(), 'ID of the function used as the starting point for the query, excluding the function itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->param('orderType', Database::ORDER_ASC, new WhiteList([Database::ORDER_ASC, Database::ORDER_DESC], true), 'Order result by ' . Database::ORDER_ASC . ' or ' . Database::ORDER_DESC . ' order.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject) {
if (!empty($cursor)) {
$cursorFunction = $dbForProject->getDocument('functions', $cursor);
$filterQueries = [];
if ($cursorFunction->isEmpty()) {
throw new Exception("Function '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
if (!empty($search)) {
$filterQueries[] = Query::search('search', $search);
}
$queries = [];
$queries[] = Query::limit($limit);
$queries[] = Query::offset($offset);
$queries[] = $orderType === Database::ORDER_ASC ? Query::orderAsc('') : Query::orderDesc('');
if (!empty($cursor)) {
$cursorDocument = $dbForProject->getDocument('functions', $cursor);
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
if ($cursorDocument->isEmpty()) {
throw new Exception("Function '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
}
$response->dynamic(new Document([
'functions' => $dbForProject->find('functions', $queries, $limit, $offset, [], [$orderType], $cursorFunction ?? null, $cursorDirection),
'total' => $dbForProject->count('functions', $queries, APP_LIMIT_COUNT),
'functions' => $dbForProject->find('functions', \array_merge($filterQueries, $queries)),
'total' => $dbForProject->count('functions', $filterQueries, APP_LIMIT_COUNT),
]), Response::MODEL_FUNCTION_LIST);
});
@ -236,9 +242,11 @@ App::get('/v1/functions/:functionId/usage')
$period = $periods[$range]['period'];
$requestDocs = $dbForProject->find('stats', [
new Query('period', Query::TYPE_EQUAL, [$period]),
new Query('metric', Query::TYPE_EQUAL, [$metric]),
], $limit, 0, ['time'], [Database::ORDER_DESC]);
Query::equal('period', [$period]),
Query::equal('metric', [$metric]),
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
foreach ($requestDocs as $requestDoc) {
@ -553,9 +561,9 @@ App::post('/v1/functions/:functionId/deployments')
if ($activate) {
// Remove deploy for all other deployments.
$activeDeployments = $dbForProject->find('deployments', [
new Query('activate', Query::TYPE_EQUAL, [true]),
new Query('resourceId', Query::TYPE_EQUAL, [$functionId]),
new Query('resourceType', Query::TYPE_EQUAL, ['functions'])
Query::equal('activate', [true]),
Query::equal('resourceId', [$functionId]),
Query::equal('resourceType', ['functions'])
]);
foreach ($activeDeployments as $activeDeployment) {
@ -643,7 +651,7 @@ App::get('/v1/functions/:functionId/deployments')
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this value to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursor', '', new UID(), 'ID of the deployment used as the starting point for the query, excluding the deployment itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->param('orderType', Database::ORDER_ASC, new WhiteList([Database::ORDER_ASC, Database::ORDER_DESC], true), 'Order result by ' . Database::ORDER_ASC . ' or ' . Database::ORDER_DESC . ' order.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $functionId, string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject) {
@ -654,25 +662,31 @@ App::get('/v1/functions/:functionId/deployments')
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
}
if (!empty($cursor)) {
$cursorDeployment = $dbForProject->getDocument('deployments', $cursor);
if ($cursorDeployment->isEmpty()) {
throw new Exception("Tag '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
}
$queries = [];
$filterQueries = [];
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
$filterQueries[] = Query::search('search', $search);
}
$queries[] = new Query('resourceId', Query::TYPE_EQUAL, [$function->getId()]);
$queries[] = new Query('resourceType', Query::TYPE_EQUAL, ['functions']);
$filterQueries[] = Query::equal('resourceId', [$function->getId()]);
$filterQueries[] = Query::equal('resourceType', ['functions']);
$results = $dbForProject->find('deployments', $queries, $limit, $offset, [], [$orderType], $cursorDeployment ?? null, $cursorDirection);
$total = $dbForProject->count('deployments', $queries, APP_LIMIT_COUNT);
$queries = [];
$queries[] = Query::limit($limit);
$queries[] = Query::offset($offset);
$queries[] = $orderType === Database::ORDER_ASC ? Query::orderAsc('') : Query::orderDesc('');
if (!empty($cursor)) {
$cursorDocument = $dbForProject->getDocument('deployments', $cursor);
if ($cursorDocument->isEmpty()) {
throw new Exception("Tag '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
}
$results = $dbForProject->find('deployments', \array_merge($filterQueries, $queries));
$total = $dbForProject->count('deployments', $filterQueries, APP_LIMIT_COUNT);
foreach ($results as $result) {
$build = $dbForProject->getDocument('builds', $result->getAttribute('buildId', ''));
@ -996,24 +1010,30 @@ App::get('/v1/functions/:functionId/executions')
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
}
if (!empty($cursor)) {
$cursorExecution = $dbForProject->getDocument('executions', $cursor);
if ($cursorExecution->isEmpty()) {
throw new Exception("Execution '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
}
$queries = [
new Query('functionId', Query::TYPE_EQUAL, [$function->getId()])
$filterQueries = [
Query::equal('functionId', [$function->getId()])
];
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
$filterQueries[] = Query::search('search', $search);
}
$results = $dbForProject->find('executions', $queries, $limit, $offset, [], [Database::ORDER_DESC], $cursorExecution ?? null, $cursorDirection);
$total = $dbForProject->count('executions', $queries, APP_LIMIT_COUNT);
$queries = [];
$queries[] = Query::limit($limit);
$queries[] = Query::offset($offset);
$queries[] = Query::orderDesc('');
if (!empty($cursor)) {
$cursorDocument = $dbForProject->getDocument('executions', $cursor);
if ($cursorDocument->isEmpty()) {
throw new Exception("Execution '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
}
$results = $dbForProject->find('executions', \array_merge($filterQueries, $queries));
$total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
'executions' => $results,

View file

@ -177,27 +177,33 @@ App::get('/v1/projects')
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursor', '', new UID(), 'ID of the project used as the starting point for the query, excluding the project itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->param('orderType', Database::ORDER_ASC, new WhiteList([Database::ORDER_ASC, Database::ORDER_DESC], true), 'Order result by ' . Database::ORDER_ASC . ' or ' . Database::ORDER_DESC . ' order.', true)
->inject('response')
->inject('dbForConsole')
->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForConsole) {
if (!empty($cursor)) {
$cursorProject = $dbForConsole->getDocument('projects', $cursor);
$filterQueries = [];
if ($cursorProject->isEmpty()) {
throw new Exception("Project '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
if (!empty($search)) {
$filterQueries[] = Query::search('search', $search);
}
$queries = [];
$queries[] = Query::limit($limit);
$queries[] = Query::offset($offset);
$queries[] = $orderType === Database::ORDER_ASC ? Query::orderAsc('') : Query::orderDesc('');
if (!empty($cursor)) {
$cursorDocument = $dbForConsole->getDocument('projects', $cursor);
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
if ($cursorDocument->isEmpty()) {
throw new Exception("Project '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
}
$results = $dbForConsole->find('projects', $queries, $limit, $offset, [], [$orderType], $cursorProject ?? null, $cursorDirection);
$total = $dbForConsole->count('projects', $queries, APP_LIMIT_COUNT);
$results = $dbForConsole->find('projects', \array_merge($filterQueries, $queries));
$total = $dbForConsole->count('projects', $filterQueries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
'projects' => $results,
@ -294,9 +300,11 @@ App::get('/v1/projects/:projectId/usage')
$period = $periods[$range]['period'];
$requestDocs = $dbForProject->find('stats', [
new Query('period', Query::TYPE_EQUAL, [$period]),
new Query('metric', Query::TYPE_EQUAL, [$metric]),
], $limit, 0, ['time'], [Database::ORDER_DESC]);
Query::equal('period', [$period]),
Query::equal('metric', [$metric]),
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
foreach ($requestDocs as $requestDoc) {
@ -634,8 +642,9 @@ App::get('/v1/projects/:projectId/webhooks')
}
$webhooks = $dbForConsole->find('webhooks', [
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
], 5000);
Query::equal('projectInternalId', [$project->getInternalId()]),
Query::limit(5000),
]);
$response->dynamic(new Document([
'webhooks' => $webhooks,
@ -666,8 +675,8 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
}
$webhook = $dbForConsole->findOne('webhooks', [
new Query('_uid', Query::TYPE_EQUAL, [$webhookId]),
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
Query::equal('_uid', [$webhookId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
]);
if ($webhook === false || $webhook->isEmpty()) {
@ -708,8 +717,8 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
$security = ($security === '1' || $security === 'true' || $security === 1 || $security === true);
$webhook = $dbForConsole->findOne('webhooks', [
new Query('_uid', Query::TYPE_EQUAL, [$webhookId]),
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
Query::equal('_uid', [$webhookId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
]);
if ($webhook === false || $webhook->isEmpty()) {
@ -754,8 +763,8 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
}
$webhook = $dbForConsole->findOne('webhooks', [
new Query('_uid', Query::TYPE_EQUAL, [$webhookId]),
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
Query::equal('_uid', [$webhookId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
]);
if ($webhook === false || $webhook->isEmpty()) {
@ -792,8 +801,8 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
}
$webhook = $dbForConsole->findOne('webhooks', [
new Query('_uid', Query::TYPE_EQUAL, [$webhookId]),
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
Query::equal('_uid', [$webhookId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
]);
if ($webhook === false || $webhook->isEmpty()) {
@ -875,8 +884,9 @@ App::get('/v1/projects/:projectId/keys')
}
$keys = $dbForConsole->find('keys', [
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()]),
], 5000);
Query::equal('projectInternalId', [$project->getInternalId()]),
Query::limit(5000),
]);
$response->dynamic(new Document([
'keys' => $keys,
@ -907,8 +917,8 @@ App::get('/v1/projects/:projectId/keys/:keyId')
}
$key = $dbForConsole->findOne('keys', [
new Query('_uid', Query::TYPE_EQUAL, [$keyId]),
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
Query::equal('_uid', [$keyId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
]);
if ($key === false || $key->isEmpty()) {
@ -944,8 +954,8 @@ App::put('/v1/projects/:projectId/keys/:keyId')
}
$key = $dbForConsole->findOne('keys', [
new Query('_uid', Query::TYPE_EQUAL, [$keyId]),
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
Query::equal('_uid', [$keyId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
]);
if ($key === false || $key->isEmpty()) {
@ -987,8 +997,8 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
}
$key = $dbForConsole->findOne('keys', [
new Query('_uid', Query::TYPE_EQUAL, [$keyId]),
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
Query::equal('_uid', [$keyId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
]);
if ($key === false || $key->isEmpty()) {
@ -1072,8 +1082,9 @@ App::get('/v1/projects/:projectId/platforms')
}
$platforms = $dbForConsole->find('platforms', [
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
], 5000);
Query::equal('projectId', [$project->getId()]),
Query::limit(5000),
]);
$response->dynamic(new Document([
'platforms' => $platforms,
@ -1104,8 +1115,8 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
}
$platform = $dbForConsole->findOne('platforms', [
new Query('_uid', Query::TYPE_EQUAL, [$platformId]),
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
Query::equal('_uid', [$platformId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
]);
if ($platform === false || $platform->isEmpty()) {
@ -1141,8 +1152,8 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
}
$platform = $dbForConsole->findOne('platforms', [
new Query('_uid', Query::TYPE_EQUAL, [$platformId]),
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
Query::equal('_uid', [$platformId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
]);
if ($platform === false || $platform->isEmpty()) {
@ -1185,8 +1196,8 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
}
$platform = $dbForConsole->findOne('platforms', [
new Query('_uid', Query::TYPE_EQUAL, [$platformId]),
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
Query::equal('_uid', [$platformId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
]);
if ($platform === false || $platform->isEmpty()) {
@ -1225,8 +1236,8 @@ App::post('/v1/projects/:projectId/domains')
}
$document = $dbForConsole->findOne('domains', [
new Query('domain', Query::TYPE_EQUAL, [$domain]),
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()]),
Query::equal('domain', [$domain]),
Query::equal('projectInternalId', [$project->getInternalId()]),
]);
if ($document && !$document->isEmpty()) {
@ -1285,8 +1296,9 @@ App::get('/v1/projects/:projectId/domains')
}
$domains = $dbForConsole->find('domains', [
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
], 5000);
Query::equal('projectInternalId', [$project->getInternalId()]),
Query::limit(5000),
]);
$response->dynamic(new Document([
'domains' => $domains,
@ -1317,8 +1329,8 @@ App::get('/v1/projects/:projectId/domains/:domainId')
}
$domain = $dbForConsole->findOne('domains', [
new Query('_uid', Query::TYPE_EQUAL, [$domainId]),
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
Query::equal('_uid', [$domainId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
]);
if ($domain === false || $domain->isEmpty()) {
@ -1351,8 +1363,8 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
}
$domain = $dbForConsole->findOne('domains', [
new Query('_uid', Query::TYPE_EQUAL, [$domainId]),
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
Query::equal('_uid', [$domainId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
]);
if ($domain === false || $domain->isEmpty()) {
@ -1411,8 +1423,8 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
}
$domain = $dbForConsole->findOne('domains', [
new Query('_uid', Query::TYPE_EQUAL, [$domainId]),
new Query('projectInternalId', Query::TYPE_EQUAL, [$project->getInternalId()])
Query::equal('_uid', [$domainId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
]);
if ($domain === false || $domain->isEmpty()) {

View file

@ -158,27 +158,37 @@ App::get('/v1/storage/buckets')
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('cursor', '', new UID(), 'ID of the bucket used as the starting point for the query, excluding the bucket itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->param('orderType', Database::ORDER_ASC, new WhiteList([Database::ORDER_ASC, Database::ORDER_DESC], true), 'Order result by ' . Database::ORDER_ASC . ' or ' . Database::ORDER_DESC . ' order.', true)
->inject('response')
->inject('dbForProject')
->inject('usage')
->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject, Stats $usage) {
$queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : [];
$filterQueries = [];
if (!empty($search)) {
$filterQueries[] = Query::search('name', $search);
}
$queries = [];
$queries[] = Query::limit($limit);
$queries[] = Query::offset($offset);
$queries[] = $orderType === Database::ORDER_ASC ? Query::orderAsc('') : Query::orderDesc('');
if (!empty($cursor)) {
$cursorBucket = $dbForProject->getDocument('buckets', $cursor);
$cursorDocument = $dbForProject->getDocument('buckets', $cursor);
if ($cursorBucket->isEmpty()) {
if ($cursorDocument->isEmpty()) {
throw new Exception("Bucket '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
}
$usage->setParam('storage.buckets.read', 1);
$response->dynamic(new Document([
'buckets' => $dbForProject->find('buckets', $queries, $limit, $offset, [], [$orderType], $cursorBucket ?? null, $cursorDirection),
'total' => $dbForProject->count('buckets', $queries, APP_LIMIT_COUNT),
'buckets' => $dbForProject->find('buckets', \array_merge($filterQueries, $queries)),
'total' => $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT),
]), Response::MODEL_BUCKET_LIST);
});
@ -678,7 +688,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursor', '', new UID(), 'ID of the file used as the starting point for the query, excluding the file itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->param('orderType', Database::ORDER_ASC, new WhiteList([Database::ORDER_ASC, Database::ORDER_DESC], true), 'Order result by ' . Database::ORDER_ASC . ' or ' . Database::ORDER_DESC . ' order.', true)
->inject('response')
->inject('dbForProject')
->inject('usage')
@ -702,34 +712,36 @@ App::get('/v1/storage/buckets/:bucketId/files')
}
}
$queries = [new Query('bucketId', Query::TYPE_EQUAL, [$bucketId])];
$filterQueries = [];
if ($search) {
$queries[] = [new Query('name', Query::TYPE_SEARCH, [$search])];
}
if (!empty($cursor)) {
if ($bucket->getAttribute('permission') === 'bucket') {
$cursorFile = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $cursor));
} else {
$cursorFile = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $cursor);
}
if ($cursorFile->isEmpty()) {
throw new Exception("File '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
if (!empty($search)) {
$filterQueries[] = Query::search('name', $search);
}
$queries = [];
$queries[] = Query::limit($limit);
$queries[] = Query::offset($offset);
$queries[] = $orderType === Database::ORDER_ASC ? Query::orderAsc('') : Query::orderDesc('');
if (!empty($cursor)) {
if ($bucket->getAttribute('permission') === 'bucket') {
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $cursor));
} else {
$cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $cursor);
}
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
if ($cursorDocument->isEmpty()) {
throw new Exception("File '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
}
if ($bucket->getAttribute('permission') === 'bucket') {
$files = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries, $limit, $offset, [], [$orderType], $cursorFile ?? null, $cursorDirection));
$files = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), \array_merge($filterQueries, $queries)));
$total = Authorization::skip(fn () => $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT));
} else {
$files = $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries, $limit, $offset, [], [$orderType], $cursorFile ?? null, $cursorDirection);
$files = $dbForProject->find('bucket_' . $bucket->getInternalId(), \array_merge($filterQueries, $queries));
$total = $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
}
$usage
@ -739,7 +751,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
$response->dynamic(new Document([
'files' => $files,
'total' => $dbForProject->count('bucket_' . $bucket->getInternalId(), $queries, APP_LIMIT_COUNT),
'total' => $total,
]), Response::MODEL_FILE_LIST);
});
@ -1526,9 +1538,11 @@ App::get('/v1/storage/usage')
$period = $periods[$range]['period'];
$requestDocs = $dbForProject->find('stats', [
new Query('period', Query::TYPE_EQUAL, [$period]),
new Query('metric', Query::TYPE_EQUAL, [$metric]),
], $limit, 0, ['time'], [Database::ORDER_DESC]);
Query::equal('period', [$period]),
Query::equal('metric', [$metric]),
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
foreach ($requestDocs as $requestDoc) {
@ -1635,9 +1649,11 @@ App::get('/v1/storage/:bucketId/usage')
$limit = $periods[$range]['limit'];
$period = $periods[$range]['period'];
$requestDocs = $dbForProject->find('stats', [
new Query('period', Query::TYPE_EQUAL, [$period]),
new Query('metric', Query::TYPE_EQUAL, [$metric]),
], $limit, 0, ['time'], [Database::ORDER_DESC]);
Query::equal('period', [$period]),
Query::equal('metric', [$metric]),
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
foreach ($requestDocs as $requestDoc) {

View file

@ -127,22 +127,28 @@ App::get('/v1/teams')
->inject('dbForProject')
->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject) {
if (!empty($cursor)) {
$cursorTeam = $dbForProject->getDocument('teams', $cursor);
$filterQueries = [];
if ($cursorTeam->isEmpty()) {
throw new Exception("Team '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
if (!empty($search)) {
$filterQueries[] = Query::search('search', $search);
}
$queries = [];
$queries[] = Query::limit($limit);
$queries[] = Query::offset($offset);
$queries[] = $orderType === Database::ORDER_ASC ? Query::orderAsc('') : Query::orderDesc('');
if (!empty($cursor)) {
$cursorDocument = $dbForProject->getDocument('teams', $cursor);
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
if ($cursorDocument->isEmpty()) {
throw new Exception("Team '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
}
$results = $dbForProject->find('teams', $queries, $limit, $offset, [], [$orderType], $cursorTeam ?? null, $cursorDirection);
$total = $dbForProject->count('teams', $queries, APP_LIMIT_COUNT);
$results = $dbForProject->find('teams', \array_merge($filterQueries, $queries));
$total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
'teams' => $results,
@ -237,8 +243,9 @@ App::delete('/v1/teams/:teamId')
}
$memberships = $dbForProject->find('memberships', [
new Query('teamId', Query::TYPE_EQUAL, [$teamId]),
], 2000, 0); // TODO fix members limit
Query::equal('teamId', [$teamId]),
Query::limit(2000), // TODO fix members limit
]);
// TODO delete all members individually from the user object
foreach ($memberships as $membership) {
@ -313,7 +320,7 @@ App::post('/v1/teams/:teamId/memberships')
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
}
$invitee = $dbForProject->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
$invitee = $dbForProject->findOne('users', [Query::equal('email', [$email])]); // Get user by email address
if (empty($invitee)) { // Create new user if no user with same email found
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
@ -452,7 +459,7 @@ App::get('/v1/teams/:teamId/memberships')
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this value to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursor', '', new UID(), 'ID of the membership used as the starting point for the query, excluding the membership itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->param('orderType', Database::ORDER_ASC, new WhiteList([Database::ORDER_ASC, Database::ORDER_DESC], true), 'Order result by ' . Database::ORDER_ASC . ' or ' . Database::ORDER_DESC . ' order.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $teamId, string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject) {
@ -463,33 +470,34 @@ App::get('/v1/teams/:teamId/memberships')
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
}
if (!empty($cursor)) {
$cursorMembership = $dbForProject->getDocument('memberships', $cursor);
if ($cursorMembership->isEmpty()) {
throw new Exception("Membership '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
}
$queries = [new Query('teamId', Query::TYPE_EQUAL, [$teamId])];
$filterQueries = [Query::equal('teamId', [$teamId])];
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
$filterQueries[] = Query::search('search', $search);
}
$otherQueries = [];
$otherQueries[] = Query::limit($limit);
$otherQueries[] = Query::offset($offset);
$otherQueries[] = $orderType === Database::ORDER_ASC ? Query::orderAsc('') : Query::orderDesc('');
if (!empty($cursor)) {
$cursorDocument = $dbForProject->getDocument('memberships', $cursor);
if ($cursorDocument->isEmpty()) {
throw new Exception("Membership '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
$otherQueries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
}
$memberships = $dbForProject->find(
collection: 'memberships',
queries: $queries,
limit: $limit,
offset: $offset,
orderTypes: [$orderType],
cursor: $cursorMembership ?? null,
cursorDirection: $cursorDirection
queries: \array_merge($filterQueries, $otherQueries),
);
$total = $dbForProject->count(
collection:'memberships',
queries: $queries,
collection: 'memberships',
queries: $filterQueries,
max: APP_LIMIT_COUNT
);

View file

@ -106,24 +106,30 @@ App::get('/v1/users')
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursor', '', new UID(), 'ID of the user used as the starting point for the query, excluding the user itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->param('orderType', Database::ORDER_ASC, new WhiteList([Database::ORDER_ASC, Database::ORDER_DESC], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForProject')
->inject('usage')
->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject, Stats $usage) {
if (!empty($cursor)) {
$cursorUser = $dbForProject->getDocument('users', $cursor);
$filterQueries = [];
if ($cursorUser->isEmpty()) {
throw new Exception("User '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
if (!empty($search)) {
$filterQueries[] = Query::search('search', $search);
}
$queries = [];
$queries[] = Query::limit($limit);
$queries[] = Query::offset($offset);
$queries[] = $orderType === Database::ORDER_ASC ? Query::orderAsc('') : Query::orderDesc('');
if (!empty($cursor)) {
$cursorDocument = $dbForProject->getDocument('users', $cursor);
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
if ($cursorDocument->isEmpty()) {
throw new Exception("User '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND);
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
}
$usage
@ -131,8 +137,8 @@ App::get('/v1/users')
;
$response->dynamic(new Document([
'users' => $dbForProject->find('users', $queries, $limit, $offset, [], [$orderType], $cursorUser ?? null, $cursorDirection),
'total' => $dbForProject->count('users', $queries, APP_LIMIT_COUNT),
'users' => $dbForProject->find('users', \array_merge($filterQueries, $queries)),
'total' => $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT),
]), Response::MODEL_USER_LIST);
});
@ -905,9 +911,11 @@ App::get('/v1/users/usage')
$period = $periods[$range]['period'];
$requestDocs = $dbForProject->find('stats', [
new Query('period', Query::TYPE_EQUAL, [$period]),
new Query('metric', Query::TYPE_EQUAL, [$metric]),
], $limit, 0, ['time'], [Database::ORDER_DESC]);
Query::equal('period', [$period]),
Query::equal('metric', [$metric]),
Query::limit($limit),
Query::orderDesc('time'),
]);
$stats[$metric] = [];
foreach ($requestDocs as $requestDoc) {

View file

@ -22,8 +22,8 @@ use Appwrite\Utopia\Response\Filters\V13 as ResponseV13;
use Appwrite\Utopia\Response\Filters\V14 as ResponseV14;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Validator\Hostname;
@ -90,7 +90,7 @@ App::init()
if (!empty($envDomain) && $envDomain !== 'localhost') {
$mainDomain = $envDomain;
} else {
$domainDocument = $dbForConsole->findOne('domains', [], 0, ['_id'], ['ASC']);
$domainDocument = $dbForConsole->findOne('domains', [Query::orderAsc('_id')]);
$mainDomain = $domainDocument ? $domainDocument->getAttribute('domain') : $domain->get();
}
@ -98,7 +98,7 @@ App::init()
Console::warning($domain->get() . ' is not a main domain. Skipping SSL certificate generation.');
} else {
$domainDocument = $dbForConsole->findOne('domains', [
new Query('domain', QUERY::TYPE_EQUAL, [$domain->get()])
Query::equal('domain', [$domain->get()])
]);
if (!$domainDocument) {
@ -173,42 +173,42 @@ App::init()
? null
: '.' . $request->getHostname());
/*
* Response format
*/
$responseFormat = $request->getHeader('x-appwrite-response-format', App::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));
if ($responseFormat) {
switch ($responseFormat) {
case version_compare($responseFormat, '0.11.2', '<='):
Response::setFilter(new ResponseV11());
break;
case version_compare($responseFormat, '0.12.4', '<='):
Response::setFilter(new ResponseV12());
break;
case version_compare($responseFormat, '0.13.4', '<='):
Response::setFilter(new ResponseV13());
break;
case version_compare($responseFormat, '0.14.0', '<='):
Response::setFilter(new ResponseV14());
break;
default:
Response::setFilter(null);
}
} else {
Response::setFilter(null);
}
/*
* Security Headers
*
* As recommended at:
* @see https://www.owasp.org/index.php/List_of_useful_HTTP_headers
*/
if (App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
if ($request->getProtocol() !== 'https') {
if ($request->getMethod() !== Request::METHOD_GET) {
throw new AppwriteException('Method unsupported over HTTP.', 500, AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED);
/*
* Response format
*/
$responseFormat = $request->getHeader('x-appwrite-response-format', App::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));
if ($responseFormat) {
switch ($responseFormat) {
case version_compare($responseFormat, '0.11.2', '<='):
Response::setFilter(new ResponseV11());
break;
case version_compare($responseFormat, '0.12.4', '<='):
Response::setFilter(new ResponseV12());
break;
case version_compare($responseFormat, '0.13.4', '<='):
Response::setFilter(new ResponseV13());
break;
case version_compare($responseFormat, '0.14.0', '<='):
Response::setFilter(new ResponseV14());
break;
default:
Response::setFilter(null);
}
} else {
Response::setFilter(null);
}
/*
* Security Headers
*
* As recommended at:
* @see https://www.owasp.org/index.php/List_of_useful_HTTP_headers
*/
if (App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
if ($request->getProtocol() !== 'https') {
if ($request->getMethod() !== Request::METHOD_GET) {
throw new AppwriteException('Method unsupported over HTTP.', 500, AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED);
}
return $response->redirect('https://' . $request->getHostname() . $request->getURI());
}

View file

@ -45,20 +45,20 @@ App::init()
/*
* Abuse Check
*/
$abuseKeyLabel = $route->getLabel('abuse-key', 'url:{url},ip:{ip}');
$timeLimitArray = [];
$abuseKeyLabel = $route->getLabel('abuse-key', 'url:{url},ip:{ip}');
$timeLimitArray = [];
$abuseKeyLabel = (!is_array($abuseKeyLabel)) ? [$abuseKeyLabel] : $abuseKeyLabel;
foreach ($abuseKeyLabel as $abuseKey) {
$timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject);
$timeLimit
foreach ($abuseKeyLabel as $abuseKey) {
$timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject);
$timeLimit
->setParam('{userId}', $user->getId())
->setParam('{userAgent}', $request->getUserAgent(''))
->setParam('{ip}', $request->getIP())
->setParam('{url}', $request->getHostname() . $route->getPath());
$timeLimitArray[] = $timeLimit;
}
$timeLimitArray[] = $timeLimit;
}
$closestLimit = null;
@ -99,11 +99,11 @@ App::init()
/*
* Background Jobs
*/
$events
$events
->setEvent($route->getLabel('event', ''))
->setProject($project)
->setUser($user)
;
;
$mails
->setProject($project)
@ -184,7 +184,7 @@ App::init()
default:
throw new Exception('Unsupported authentication route', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
}
}
});
App::shutdown()

View file

@ -268,9 +268,10 @@ Database::addFilter(
function (mixed $value, Document $document, Database $database) {
return $database
->find('attributes', [
new Query('collectionInternalId', Query::TYPE_EQUAL, [$document->getInternalId()]),
new Query('databaseInternalId', Query::TYPE_EQUAL, [$document->getAttribute('databaseInternalId')])
], $database->getAttributeLimit(), 0, []);
Query::equal('collectionInternalId', [$document->getInternalId()]),
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
Query::limit($database->getAttributeLimit()),
]);
}
);
@ -282,9 +283,10 @@ Database::addFilter(
function (mixed $value, Document $document, Database $database) {
return $database
->find('indexes', [
new Query('collectionInternalId', Query::TYPE_EQUAL, [$document->getInternalId()]),
new Query('databaseInternalId', Query::TYPE_EQUAL, [$document->getAttribute('databaseInternalId')])
], 64);
Query::equal('collectionInternalId', [$document->getInternalId()]),
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
Query::limit(64),
]);
}
);
@ -296,8 +298,9 @@ Database::addFilter(
function (mixed $value, Document $document, Database $database) {
return $database
->find('platforms', [
new Query('projectInternalId', Query::TYPE_EQUAL, [$document->getInternalId()])
], APP_LIMIT_SUBQUERY);
Query::equal('projectInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
);
@ -309,8 +312,9 @@ Database::addFilter(
function (mixed $value, Document $document, Database $database) {
return $database
->find('domains', [
new Query('projectInternalId', Query::TYPE_EQUAL, [$document->getInternalId()])
], APP_LIMIT_SUBQUERY);
Query::equal('projectInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
);
@ -322,8 +326,9 @@ Database::addFilter(
function (mixed $value, Document $document, Database $database) {
return $database
->find('keys', [
new Query('projectInternalId', Query::TYPE_EQUAL, [$document->getInternalId()])
], APP_LIMIT_SUBQUERY);
Query::equal('projectInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
);
@ -335,8 +340,9 @@ Database::addFilter(
function (mixed $value, Document $document, Database $database) {
return $database
->find('webhooks', [
new Query('projectInternalId', Query::TYPE_EQUAL, [$document->getInternalId()])
], APP_LIMIT_SUBQUERY);
Query::equal('projectInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
);
@ -347,8 +353,9 @@ Database::addFilter(
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database->find('sessions', [
new Query('userInternalId', Query::TYPE_EQUAL, [$document->getInternalId()])
], APP_LIMIT_SUBQUERY));
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
}
);
@ -360,8 +367,9 @@ Database::addFilter(
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn() => $database
->find('tokens', [
new Query('userInternalId', Query::TYPE_EQUAL, [$document->getInternalId()])
], APP_LIMIT_SUBQUERY));
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
}
);
@ -373,8 +381,9 @@ Database::addFilter(
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn() => $database
->find('memberships', [
new Query('userInternalId', Query::TYPE_EQUAL, [$document->getInternalId()])
], APP_LIMIT_SUBQUERY));
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
}
);

View file

@ -209,7 +209,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
$payload = [];
$list = Authorization::skip(fn () => $database->find('realtime', [
new Query('timestamp', Query::TYPE_GREATER, [DateTime::addSeconds(new \DateTime(), -15)])
Query::greaterThan('timestamp', DateTime::addSeconds(new \DateTime(), -15)),
]));
/**

View file

@ -108,8 +108,8 @@ $cli
$time = DateTime::now();
$certificates = $dbForConsole->find('certificates', [
new Query('attempts', Query::TYPE_LESSEREQUAL, [5]), // Maximum 5 attempts
new Query('renewDate', Query::TYPE_LESSEREQUAL, [$time]) // includes 60 days cooldown (we have 30 days to renew)
Query::lessThanEqual('attempts', 5), // Maximum 5 attempts
Query::lessThanEqual('renewDate', $time) // includes 60 days cooldown (we have 30 days to renew)
], 200); // Limit 200 comes from LetsEncrypt (300 orders per 3 hours, keeping some for new domains)

View file

@ -9,6 +9,7 @@ use Utopia\Cache\Cache;
use Utopia\Cache\Adapter\Redis as RedisCache;
use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Database;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Validator\Text;
@ -70,7 +71,7 @@ $cli
}
$sum = \count($projects);
$projects = $consoleDB->find('projects', limit: $limit, offset: $offset);
$projects = $consoleDB->find('projects', [Query::limit($limit), Query::offset($offset)]);
$offset = $offset + $limit;
$count = $count + $sum;

View file

@ -73,7 +73,7 @@ class CertificatesV1 extends Worker
$domain = new Domain($document->getAttribute('domain', ''));
// Get current certificate
$certificate = $this->dbForConsole->findOne('certificates', [new Query('domain', Query::TYPE_EQUAL, [$domain->get()])]);
$certificate = $this->dbForConsole->findOne('certificates', [Query::equal('domain', [$domain->get()])]);
// If we don't have certificate for domain yet, let's create new document. At the end we save it
if (!$certificate) {
@ -151,7 +151,7 @@ class CertificatesV1 extends Worker
private function saveCertificateDocument(string $domain, Document $certificate): void
{
// Check if update or insert required
$certificateDocument = $this->dbForConsole->findOne('certificates', [new Query('domain', Query::TYPE_EQUAL, [$domain])]);
$certificateDocument = $this->dbForConsole->findOne('certificates', [Query::equal('domain', [$domain])]);
if (!empty($certificateDocument) && !$certificateDocument->isEmpty()) {
// Merge new data with current data
$certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy()));
@ -176,7 +176,7 @@ class CertificatesV1 extends Worker
if (!empty($envDomain) && $envDomain !== 'localhost') {
return $envDomain;
} else {
$domainDocument = $this->dbForConsole->findOne('domains', [], 0, ['_id'], ['ASC']);
$domainDocument = $this->dbForConsole->findOne('domains', [Query::orderAsc('_id')]);
if ($domainDocument) {
return $domainDocument->getAttribute('domain');
}
@ -394,8 +394,9 @@ class CertificatesV1 extends Worker
private function updateDomainDocuments(string $certificateId, string $domain): void
{
$domains = $this->dbForConsole->find('domains', [
new Query('domain', Query::TYPE_EQUAL, [$domain])
], 1000);
Query::equal('domain', [$domain]),
Query::limit(1000),
]);
foreach ($domains as $domainDocument) {
$domainDocument->setAttribute('updated', DateTime::now());

View file

@ -149,11 +149,11 @@ class DeletesV1 extends Worker
$dbForProject->deleteCollection('database_' . $databaseId . '_collection_' . $document->getInternalId());
$this->deleteByGroup('attributes', [
new Query('collectionId', Query::TYPE_EQUAL, [$collectionId])
Query::equal('collectionId', [$collectionId])
], $dbForProject);
$this->deleteByGroup('indexes', [
new Query('collectionId', Query::TYPE_EQUAL, [$collectionId])
Query::equal('collectionId', [$collectionId])
], $dbForProject);
$this->deleteAuditLogsByResource('collection/' . $collectionId, $projectId);
@ -169,13 +169,13 @@ class DeletesV1 extends Worker
$dbForProject = $this->getProjectDB($projectId);
// Delete Usage stats
$this->deleteByGroup('stats', [
new Query('time', Query::TYPE_LESSER, [$datetime1d]),
new Query('period', Query::TYPE_EQUAL, ['1d']),
Query::lessThan('time', $datetime1d),
Query::equal('period', ['1d']),
], $dbForProject);
$this->deleteByGroup('stats', [
new Query('time', Query::TYPE_LESSER, [$datetime30m]),
new Query('period', Query::TYPE_EQUAL, ['30m']),
Query::lessThan('time', [$datetime30m]),
Query::equal('period', ['30m']),
], $dbForProject);
});
}
@ -190,7 +190,7 @@ class DeletesV1 extends Worker
// Delete Memberships
$this->deleteByGroup('memberships', [
new Query('teamId', Query::TYPE_EQUAL, [$teamId])
Query::equal('teamId', [$teamId])
], $this->getProjectDB($projectId));
}
@ -222,14 +222,14 @@ class DeletesV1 extends Worker
// Delete all sessions of this user from the sessions table and update the sessions field of the user record
$this->deleteByGroup('sessions', [
new Query('userId', Query::TYPE_EQUAL, [$userId])
Query::equal('userId', [$userId])
], $this->getProjectDB($projectId));
$this->getProjectDB($projectId)->deleteCachedDocument('users', $userId);
// Delete Memberships and decrement team membership counts
$this->deleteByGroup('memberships', [
new Query('userId', Query::TYPE_EQUAL, [$userId])
Query::equal('userId', [$userId])
], $this->getProjectDB($projectId), function (Document $document) use ($projectId) {
if ($document->getAttribute('confirm')) { // Count only confirmed members
@ -250,7 +250,7 @@ class DeletesV1 extends Worker
// Delete tokens
$this->deleteByGroup('tokens', [
new Query('userId', Query::TYPE_EQUAL, [$userId])
Query::equal('userId', [$userId])
], $this->getProjectDB($projectId));
}
@ -263,7 +263,7 @@ class DeletesV1 extends Worker
$dbForProject = $this->getProjectDB($projectId);
// Delete Executions
$this->deleteByGroup('executions', [
new Query('$createdAt', Query::TYPE_LESSER, [$datetime])
Query::lessThan('$createdAt', $datetime)
], $dbForProject);
});
}
@ -277,7 +277,7 @@ class DeletesV1 extends Worker
$dbForProject = $this->getProjectDB($projectId);
// Delete Sessions
$this->deleteByGroup('sessions', [
new Query('expire', Query::TYPE_LESSER, [$datetime])
Query::lessThan('expire', $datetime)
], $dbForProject);
});
}
@ -291,7 +291,7 @@ class DeletesV1 extends Worker
$dbForProject = $this->getProjectDB($projectId);
// Delete Dead Realtime Logs
$this->deleteByGroup('realtime', [
new Query('timestamp', Query::TYPE_LESSER, [$datetime])
Query::lessThan('timestamp', $datetime)
], $dbForProject);
});
}
@ -346,7 +346,7 @@ class DeletesV1 extends Worker
$dbForProject = $this->getProjectDB($projectId);
$this->deleteByGroup(Audit::COLLECTION, [
new Query('resource', Query::TYPE_EQUAL, [$resource])
Query::equal('resource', [$resource])
], $dbForProject);
}
@ -366,7 +366,7 @@ class DeletesV1 extends Worker
$storageFunctions = new Local(APP_STORAGE_FUNCTIONS . '/app-' . $projectId);
$deploymentIds = [];
$this->deleteByGroup('deployments', [
new Query('resourceId', Query::TYPE_EQUAL, [$functionId])
Query::equal('resourceId', [$functionId])
], $dbForProject, function (Document $document) use ($storageFunctions, &$deploymentIds) {
$deploymentIds[] = $document->getId();
if ($storageFunctions->delete($document->getAttribute('path', ''), true)) {
@ -383,7 +383,7 @@ class DeletesV1 extends Worker
$storageBuilds = new Local(APP_STORAGE_BUILDS . '/app-' . $projectId);
foreach ($deploymentIds as $deploymentId) {
$this->deleteByGroup('builds', [
new Query('deploymentId', Query::TYPE_EQUAL, [$deploymentId])
Query::equal('deploymentId', [$deploymentId])
], $dbForProject, function (Document $document) use ($storageBuilds, $deploymentId) {
if ($storageBuilds->delete($document->getAttribute('outputPath', ''), true)) {
Console::success('Deleted build files: ' . $document->getAttribute('outputPath', ''));
@ -398,7 +398,7 @@ class DeletesV1 extends Worker
*/
Console::info("Deleting executions for function " . $functionId);
$this->deleteByGroup('executions', [
new Query('functionId', Query::TYPE_EQUAL, [$functionId])
Query::equal('functionId', [$functionId])
], $dbForProject);
/**
@ -442,7 +442,7 @@ class DeletesV1 extends Worker
Console::info("Deleting builds for deployment " . $deploymentId);
$storageBuilds = new Local(APP_STORAGE_BUILDS . '/app-' . $projectId);
$this->deleteByGroup('builds', [
new Query('deploymentId', Query::TYPE_EQUAL, [$deploymentId])
Query::equal('deploymentId', [$deploymentId])
], $dbForProject, function (Document $document) use ($storageBuilds) {
if ($storageBuilds->delete($document->getAttribute('outputPath', ''), true)) {
Console::success('Deleted build files: ' . $document->getAttribute('outputPath', ''));
@ -501,7 +501,7 @@ class DeletesV1 extends Worker
$executionStart = \microtime(true);
while ($sum === $limit) {
$projects = $this->getConsoleDB()->find('projects', [], $limit, ($chunk * $limit));
$projects = $this->getConsoleDB()->find('projects', [Query::limit($limit), Query::offset($chunk * $limit)]);
$chunk++;
@ -540,7 +540,7 @@ class DeletesV1 extends Worker
while ($sum === $limit) {
$chunk++;
$results = $database->find($collection, $queries, $limit, 0);
$results = $database->find($collection, \array_merge([Query::limit($limit)], $queries));
$sum = count($results);
@ -567,7 +567,7 @@ class DeletesV1 extends Worker
// If domain has certificate generated
if (isset($document['certificateId'])) {
$domainUsingCertificate = $consoleDB->findOne('domains', [
new Query('certificateId', Query::TYPE_EQUAL, [$document['certificateId']])
Query::equal('certificateId', [$document['certificateId']])
]);
if (!$domainUsingCertificate) {

View file

@ -14,6 +14,7 @@ use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Query;
require_once __DIR__ . '/../init.php';
@ -61,7 +62,11 @@ class FunctionsV1 extends Worker
/** @var Document[] $functions */
while ($sum >= $limit) {
$functions = $database->find('functions', [], $limit, $offset, ['name'], [Database::ORDER_ASC]);
$functions = $database->find('functions', [
Query::limit($limit),
Query::offset($offset),
Query::orderAsc('name'),
]);
$sum = \count($functions);
$offset = $offset + $limit;

View file

@ -44,13 +44,13 @@
"appwrite/php-runtimes": "0.10.*",
"utopia-php/framework": "0.20.*",
"utopia-php/logger": "0.3.*",
"utopia-php/abuse": "dev-origin/timestamp-to-datetime as 0.7.2",
"utopia-php/abuse": "dev-feat-update-database-queries",
"utopia-php/analytics": "0.2.*",
"utopia-php/audit": "dev-origin/unix-to-datetime as 0.8.2",
"utopia-php/audit": "dev-feat-update-database-queries",
"utopia-php/cache": "0.6.*",
"utopia-php/cli": "0.13.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.19.*",
"utopia-php/database": "dev-feat-query-gen-2.3",
"utopia-php/locale": "0.4.*",
"utopia-php/registry": "0.5.*",
"utopia-php/preloader": "0.2.*",
@ -73,6 +73,18 @@
{
"url": "https://github.com/appwrite/runtimes.git",
"type": "git"
},
{
"url": "https://github.com/utopia-php/database.git",
"type": "git"
},
{
"url": "https://github.com/utopia-php/abuse.git",
"type": "git"
},
{
"url": "https://github.com/utopia-php/audit.git",
"type": "git"
}
],
"require-dev": {
@ -90,4 +102,4 @@
"php": "8.0"
}
}
}
}

72
composer.lock generated
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": "993486075710ab0cdbba6c33f0b09218",
"content-hash": "31918539abb7d1e82ca84ed8fc21969e",
"packages": [
{
"name": "adhocore/jwt",
@ -1733,22 +1733,16 @@
},
{
"name": "utopia-php/abuse",
"version": "0.7.0",
"version": "dev-feat-update-database-queries",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/abuse.git",
"reference": "52fb20e39e2e9619948bc0a73b52e10caa71350d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/52fb20e39e2e9619948bc0a73b52e10caa71350d",
"reference": "52fb20e39e2e9619948bc0a73b52e10caa71350d",
"shasum": ""
"reference": "5c200defd69155c956fc127e8e1771ef820f9dad"
},
"require": {
"ext-pdo": "*",
"php": ">=8.0",
"utopia-php/database": ">=0.11 <1.0"
"utopia-php/database": "dev-feat-query-gen-2.3"
},
"require-dev": {
"phpunit/phpunit": "^9.4",
@ -1760,7 +1754,6 @@
"Utopia\\Abuse\\": "src/Abuse"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -1772,17 +1765,13 @@
],
"description": "A simple abuse library to manage application usage limits",
"keywords": [
"Abuse",
"abuse",
"framework",
"php",
"upf",
"utopia"
],
"support": {
"issues": "https://github.com/utopia-php/abuse/issues",
"source": "https://github.com/utopia-php/abuse/tree/0.7.0"
},
"time": "2021-12-27T13:06:45+00:00"
"time": "2022-08-10T19:51:33+00:00"
},
{
"name": "utopia-php/analytics",
@ -1841,22 +1830,16 @@
},
{
"name": "utopia-php/audit",
"version": "0.8.0",
"version": "dev-feat-update-database-queries",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "b46dc42614a69437c45eb229249b6a6d000122c1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/b46dc42614a69437c45eb229249b6a6d000122c1",
"reference": "b46dc42614a69437c45eb229249b6a6d000122c1",
"shasum": ""
"reference": "165d4f74fedf31c98c038eb92a0516472885a78a"
},
"require": {
"ext-pdo": "*",
"php": ">=8.0",
"utopia-php/database": ">=0.11 <1.0"
"utopia-php/database": "dev-feat-query-gen-2.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3",
@ -1868,7 +1851,6 @@
"Utopia\\Audit\\": "src/Audit"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -1880,17 +1862,13 @@
],
"description": "A simple audit library to manage application users logs",
"keywords": [
"Audit",
"audit",
"framework",
"php",
"upf",
"utopia"
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/0.8.0"
},
"time": "2021-12-27T13:05:56+00:00"
"time": "2022-08-10T20:15:22+00:00"
},
{
"name": "utopia-php/cache",
@ -2051,17 +2029,11 @@
},
{
"name": "utopia-php/database",
"version": "0.18.9",
"version": "dev-feat-query-gen-2.3",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "227b3ca919149b7b0d6556c8effe9ee46ed081e6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/227b3ca919149b7b0d6556c8effe9ee46ed081e6",
"reference": "227b3ca919149b7b0d6556c8effe9ee46ed081e6",
"shasum": ""
"reference": "d6ba28d373e2cad2fc5d916564fd245e17bea38f"
},
"require": {
"ext-mongodb": "*",
@ -2085,7 +2057,11 @@
"Utopia\\Database\\": "src/Database"
}
},
"notification-url": "https://packagist.org/downloads/",
"autoload-dev": {
"psr-4": {
"Utopia\\Tests\\": "tests/Database"
}
},
"license": [
"MIT"
],
@ -2107,11 +2083,7 @@
"upf",
"utopia"
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.18.9"
},
"time": "2022-07-19T09:42:53+00:00"
"time": "2022-08-11T19:26:25+00:00"
},
{
"name": "utopia-php/domains",
@ -5348,7 +5320,11 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {
"utopia-php/abuse": 20,
"utopia-php/audit": 20,
"utopia-php/database": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {

View file

@ -5,6 +5,7 @@ namespace Appwrite\Migration;
use Swoole\Runtime;
use Utopia\Database\Document;
use Utopia\Database\Database;
use Utopia\Database\Query;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Exception;
@ -116,7 +117,11 @@ abstract class Migration
Console::log('Migrating Collection ' . $collection['$id'] . ':');
do {
$documents = $this->projectDB->find($collection['$id'], limit: $this->limit, cursor: $nextDocument);
$queries = [Query::limit($this->limit)];
if ($nextDocument !== null) {
$queries[] = Query::cursorAfter($nextDocument);
}
$documents = $this->projectDB->find($collection['$id'], $queries);
$count = count($documents);
$sum += $count;

View file

@ -156,7 +156,7 @@ class V12 extends Migration
*/
$this->createCollection('buckets');
if (!$this->projectDB->findOne('buckets', [new Query('$id', Query::TYPE_EQUAL, ['default'])])) {
if (!$this->projectDB->findOne('buckets', [Query::equal('$id', ['default'])])) {
$this->projectDB->createDocument('buckets', new Document([
'$id' => 'default',
'$collection' => 'buckets',
@ -180,7 +180,11 @@ class V12 extends Migration
*/
$nextDocument = null;
do {
$documents = $this->projectDB->find('files', limit: $this->limit, cursor: $nextDocument);
$queries = [Query::limit($this->limit)];
if ($nextDocument !== null) {
$queries[] = Query::cursorAfter($nextDocument);
}
$documents = $this->projectDB->find('files', $queries);
$count = count($documents);
\Co\run(function (array $documents) {
foreach ($documents as $document) {
@ -344,7 +348,11 @@ class V12 extends Migration
$nextCollection = null;
do {
$documents = $this->projectDB->find('collections', limit: $this->limit, cursor: $nextCollection);
$queries = [Query::limit($this->limit)];
if ($nextCollection !== null) {
$queries[] = Query::cursorAfter($nextCollection);
}
$documents = $this->projectDB->find('collections', $queries);
$count = count($documents);
\Co\run(function (array $documents) {
@ -387,7 +395,11 @@ class V12 extends Migration
$nextDocument = null;
do {
$documents = $this->projectDB->find('collection_' . $internalId, limit: $this->limit, cursor: $nextDocument);
$queries = [Query::limit($this->limit)];
if ($nextDocument !== null) {
$queries[] = Query::cursorAfter($nextDocument);
}
$documents = $this->projectDB->find('collection_' . $internalId, $queries);
$count = count($documents);
foreach ($documents as $document) {
@ -462,7 +474,11 @@ class V12 extends Migration
$nextDocument = null;
do {
$documents = $this->projectDB->find($id, limit: $this->limit, cursor: $nextDocument);
$queries = [Query::limit($this->limit)];
if ($nextDocument !== null) {
$queries[] = Query::cursorAfter($nextDocument);
}
$documents = $this->projectDB->find($id, $queries);
$count = count($documents);
\Co\run(function (array $documents) {

View file

@ -87,7 +87,11 @@ class V14 extends Migration
{
$nextFile = null;
do {
$documents = $this->projectDB->find("bucket_{$bucket->getInternalId()}", limit: $this->limit, cursor: $nextFile);
$queries = [Query::limit($this->limit)];
if ($nextFile !== null) {
$queries[] = Query::cursorAfter($nextFile);
}
$documents = $this->projectDB->find("bucket_{$bucket->getInternalId()}", $queries);
$count = count($documents);
foreach ($documents as $document) {
@ -164,7 +168,11 @@ class V14 extends Migration
$nextCollection = null;
do {
$documents = $this->projectDB->find('database_1', limit: $this->limit, cursor: $nextCollection);
$queries = [Query::limit($this->limit)];
if ($nextCollection !== null) {
$queries[] = Query::cursorAfter($nextCollection);
}
$documents = $this->projectDB->find('database_1', $queries);
$count = count($documents);
\Co\run(function (array $documents) {
@ -235,10 +243,15 @@ class V14 extends Migration
* Offset pagination instead of cursor, since documents are re-created!
*/
$offset = 0;
$attributesCount = $this->projectDB->count($type, queries: [new Query('collectionId', Query::TYPE_EQUAL, [$collection->getId()])]);
$attributesCount = $this->projectDB->count($type, queries: [Query::equal('collectionId', [$collection->getId()])]);
do {
$documents = $this->projectDB->find($type, limit: $this->limit, offset: $offset, queries: [new Query('collectionId', Query::TYPE_EQUAL, [$collection->getId()])]);
$queries = [
Query::limit($this->limit),
Query::offset($offset),
Query::equal('collectionId', [$collection->getId()]),
];
$documents = $this->projectDB->find($type, $queries);
$offset += $this->limit;
foreach ($documents as $document) {

View file

@ -5,6 +5,7 @@ namespace Appwrite\Stats;
use Exception;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
class UsageDB extends Usage
{
@ -60,7 +61,7 @@ class UsageDB extends Usage
$document->setAttribute('value', $value)
);
}
} catch (Exception$e) { // if projects are deleted this might fail
} catch (\Exception $e) { // if projects are deleted this might fail
if (is_callable($this->errorHandler)) {
call_user_func($this->errorHandler, $e, "sync_project_{$projectId}_metric_{$metric}");
} else {
@ -91,8 +92,12 @@ class UsageDB extends Usage
while ($sum === $limit) {
try {
$results = $this->database->find($collection, $queries, $limit, cursor:$latestDocument);
} catch (Exception $e) {
$paginationQueries = [Query::limit($limit)];
if ($latestDocument !== null) {
$paginationQueries[] = Query::cursorAfter($latestDocument);
}
$results = $this->database->find($collection, \array_merge($paginationQueries, $queries));
} catch (\Exception $e) {
if (is_callable($this->errorHandler)) {
call_user_func($this->errorHandler, $e, "fetch_documents_project_{$projectId}_collection_{$collection}");
return;

View file

@ -9,18 +9,21 @@ class Queries extends ValidatorQueries
{
/**
* Expression constructor
*
* This Queries Validator that filters indexes for only available indexes
*
* @param QueryValidator $validator
* @param Document[] $attributes
* @param Document[] $indexes
* @param bool $strict
*/
public function __construct($attributes, $indexes, $strict)
public function __construct($validator, $attributes = [], $indexes = [], $strict = true)
{
// Remove failed/stuck/processing indexes
$indexes = \array_filter($indexes, function ($index) {
$availableIndexes = \array_filter($indexes, function ($index) {
return $index->getAttribute('status') === 'available';
});
parent::__construct($attributes, $indexes, $strict);
parent::__construct($validator, $attributes, $availableIndexes, $strict);
}
}

View file

@ -197,7 +197,7 @@ trait DatabasesBase
$this->assertEquals($actors['body']['required'], false);
$this->assertEquals($actors['body']['array'], true);
$this->assertEquals($datetime['headers']['status-code'], 201);
$this->assertEquals($datetime['headers']['status-code'], 202);
$this->assertEquals($datetime['body']['key'], 'birthDay');
$this->assertEquals($datetime['body']['type'], 'datetime');
$this->assertEquals($datetime['body']['required'], false);
@ -410,7 +410,7 @@ trait DatabasesBase
$this->assertEquals(false, $boolean['body']['array']);
$this->assertEquals(true, $boolean['body']['default']);
$this->assertEquals(201, $datetime['headers']['status-code']);
$this->assertEquals(202, $datetime['headers']['status-code']);
$this->assertEquals('datetime', $datetime['body']['key']);
$this->assertEquals('datetime', $datetime['body']['type']);
$this->assertEquals(false, $datetime['body']['required']);
@ -834,7 +834,7 @@ trait DatabasesBase
'attributes' => ['birthDay'],
]);
$this->assertEquals(201, $releaseWithDate['headers']['status-code']);
$this->assertEquals(202, $releaseWithDate['headers']['status-code']);
$this->assertEquals('birthDay', $releaseWithDate['body']['key']);
$this->assertEquals('key', $releaseWithDate['body']['type']);
$this->assertCount(1, $releaseWithDate['body']['attributes']);
@ -1359,7 +1359,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['title.search("Captain America")'],
'queries' => ['search("title", "Captain America")'],
]);
$this->assertEquals($documents['headers']['status-code'], 200);
@ -1370,7 +1370,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['$id.equal("' . $documents['body']['documents'][0]['$id'] . '")'],
'queries' => ['equal("$id", "' . $documents['body']['documents'][0]['$id'] . '")'],
]);
$this->assertEquals($documents['headers']['status-code'], 200);
@ -1381,7 +1381,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['title.search("Homecoming")'],
'queries' => ['search("title", "Homecoming")'],
]);
$this->assertEquals($documents['headers']['status-code'], 200);
@ -1392,7 +1392,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['title.search("spider")'],
'queries' => ['search("title", "spider")'],
]);
$this->assertEquals($documents['headers']['status-code'], 200);
@ -1404,7 +1404,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['releaseYear.equal(1944)'],
'queries' => ['equal("releaseYear", 1944)'],
]);
$this->assertCount(1, $documents['body']['documents']);
@ -1414,7 +1414,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['releaseYear.notEqual(1944)'],
'queries' => ['notEqual("releaseYear", 1944)'],
]);
$this->assertCount(2, $documents['body']['documents']);
@ -1425,7 +1425,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['$createdAt.greater("1976-06-12")'],
'queries' => ['greaterThan("$createdAt", "1976-06-12")'],
]);
$this->assertCount(3, $documents['body']['documents']);
@ -1434,7 +1434,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['$createdAt.lesser("1976-06-12")'],
'queries' => ['lessThan("$createdAt", "1976-06-12")'],
]);
$this->assertCount(0, $documents['body']['documents']);
@ -1446,7 +1446,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['actors.equal("Tom Holland")'],
'queries' => ['equal("actors", "Tom Holland")'],
]);
$this->assertEquals(400, $documents['headers']['status-code']);
$this->assertEquals('Index not found: actors', $documents['body']['message']);
@ -1461,7 +1461,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['releaseYear.equal(' . implode(',', $conditions) . ')'],
'queries' => ['equal("releaseYear", [' . implode(',', $conditions) . '])'],
]);
$this->assertEquals(400, $documents['headers']['status-code']);
@ -1476,7 +1476,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['title.search(' . implode(',', $conditions) . ')'],
'queries' => ['search("title", ' . implode(',', $conditions) . ')'],
]);
$this->assertEquals(400, $documents['headers']['status-code']);
@ -1486,7 +1486,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => ['birthDay.greater("1960-01-01 10:10:10+02:30")'],
'queries' => ['greaterThan("birthDay", "1960-01-01 10:10:10+02:30")'],
]);
$this->assertEquals($documents['headers']['status-code'], 200);

View file

@ -177,7 +177,7 @@ class FunctionsCustomClientTest extends Scope
$deploymentId = $deployment['body']['$id'] ?? '';
// Wait for deployment to be built.
sleep(5);
sleep(10);
$this->assertEquals(202, $deployment['headers']['status-code']);