1
0
Fork 0
mirror of synced 2024-06-13 16:24:47 +12:00

feat(controllers): implement before cursor pagination

This commit is contained in:
Torsten Dittmann 2021-10-05 12:30:33 +02:00
parent 45066d99db
commit 33699ea2c4
19 changed files with 481 additions and 180 deletions

View file

@ -93,7 +93,7 @@ $attributesCallback = function ($collectionId, $attribute, $response, $dbForInte
throw new Exception('Attribute already exists', 409);
}
$dbForInternal->purgeDocument('collections', $collectionId);
$dbForInternal->deleteCachedCollection($collectionId);
$database
->setParam('type', DATABASE_TYPE_CREATE_ATTRIBUTE)
@ -186,12 +186,13 @@ App::get('/v1/database/collections')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 40000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', 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('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.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->inject('usage')
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal, $usage) {
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
@ -201,18 +202,18 @@ App::get('/v1/database/collections')
$queries[] = new Query('name', Query::TYPE_SEARCH, [$search]);
}
if (!empty($after)) {
$afterCollection = $dbForInternal->getDocument('collections', $after);
if (!empty($cursor)) {
$cursorCollection = $dbForInternal->getDocument('collections', $cursor);
if ($afterCollection->isEmpty()) {
throw new Exception("Collection '{$after}' for the 'after' value not found.", 400);
if ($cursorCollection->isEmpty()) {
throw new Exception("Collection '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$usage->setParam('database.collections.read', 1);
$response->dynamic(new Document([
'collections' => $dbForInternal->find('collections', $queries, $limit, $offset, [], [$orderType], $afterCollection ?? null),
'collections' => $dbForInternal->find('collections', $queries, $limit, $offset, [], [$orderType], $cursorCollection ?? null, $cursorDirection),
'sum' => $dbForInternal->count('collections', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_COLLECTION_LIST);
});
@ -1060,7 +1061,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
}
$attribute = $dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'deleting'));
$dbForInternal->purgeDocument('collections', $collectionId);
$dbForInternal->deleteCachedCollection($collectionId);
$database
->setParam('type', DATABASE_TYPE_DELETE_ATTRIBUTE)
@ -1167,7 +1168,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
throw new Exception('Index already exists', 409);
}
$dbForInternal->purgeDocument('collections', $collectionId);
$dbForInternal->deleteCachedCollection($collectionId);
$database
->setParam('type', DATABASE_TYPE_CREATE_INDEX)
@ -1312,7 +1313,7 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId')
}
$index = $dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'deleting'));
$dbForInternal->purgeDocument('collections', $collectionId);
$dbForInternal->deleteCachedCollection($collectionId);
$database
->setParam('type', DATABASE_TYPE_DELETE_INDEX)
@ -1365,7 +1366,7 @@ App::post('/v1/database/collections/:collectionId/documents')
/** @var Utopia\Database\Document $user */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
if (empty($data)) {
@ -1375,7 +1376,7 @@ App::post('/v1/database/collections/:collectionId/documents')
if (isset($data['$id'])) {
throw new Exception('$id is not allowed for creating new documents, try update instead', 400);
}
$collection = $dbForInternal->getDocument('collections', $collectionId);
if ($collection->isEmpty()) {
@ -1427,14 +1428,15 @@ App::get('/v1/database/collections/:collectionId/documents')
->param('queries', [], new ArrayList(new Text(128)), 'Array of query strings.', true)
->param('limit', 25, new Range(0, 100), 'Maximum number of documents to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 900000000), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', 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.', true)
->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.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderAttributes', [], new ArrayList(new Text(128)), 'Array of attributes used to sort results.', true)
->param('orderTypes', [], new ArrayList(new WhiteList(['DESC', 'ASC'], true)), 'Array of order directions for sorting attribtues. Possible values are DESC for descending order, or ASC for ascending order.', true)
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')
->inject('usage')
->action(function ($collectionId, $queries, $limit, $offset, $after, $orderAttributes, $orderTypes, $response, $dbForInternal, $dbForExternal, $usage) {
->action(function ($collectionId, $queries, $limit, $offset, $cursor, $cursorDirection, $orderAttributes, $orderTypes, $response, $dbForInternal, $dbForExternal, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Utopia\Database\Database $dbForExternal */
@ -1457,22 +1459,22 @@ App::get('/v1/database/collections/:collectionId/documents')
throw new Exception($validator->getDescription(), 400);
}
if (!empty($after)) {
$afterDocument = $dbForExternal->getDocument($collectionId, $after);
if (!empty($cursor)) {
$cursorDocument = $dbForExternal->getDocument($collectionId, $cursor);
if ($afterDocument->isEmpty()) {
throw new Exception("Document '{$after}' for the 'after' value not found.", 400);
if ($cursorDocument->isEmpty()) {
throw new Exception("Document '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$usage
->setParam('database.documents.read', 1)
->setParam('collectionId', $collectionId)
;
;
$response->dynamic(new Document([
'sum' => $dbForExternal->count($collectionId, $queries, APP_LIMIT_COUNT),
'documents' => $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument ?? null),
'documents' => $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection),
]), Response::MODEL_DOCUMENT_LIST);
});

View file

@ -88,26 +88,27 @@ App::get('/v1/functions')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', 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.', 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.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal) {
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
$queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : [];
if (!empty($after)) {
$afterFunction = $dbForInternal->getDocument('functions', $after);
if (!empty($cursor)) {
$cursorFunction = $dbForInternal->getDocument('functions', $cursor);
if ($afterFunction->isEmpty()) {
throw new Exception("Function '{$after}' for the 'after' value not found.", 400);
if ($cursorFunction->isEmpty()) {
throw new Exception("Function '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$response->dynamic(new Document([
'functions' => $dbForInternal->find('functions', $queries, $limit, $offset, [], [$orderType], $afterFunction ?? null),
'functions' => $dbForInternal->find('functions', $queries, $limit, $offset, [], [$orderType], $cursorFunction ?? null, $cursorDirection),
'sum' => $dbForInternal->count('functions', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_FUNCTION_LIST);
});
@ -481,11 +482,12 @@ App::get('/v1/functions/:functionId/tags')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', new UID(), 'ID of the tag used as the starting point for the query, excluding the tag itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursor', '', new UID(), 'ID of the tag used as the starting point for the query, excluding the tag 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.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->action(function ($functionId, $search, $limit, $offset, $after, $orderType, $response, $dbForInternal) {
->action(function ($functionId, $search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
@ -497,15 +499,15 @@ App::get('/v1/functions/:functionId/tags')
$queries[] = new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]);
if (!empty($after)) {
$afterTag = $dbForInternal->getDocument('tags', $after);
if (!empty($cursor)) {
$cursorTag = $dbForInternal->getDocument('tags', $cursor);
if ($afterTag->isEmpty()) {
throw new Exception("Tag '{$after}' for the 'after' value not found.", 400);
if ($cursorTag->isEmpty()) {
throw new Exception("Tag '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$results = $dbForInternal->find('tags', $queries, $limit, $offset, [], [$orderType], $afterTag ?? null);
$results = $dbForInternal->find('tags', $queries, $limit, $offset, [], [$orderType], $cursorTag ?? null, $cursorDirection);
$sum = $dbForInternal->count('tags', $queries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
@ -732,10 +734,11 @@ App::get('/v1/functions/:functionId/executions')
->param('functionId', '', new UID(), 'Function unique ID.')
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', new UID(), 'ID of the execution used as the starting point for the query, excluding the execution itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursor', '', new UID(), 'ID of the execution used as the starting point for the query, excluding the execution 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.', true)
->inject('response')
->inject('dbForInternal')
->action(function ($functionId, $limit, $offset, $after, $response, $dbForInternal) {
->action(function ($functionId, $limit, $offset, $cursor, $cursorDirection, $response, $dbForInternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
@ -747,17 +750,17 @@ App::get('/v1/functions/:functionId/executions')
throw new Exception('Function not found', 404);
}
if (!empty($after)) {
$afterExecution = $dbForInternal->getDocument('executions', $after);
if (!empty($cursor)) {
$cursorExecution = $dbForInternal->getDocument('executions', $cursor);
if ($afterExecution->isEmpty()) {
throw new Exception("Execution '{$after}' for the 'after' value not found.", 400);
if ($cursorExecution->isEmpty()) {
throw new Exception("Execution '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$results = $dbForInternal->find('executions', [
new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]),
], $limit, $offset, [], [Database::ORDER_DESC], $afterExecution ?? null);
], $limit, $offset, [], [Database::ORDER_DESC], $cursorExecution ?? null, $cursorDirection);
$sum = $dbForInternal->count('executions', [
new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]),

View file

@ -163,25 +163,26 @@ App::get('/v1/projects')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', 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.', 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.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForConsole')
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForConsole) {
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
$queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : [];
if (!empty($after)) {
$afterProject = $dbForConsole->getDocument('projects', $after);
if (!empty($cursor)) {
$cursorProject = $dbForConsole->getDocument('projects', $cursor);
if ($afterProject->isEmpty()) {
throw new Exception("Project '{$after}' for the 'after' value not found.", 400);
if ($cursorProject->isEmpty()) {
throw new Exception("Project '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$results = $dbForConsole->find('projects', $queries, $limit, $offset, [], [$orderType], $afterProject ?? null);
$results = $dbForConsole->find('projects', $queries, $limit, $offset, [], [$orderType], $cursorProject ?? null, $cursorDirection);
$sum = $dbForConsole->count('projects', $queries, APP_LIMIT_COUNT);
$response->dynamic(new Document([

View file

@ -175,23 +175,24 @@ App::get('/v1/storage/files')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', 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.', 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.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->inject('usage')
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal, $usage) {
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Appwrite\Stats\Stats $usage */
$queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, $search)] : [];
if (!empty($after)) {
$afterFile = $dbForInternal->getDocument('files', $after);
if (!empty($cursor)) {
$cursorFile = $dbForInternal->getDocument('files', $cursor);
if ($afterFile->isEmpty()) {
throw new Exception("File '{$after}' for the 'after' value not found.", 400);
if ($cursorFile->isEmpty()) {
throw new Exception("File '{$cursor}' for the 'cursor' value not found.", 400);
}
}
@ -201,7 +202,7 @@ App::get('/v1/storage/files')
;
$response->dynamic(new Document([
'files' => $dbForInternal->find('files', $queries, $limit, $offset, [], [$orderType], $afterFile ?? null),
'files' => $dbForInternal->find('files', $queries, $limit, $offset, [], [$orderType], $cursorFile ?? null, $cursorDirection),
'sum' => $dbForInternal->count('files', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_FILE_LIST);
});

View file

@ -14,6 +14,7 @@ use Utopia\Validator\Text;
use Utopia\Validator\Range;
use Utopia\Validator\ArrayList;
use Utopia\Validator\WhiteList;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Query;
@ -99,25 +100,26 @@ App::get('/v1/teams')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', new UID(), 'ID of the team used as the starting point for the query, excluding the team itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursor', '', new UID(), 'ID of the team used as the starting point for the query, excluding the team 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.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal) {
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
$queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : [];
if (!empty($after)) {
$afterTeam = $dbForInternal->getDocument('teams', $after);
if (!empty($cursor)) {
$cursorTeam = $dbForInternal->getDocument('teams', $cursor);
if ($afterTeam->isEmpty()) {
throw new Exception("Team '{$after}' for the 'after' value not found.", 400);
if ($cursorTeam->isEmpty()) {
throw new Exception("Team '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$results = $dbForInternal->find('teams', $queries, $limit, $offset, [], [$orderType], $afterTeam ?? null);
$results = $dbForInternal->find('teams', $queries, $limit, $offset, [], [$orderType], $cursorTeam ?? null, $cursorDirection);
$sum = $dbForInternal->count('teams', $queries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
@ -424,11 +426,12 @@ App::get('/v1/teams/:teamId/memberships')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', 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.', 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.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->action(function ($teamId, $search, $limit, $offset, $after, $orderType, $response, $dbForInternal) {
->action(function ($teamId, $search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
@ -438,15 +441,15 @@ App::get('/v1/teams/:teamId/memberships')
throw new Exception('Team not found', 404);
}
if (!empty($after)) {
$afterMembership = $dbForInternal->getDocument('memberships', $after);
if (!empty($cursor)) {
$cursorMembership = $dbForInternal->getDocument('memberships', $cursor);
if ($afterMembership->isEmpty()) {
throw new Exception("Membership '{$after}' for the 'after' value not found.", 400);
if ($cursorMembership->isEmpty()) {
throw new Exception("Membership '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$memberships = $dbForInternal->find('memberships', [new Query('teamId', Query::TYPE_EQUAL, [$teamId])], $limit, $offset, [], [$orderType], $afterMembership ?? null);
$memberships = $dbForInternal->find('memberships', [new Query('teamId', Query::TYPE_EQUAL, [$teamId])], $limit, $offset, [], [$orderType], $cursorMembership ?? null, $cursorDirection);
$sum = $dbForInternal->count('memberships', [new Query('teamId', Query::TYPE_EQUAL, [$teamId])], APP_LIMIT_COUNT);
$users = [];

View file

@ -93,27 +93,28 @@ App::get('/v1/users')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', 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.', 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.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->inject('usage')
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal, $usage) {
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Appwrite\Stats\Stats $usage */
if (!empty($after)) {
$afterUser = $dbForInternal->getDocument('users', $after);
if (!empty($cursor)) {
$cursorUser = $dbForInternal->getDocument('users', $cursor);
if ($afterUser->isEmpty()) {
if ($cursorUser->isEmpty()) {
throw new Exception('User for after not found', 400);
}
}
$results = $dbForInternal->find('users', [], $limit, $offset, [], [$orderType], $afterUser ?? null);
$results = $dbForInternal->find('users', [], $limit, $offset, [], [$orderType], $cursorUser ?? null, $cursorDirection);
$sum = $dbForInternal->count('users', [], APP_LIMIT_COUNT);
$usage
->setParam('users.read', 1)
;

View file

@ -342,7 +342,7 @@ $cli
do { // list projects
try {
$attempts++;
$projects = $dbForConsole->find('projects', [], 100, orderAfter:$latestProject);
$projects = $dbForConsole->find('projects', [], 100, cursor:$latestProject);
break; // leave the do-while if successful
} catch (\Exception $e) {
Console::warning("Console DB not ready yet. Retrying ({$attempts})...");
@ -472,7 +472,7 @@ $cli
do { // Loop over all the parent collection document for each sub collection
$dbForProject->setNamespace("project_{$projectId}_{$options['namespace']}");
$parents = $dbForProject->find($collection, [], 100, orderAfter:$latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections
$parents = $dbForProject->find($collection, [], 100, cursor:$latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections
if (empty($parents)) {
continue;

View file

@ -94,7 +94,7 @@ class DatabaseV1 extends Worker
$dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed'));
}
$dbForInternal->purgeDocument('collections', $collectionId);
//$dbForInternal->deleteCachedCollection($collectionId);
}
/**
@ -120,7 +120,7 @@ class DatabaseV1 extends Worker
$dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed'));
}
$dbForInternal->purgeDocument('collections', $collectionId);
$dbForInternal->deleteCachedCollection($collectionId);
}
/**
@ -150,7 +150,7 @@ class DatabaseV1 extends Worker
$dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'failed'));
}
$dbForInternal->purgeDocument('collections', $collectionId);
$dbForInternal->deleteCachedCollection($collectionId);
}
/**
@ -177,6 +177,6 @@ class DatabaseV1 extends Worker
$dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'failed'));
}
$dbForInternal->purgeDocument('collections', $collectionId);
$dbForInternal->deleteCachedCollection($collectionId);
}
}

View file

@ -45,7 +45,7 @@
"utopia-php/cache": "0.4.*",
"utopia-php/cli": "0.11.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "dev-feat-adjusted-query-validator as 0.10.0",
"utopia-php/database": "0.10.*",
"utopia-php/locale": "0.4.*",
"utopia-php/registry": "0.5.*",
"utopia-php/preloader": "0.2.*",

154
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": "175f077512c575216c4c88f1d33c6d00",
"content-hash": "5ed5aceb1c5a8a31b2a1cd2122a74d78",
"packages": [
{
"name": "adhocore/jwt",
@ -248,16 +248,16 @@
},
{
"name": "chillerlan/php-settings-container",
"version": "2.1.1",
"version": "2.1.2",
"source": {
"type": "git",
"url": "https://github.com/chillerlan/php-settings-container.git",
"reference": "98ccc1b31b31a53bcb563465c4961879b2b93096"
"reference": "ec834493a88682dd69652a1eeaf462789ed0c5f5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/98ccc1b31b31a53bcb563465c4961879b2b93096",
"reference": "98ccc1b31b31a53bcb563465c4961879b2b93096",
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/ec834493a88682dd69652a1eeaf462789ed0c5f5",
"reference": "ec834493a88682dd69652a1eeaf462789ed0c5f5",
"shasum": ""
},
"require": {
@ -307,7 +307,7 @@
"type": "ko_fi"
}
],
"time": "2021-01-06T15:57:03+00:00"
"time": "2021-09-06T15:17:01+00:00"
},
{
"name": "colinmollenhour/credis",
@ -355,16 +355,16 @@
},
{
"name": "composer/package-versions-deprecated",
"version": "1.11.99.3",
"version": "1.11.99.4",
"source": {
"type": "git",
"url": "https://github.com/composer/package-versions-deprecated.git",
"reference": "fff576ac850c045158a250e7e27666e146e78d18"
"reference": "b174585d1fe49ceed21928a945138948cb394600"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/fff576ac850c045158a250e7e27666e146e78d18",
"reference": "fff576ac850c045158a250e7e27666e146e78d18",
"url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600",
"reference": "b174585d1fe49ceed21928a945138948cb394600",
"shasum": ""
},
"require": {
@ -408,7 +408,7 @@
"description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
"support": {
"issues": "https://github.com/composer/package-versions-deprecated/issues",
"source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.3"
"source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4"
},
"funding": [
{
@ -424,7 +424,7 @@
"type": "tidelift"
}
],
"time": "2021-08-17T13:49:14+00:00"
"time": "2021-09-13T08:41:34+00:00"
},
{
"name": "dragonmantank/cron-expression",
@ -1984,11 +1984,11 @@
},
{
"name": "utopia-php/database",
"version": "dev-feat-adjusted-query-validator",
"version": "0.10.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database",
"reference": "cb73391371f70ddb54bc0000064b15c5f173cb7a"
"reference": "b7c60b0ec769a9050dd2b939b78ff1f5d4fa27e8"
},
"require": {
"ext-mongodb": "*",
@ -2037,7 +2037,7 @@
"upf",
"utopia"
],
"time": "2021-08-23T14:18:47+00:00"
"time": "2021-10-04T17:23:25+00:00"
},
{
"name": "utopia-php/domains",
@ -2576,16 +2576,16 @@
"packages-dev": [
{
"name": "amphp/amp",
"version": "v2.6.0",
"version": "v2.6.1",
"source": {
"type": "git",
"url": "https://github.com/amphp/amp.git",
"reference": "caa95edeb1ca1bf7532e9118ede4a3c3126408cc"
"reference": "c5fc66a78ee38d7ac9195a37bacaf940eb3f65ae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/amphp/amp/zipball/caa95edeb1ca1bf7532e9118ede4a3c3126408cc",
"reference": "caa95edeb1ca1bf7532e9118ede4a3c3126408cc",
"url": "https://api.github.com/repos/amphp/amp/zipball/c5fc66a78ee38d7ac9195a37bacaf940eb3f65ae",
"reference": "c5fc66a78ee38d7ac9195a37bacaf940eb3f65ae",
"shasum": ""
},
"require": {
@ -2653,7 +2653,7 @@
"support": {
"irc": "irc://irc.freenode.org/amphp",
"issues": "https://github.com/amphp/amp/issues",
"source": "https://github.com/amphp/amp/tree/v2.6.0"
"source": "https://github.com/amphp/amp/tree/v2.6.1"
},
"funding": [
{
@ -2661,7 +2661,7 @@
"type": "github"
}
],
"time": "2021-07-16T20:06:06+00:00"
"time": "2021-09-23T18:43:08+00:00"
},
{
"name": "amphp/byte-stream",
@ -3383,16 +3383,16 @@
},
{
"name": "nikic/php-parser",
"version": "v4.12.0",
"version": "v4.13.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "6608f01670c3cc5079e18c1dab1104e002579143"
"reference": "50953a2691a922aa1769461637869a0a2faa3f53"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6608f01670c3cc5079e18c1dab1104e002579143",
"reference": "6608f01670c3cc5079e18c1dab1104e002579143",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/50953a2691a922aa1769461637869a0a2faa3f53",
"reference": "50953a2691a922aa1769461637869a0a2faa3f53",
"shasum": ""
},
"require": {
@ -3433,9 +3433,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.12.0"
"source": "https://github.com/nikic/PHP-Parser/tree/v4.13.0"
},
"time": "2021-07-21T10:44:31+00:00"
"time": "2021-09-20T12:20:58+00:00"
},
{
"name": "openlss/lib-array2xml",
@ -3712,16 +3712,16 @@
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.4.0",
"version": "1.5.1",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0"
"reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
"reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae",
"reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae",
"shasum": ""
},
"require": {
@ -3729,7 +3729,8 @@
"phpdocumentor/reflection-common": "^2.0"
},
"require-dev": {
"ext-tokenizer": "*"
"ext-tokenizer": "*",
"psalm/phar": "^4.8"
},
"type": "library",
"extra": {
@ -3755,39 +3756,39 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1"
},
"time": "2020-09-17T18:55:26+00:00"
"time": "2021-10-02T14:08:47+00:00"
},
{
"name": "phpspec/prophecy",
"version": "1.13.0",
"version": "1.14.0",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "be1996ed8adc35c3fd795488a653f4b518be70ea"
"reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea",
"reference": "be1996ed8adc35c3fd795488a653f4b518be70ea",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e",
"reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.2",
"php": "^7.2 || ~8.0, <8.1",
"php": "^7.2 || ~8.0, <8.2",
"phpdocumentor/reflection-docblock": "^5.2",
"sebastian/comparator": "^3.0 || ^4.0",
"sebastian/recursion-context": "^3.0 || ^4.0"
},
"require-dev": {
"phpspec/phpspec": "^6.0",
"phpspec/phpspec": "^6.0 || ^7.0",
"phpunit/phpunit": "^8.0 || ^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.11.x-dev"
"dev-master": "1.x-dev"
}
},
"autoload": {
@ -3822,29 +3823,29 @@
],
"support": {
"issues": "https://github.com/phpspec/prophecy/issues",
"source": "https://github.com/phpspec/prophecy/tree/1.13.0"
"source": "https://github.com/phpspec/prophecy/tree/1.14.0"
},
"time": "2021-03-17T13:42:18+00:00"
"time": "2021-09-10T09:02:12+00:00"
},
{
"name": "phpunit/php-code-coverage",
"version": "9.2.6",
"version": "9.2.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "f6293e1b30a2354e8428e004689671b83871edde"
"reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde",
"reference": "f6293e1b30a2354e8428e004689671b83871edde",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218",
"reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"ext-xmlwriter": "*",
"nikic/php-parser": "^4.10.2",
"nikic/php-parser": "^4.12.0",
"php": ">=7.3",
"phpunit/php-file-iterator": "^3.0.3",
"phpunit/php-text-template": "^2.0.2",
@ -3893,7 +3894,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7"
},
"funding": [
{
@ -3901,7 +3902,7 @@
"type": "github"
}
],
"time": "2021-03-28T07:26:59+00:00"
"time": "2021-09-17T05:39:03+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -5313,16 +5314,16 @@
},
{
"name": "symfony/console",
"version": "v5.3.6",
"version": "v5.3.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "51b71afd6d2dc8f5063199357b9880cea8d8bfe2"
"reference": "8b1008344647462ae6ec57559da166c2bfa5e16a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/51b71afd6d2dc8f5063199357b9880cea8d8bfe2",
"reference": "51b71afd6d2dc8f5063199357b9880cea8d8bfe2",
"url": "https://api.github.com/repos/symfony/console/zipball/8b1008344647462ae6ec57559da166c2bfa5e16a",
"reference": "8b1008344647462ae6ec57559da166c2bfa5e16a",
"shasum": ""
},
"require": {
@ -5392,7 +5393,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v5.3.6"
"source": "https://github.com/symfony/console/tree/v5.3.7"
},
"funding": [
{
@ -5408,7 +5409,7 @@
"type": "tidelift"
}
],
"time": "2021-07-27T19:10:22+00:00"
"time": "2021-08-25T20:02:16+00:00"
},
{
"name": "symfony/deprecation-contracts",
@ -5882,16 +5883,16 @@
},
{
"name": "symfony/string",
"version": "v5.3.3",
"version": "v5.3.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1"
"reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1",
"reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1",
"url": "https://api.github.com/repos/symfony/string/zipball/8d224396e28d30f81969f083a58763b8b9ceb0a5",
"reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5",
"shasum": ""
},
"require": {
@ -5945,7 +5946,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v5.3.3"
"source": "https://github.com/symfony/string/tree/v5.3.7"
},
"funding": [
{
@ -5961,7 +5962,7 @@
"type": "tidelift"
}
],
"time": "2021-06-27T11:44:38+00:00"
"time": "2021-08-26T08:00:08+00:00"
},
{
"name": "theseer/tokenizer",
@ -6015,16 +6016,16 @@
},
{
"name": "twig/twig",
"version": "v2.14.6",
"version": "v2.14.7",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "27e5cf2b05e3744accf39d4c68a3235d9966d260"
"reference": "8e202327ee1ed863629de9b18a5ec70ac614d88f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/27e5cf2b05e3744accf39d4c68a3235d9966d260",
"reference": "27e5cf2b05e3744accf39d4c68a3235d9966d260",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/8e202327ee1ed863629de9b18a5ec70ac614d88f",
"reference": "8e202327ee1ed863629de9b18a5ec70ac614d88f",
"shasum": ""
},
"require": {
@ -6034,7 +6035,7 @@
},
"require-dev": {
"psr/container": "^1.0",
"symfony/phpunit-bridge": "^4.4.9|^5.0.9"
"symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0"
},
"type": "library",
"extra": {
@ -6078,7 +6079,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v2.14.6"
"source": "https://github.com/twigphp/Twig/tree/v2.14.7"
},
"funding": [
{
@ -6090,7 +6091,7 @@
"type": "tidelift"
}
],
"time": "2021-05-16T12:12:47+00:00"
"time": "2021-09-17T08:39:54+00:00"
},
{
"name": "vimeo/psalm",
@ -6248,18 +6249,9 @@
"time": "2015-12-17T08:42:14+00:00"
}
],
"aliases": [
{
"package": "utopia-php/database",
"version": "dev-feat-adjusted-query-validator",
"alias": "0.10.0",
"alias_normalized": "0.10.0.0"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"utopia-php/database": 20
},
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
@ -6281,5 +6273,5 @@
"platform-overrides": {
"php": "8.0"
},
"plugin-api-version": "2.1.0"
"plugin-api-version": "2.0.0"
}

View file

@ -3,6 +3,7 @@
namespace Tests\E2E\Services\Database;
use Tests\E2E\Client;
use Utopia\Database\Database;
trait DatabaseBase
{
@ -305,7 +306,7 @@ trait DatabaseBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'after' => $base['body']['documents'][0]['$id']
'cursor' => $base['body']['documents'][0]['$id']
]);
$this->assertEquals($documents['headers']['status-code'], 200);
@ -317,7 +318,7 @@ trait DatabaseBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'after' => $base['body']['documents'][2]['$id']
'cursor' => $base['body']['documents'][2]['$id']
]);
$this->assertEquals($documents['headers']['status-code'], 200);
@ -346,7 +347,7 @@ trait DatabaseBase
], $this->getHeaders()), [
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['ASC'],
'after' => $base['body']['documents'][1]['$id']
'cursor' => $base['body']['documents'][1]['$id']
]);
$this->assertEquals($documents['headers']['status-code'], 200);
@ -376,7 +377,7 @@ trait DatabaseBase
], $this->getHeaders()), [
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['DESC'],
'after' => $base['body']['documents'][1]['$id']
'cursor' => $base['body']['documents'][1]['$id']
]);
$this->assertEquals($documents['headers']['status-code'], 200);
@ -390,7 +391,7 @@ trait DatabaseBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'after' => 'unknown'
'cursor' => 'unknown'
]);
$this->assertEquals($documents['headers']['status-code'], 400);
@ -398,6 +399,114 @@ trait DatabaseBase
return [];
}
/**
* @depends testCreateDocument
*/
public function testListDocumentsBeforePagination(array $data):array
{
/**
* Test before without order.
*/
$base = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($base['headers']['status-code'], 200);
$this->assertEquals('Captain America', $base['body']['documents'][0]['title']);
$this->assertEquals('Spider-Man: Far From Home', $base['body']['documents'][1]['title']);
$this->assertEquals('Spider-Man: Homecoming', $base['body']['documents'][2]['title']);
$this->assertCount(3, $base['body']['documents']);
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['documents'][3]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
]);
$this->assertEquals($documents['headers']['status-code'], 200);
$this->assertEquals($base['body']['documents'][1]['$id'], $documents['body']['documents'][0]['$id']);
$this->assertEquals($base['body']['documents'][2]['$id'], $documents['body']['documents'][1]['$id']);
$this->assertCount(2, $documents['body']['documents']);
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['documents'][0]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
]);
$this->assertEquals($documents['headers']['status-code'], 200);
$this->assertEmpty($documents['body']['documents']);
/**
* Test with ASC order and after.
*/
$base = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['ASC'],
]);
$this->assertEquals($base['headers']['status-code'], 200);
$this->assertEquals(1944, $base['body']['documents'][0]['releaseYear']);
$this->assertEquals(2017, $base['body']['documents'][1]['releaseYear']);
$this->assertEquals(2019, $base['body']['documents'][2]['releaseYear']);
$this->assertCount(3, $base['body']['documents']);
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['ASC'],
'cursor' => $base['body']['documents'][1]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
]);
$this->assertEquals($documents['headers']['status-code'], 200);
$this->assertEquals($base['body']['documents'][0]['$id'], $documents['body']['documents'][0]['$id']);
$this->assertCount(1, $documents['body']['documents']);
/**
* Test with DESC order and after.
*/
$base = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['DESC'],
]);
$this->assertEquals($base['headers']['status-code'], 200);
$this->assertEquals(1944, $base['body']['documents'][2]['releaseYear']);
$this->assertEquals(2017, $base['body']['documents'][1]['releaseYear']);
$this->assertEquals(2019, $base['body']['documents'][0]['releaseYear']);
$this->assertCount(3, $base['body']['documents']);
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['DESC'],
'cursor' => $base['body']['documents'][1]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
]);
$this->assertEquals($documents['headers']['status-code'], 200);
$this->assertEquals($base['body']['documents'][0]['$id'], $documents['body']['documents'][0]['$id']);
$this->assertCount(1, $documents['body']['documents']);
return [];
}
/**
* @depends testCreateDocument
*/

View file

@ -6,6 +6,7 @@ use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
use Tests\E2E\Client;
use Utopia\Database\Database;
class DatabaseCustomServerTest extends Scope
{
@ -78,7 +79,7 @@ class DatabaseCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'after' => $base['body']['collections'][0]['$id']
'cursor' => $base['body']['collections'][0]['$id']
]);
$this->assertCount(1, $collections['body']['collections']);
@ -88,11 +89,53 @@ class DatabaseCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'after' => $base['body']['collections'][1]['$id']
'cursor' => $base['body']['collections'][1]['$id']
]);
$this->assertCount(0, $collections['body']['collections']);
$this->assertEmpty($collections['body']['collections']);
/**
* Test for Before
*/
$base = $this->client->call(Client::METHOD_GET, '/database/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$collections = $this->client->call(Client::METHOD_GET, '/database/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['collections'][1]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
]);
$this->assertCount(1, $collections['body']['collections']);
$this->assertEquals($base['body']['collections'][0]['$id'], $collections['body']['collections'][0]['$id']);
$collections = $this->client->call(Client::METHOD_GET, '/database/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['collections'][0]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
]);
$this->assertCount(0, $collections['body']['collections']);
$this->assertEmpty($collections['body']['collections']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/database/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => 'unknown',
]);
$this->assertEquals($response['headers']['status-code'], 400);
}
public function testDeleteAttribute(): array

View file

@ -7,6 +7,7 @@ use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideClient;
use Utopia\Database\Database;
class FunctionsCustomClientTest extends Scope
{
@ -253,11 +254,35 @@ class FunctionsCustomClientTest extends Scope
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'after' => $base['body']['executions'][0]['$id']
'cursor' => $base['body']['executions'][0]['$id']
]);
$this->assertCount(1, $executions['body']['executions']);
$this->assertEquals($base['body']['executions'][1]['$id'], $executions['body']['executions'][0]['$id']);
$executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions', [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'cursor' => $base['body']['executions'][1]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
]);
$this->assertCount(1, $executions['body']['executions']);
$this->assertEquals($base['body']['executions'][0]['$id'], $executions['body']['executions'][0]['$id']);
/**
* Test for FAILURE
*/
$executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions', [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'cursor' => 'unknown'
]);
$this->assertEquals(400, $executions['headers']['status-code']);
}
}

View file

@ -7,6 +7,7 @@ use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
use Utopia\Database\Database;
class FunctionsCustomServerTest extends Scope
{
@ -115,13 +116,36 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'after' => $functions['body']['functions'][0]['$id']
'cursor' => $functions['body']['functions'][0]['$id']
]);
$this->assertEquals($response['headers']['status-code'], 200);
$this->assertCount(1, $response['body']['functions']);
$this->assertEquals($response['body']['functions'][0]['name'], 'Test 2');
$response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $functions['body']['functions'][1]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
]);
$this->assertEquals($response['headers']['status-code'], 200);
$this->assertCount(1, $response['body']['functions']);
$this->assertEquals($response['body']['functions'][0]['name'], 'Test');
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => 'unknown',
]);
$this->assertEquals($response['headers']['status-code'], 400);
return $data;
}

View file

@ -7,6 +7,7 @@ use Tests\E2E\Scopes\ProjectConsole;
use Tests\E2E\Scopes\SideClient;
use Tests\E2E\Services\Projects\ProjectsBase;
use Tests\E2E\Client;
use Utopia\Database\Database;
class ProjectsConsoleClientTest extends Scope
{
@ -144,16 +145,38 @@ class ProjectsConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),[
'after' => $response['body']['projects'][0]['$id']
'cursor' => $response['body']['projects'][0]['$id']
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']);
$this->assertCount(1, $response['body']['projects']);
$this->assertEquals('Project Test 2', $response['body']['projects'][0]['name']);
$response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),[
'cursor' => $response['body']['projects'][0]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']);
$this->assertCount(1, $response['body']['projects']);
$this->assertEquals('Project Test', $response['body']['projects'][0]['name']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()),[
'cursor' => 'unknown'
]);
$this->assertEquals(400, $response['headers']['status-code']);
return $data;
}

View file

@ -4,6 +4,7 @@ namespace Tests\E2E\Services\Storage;
use CURLFile;
use Tests\E2E\Client;
use Utopia\Database\Database;
use Utopia\Image\Image;
trait StorageBase
@ -190,16 +191,37 @@ trait StorageBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 1,
'after' => $files['body']['files'][0]['$id']
'cursor' => $files['body']['files'][0]['$id']
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($files['body']['files'][1]['$id'], $response['body']['files'][0]['$id']);
$this->assertCount(1, $response['body']['files']);
$response = $this->client->call(Client::METHOD_GET, '/storage/files', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 1,
'cursor' => $files['body']['files'][1]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($files['body']['files'][0]['$id'], $response['body']['files'][0]['$id']);
$this->assertCount(1, $response['body']['files']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/storage/files', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => 'unknown'
]);
$this->assertEquals(400, $response['headers']['status-code']);
return $data;
}

View file

@ -3,6 +3,7 @@
namespace Tests\E2E\Services\Teams;
use Tests\E2E\Client;
use Utopia\Database\Database;
trait TeamsBase
{
@ -189,7 +190,7 @@ trait TeamsBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 1,
'after' => $teams['body']['teams'][0]['$id']
'cursor' => $teams['body']['teams'][0]['$id']
]);
$this->assertEquals(200, $response['headers']['status-code']);
@ -198,9 +199,32 @@ trait TeamsBase
$this->assertCount(1, $response['body']['teams']);
$this->assertEquals($teams['body']['teams'][1]['$id'], $response['body']['teams'][0]['$id']);
$response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 1,
'cursor' => $teams['body']['teams'][1]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertGreaterThan(0, $response['body']['sum']);
$this->assertIsInt($response['body']['sum']);
$this->assertCount(1, $response['body']['teams']);
$this->assertEquals($teams['body']['teams'][0]['$id'], $response['body']['teams'][0]['$id']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => 'unknown'
]);
$this->assertEquals(400, $response['headers']['status-code']);
return [];
}

View file

@ -157,7 +157,7 @@ trait TeamsBaseClient
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'after' => $memberships['body']['memberships'][0]['$id']
'cursor' => $memberships['body']['memberships'][0]['$id']
]);
$this->assertEquals(200, $response['headers']['status-code']);

View file

@ -3,6 +3,7 @@
namespace Tests\E2E\Services\Users;
use Tests\E2E\Client;
use Utopia\Database\Database;
trait UsersBase
{
@ -75,7 +76,7 @@ trait UsersBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'after' => $response['body']['users'][0]['$id']
'cursor' => $response['body']['users'][0]['$id']
]);
$this->assertEquals($response['headers']['status-code'], 200);
@ -84,6 +85,33 @@ trait UsersBase
$this->assertCount(1, $response['body']['users']);
$this->assertEquals($response['body']['users'][0]['$id'], 'user1');
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => 'user1',
'cursorDirection' => Database::CURSOR_BEFORE
]);
$this->assertEquals($response['headers']['status-code'], 200);
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']['users']);
$this->assertCount(1, $response['body']['users']);
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => 'unknown'
]);
$this->assertEquals($response['headers']['status-code'], 400);
}
/**