1
0
Fork 0
mirror of synced 2024-06-14 08:44:49 +12:00

Merge branch '0.16.x' of https://github.com/appwrite/appwrite into feat-additional-compression-support

This commit is contained in:
Damodar Lohani 2022-08-31 02:55:26 +00:00
commit 63f95c0a5c
54 changed files with 1423 additions and 437 deletions

View file

@ -52,6 +52,13 @@ $collections = [
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_name'),
'type' => Database::INDEX_KEY,
'attributes' => ['name'],
'lengths' => [256],
'orders' => [Database::ORDER_ASC],
],
],
],
'collections' => [
@ -150,6 +157,27 @@ $collections = [
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_name'),
'type' => Database::INDEX_KEY,
'attributes' => ['name'],
'lengths' => [256],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_enabled'),
'type' => Database::INDEX_KEY,
'attributes' => ['enabled'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_documentSecurity'),
'type' => Database::INDEX_KEY,
'attributes' => ['documentSecurity'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
],
],
@ -975,6 +1003,28 @@ $collections = [
'array' => false,
'filters' => ['datetime'],
],
[
'$id' => ID::custom('accessedAt'),
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
[
'$id' => ID::custom('sdks'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => true,
'filters' => [],
],
],
'indexes' => [
[
@ -984,6 +1034,13 @@ $collections = [
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_accessedAt',
'type' => Database::INDEX_KEY,
'attributes' => ['accessedAt'],
'lengths' => [],
'orders' => [],
],
],
],
@ -1792,6 +1849,20 @@ $collections = [
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_name'),
'type' => Database::INDEX_KEY,
'attributes' => ['name'],
'lengths' => [128],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_total'),
'type' => Database::INDEX_KEY,
'attributes' => ['total'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
],
],
@ -1940,6 +2011,41 @@ $collections = [
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_userId'),
'type' => Database::INDEX_KEY,
'attributes' => ['userId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_teamId'),
'type' => Database::INDEX_KEY,
'attributes' => ['teamId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_invited'),
'type' => Database::INDEX_KEY,
'attributes' => ['invited'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_joined'),
'type' => Database::INDEX_KEY,
'attributes' => ['joined'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_confirm'),
'type' => Database::INDEX_KEY,
'attributes' => ['confirm'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
],
],
@ -2088,7 +2194,63 @@ $collections = [
'attributes' => ['search'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
]
],
[
'$id' => ID::custom('_key_name'),
'type' => Database::INDEX_KEY,
'attributes' => ['name'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_status'),
'type' => Database::INDEX_KEY,
'attributes' => ['status'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_runtime'),
'type' => Database::INDEX_KEY,
'attributes' => ['runtime'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_deployment'),
'type' => Database::INDEX_KEY,
'attributes' => ['deployment'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_schedule'),
'type' => Database::INDEX_KEY,
'attributes' => ['schedule'],
'lengths' => [128],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_scheduleNext'),
'type' => Database::INDEX_KEY,
'attributes' => ['scheduleNext'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_schedulePrevious'),
'type' => Database::INDEX_KEY,
'attributes' => ['schedulePrevious'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_timeout'),
'type' => Database::INDEX_KEY,
'attributes' => ['timeout'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
],
],
@ -2241,6 +2403,34 @@ $collections = [
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_entrypoint'),
'type' => Database::INDEX_KEY,
'attributes' => ['entrypoint'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_size'),
'type' => Database::INDEX_KEY,
'attributes' => ['size'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_buildId'),
'type' => Database::INDEX_KEY,
'attributes' => ['buildId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_activate'),
'type' => Database::INDEX_KEY,
'attributes' => ['activate'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
],
],
@ -2513,6 +2703,34 @@ $collections = [
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_trigger'),
'type' => Database::INDEX_KEY,
'attributes' => ['trigger'],
'lengths' => [128],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_status'),
'type' => Database::INDEX_KEY,
'attributes' => ['status'],
'lengths' => [128],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_statusCode'),
'type' => Database::INDEX_KEY,
'attributes' => ['statusCode'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_time'),
'type' => Database::INDEX_KEY,
'attributes' => ['time'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
],
],
@ -2714,6 +2932,48 @@ $collections = [
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_enabled'),
'type' => Database::INDEX_KEY,
'attributes' => ['enabled'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_name'),
'type' => Database::INDEX_KEY,
'attributes' => ['name'],
'lengths' => [128],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_fileSecurity'),
'type' => Database::INDEX_KEY,
'attributes' => ['fileSecurity'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_maximumFileSize'),
'type' => Database::INDEX_KEY,
'attributes' => ['maximumFileSize'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_encryption'),
'type' => Database::INDEX_KEY,
'attributes' => ['encryption'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_antivirus'),
'type' => Database::INDEX_KEY,
'attributes' => ['antivirus'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
]
],
@ -3116,6 +3376,48 @@ $collections = [
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_name'),
'type' => Database::INDEX_KEY,
'attributes' => ['name'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_signature'),
'type' => Database::INDEX_KEY,
'attributes' => ['signature'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_mimeType'),
'type' => Database::INDEX_KEY,
'attributes' => ['mimeType'],
'lengths' => [127],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_sizeOriginal'),
'type' => Database::INDEX_KEY,
'attributes' => ['sizeOriginal'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_chunksTotal'),
'type' => Database::INDEX_KEY,
'attributes' => ['chunksTotal'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_chunksUploaded'),
'type' => Database::INDEX_KEY,
'attributes' => ['chunksUploaded'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
]
],
];

View file

@ -18,6 +18,9 @@ use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Template\Template;
use Appwrite\URL\URL as URLParser;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Query\Limit;
use Appwrite\Utopia\Database\Validator\Query\Offset;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use MaxMind\Db\Reader;
@ -1345,14 +1348,18 @@ App::get('/v1/account/logs')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_LOG_LIST)
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->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('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Only supported methods are limit and offset', true)
->inject('response')
->inject('user')
->inject('locale')
->inject('geodb')
->inject('dbForProject')
->action(function (int $limit, int $offset, Response $response, Document $user, Locale $locale, Reader $geodb, Database $dbForProject) {
->action(function (array $queries, Response $response, Document $user, Locale $locale, Reader $geodb, Database $dbForProject) {
$queries = Query::parseQueries($queries);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new EventAudit($dbForProject);

View file

@ -36,16 +36,16 @@ use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\IP;
use Appwrite\Network\Validator\URL;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\IndexedQueries;
use Appwrite\Utopia\Database\Validator\Query\Cursor as CursorQueryValidator;
use Appwrite\Utopia\Database\Validator\Query\Filter as FilterQueryValidator;
use Appwrite\Utopia\Database\Validator\Query\Limit as LimitQueryValidator;
use Appwrite\Utopia\Database\Validator\Query\Offset as OffsetQueryValidator;
use Appwrite\Utopia\Database\Validator\Query\Order as OrderQueryValidator;
use Appwrite\Utopia\Database\Validator\Query\Limit;
use Appwrite\Utopia\Database\Validator\Query\Offset;
use Appwrite\Utopia\Response;
use Appwrite\Detector\Detector;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\Collections;
use Appwrite\Utopia\Database\Validator\Queries\Databases;
use Appwrite\Utopia\Database\Validator\Queries\Documents;
use Utopia\Config\Config;
use MaxMind\Db\Reader;
@ -233,38 +233,36 @@ App::get('/v1/databases')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DATABASE_LIST)
->param('queries', [], new Databases(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Databases::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Maximum number of collection to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->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', 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) {
->action(function (array $queries, string $search, Response $response, Database $dbForProject) {
$filterQueries = [];
$queries = Query::parseQueries($queries);
if (!empty($search)) {
$filterQueries[] = Query::search('search', $search);
$queries[] = 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);
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
if ($cursor) {
/** @var Query $cursor */
$databaseId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('databases', $databaseId);
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Collection '{$cursor}' for the 'cursor' value not found.");
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Database '{$databaseId}' for the 'cursor' value not found.");
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
$cursor->setValue($cursorDocument);
}
$filterQueries = Query::groupByType($queries)['filters'];
$response->dynamic(new Document([
'databases' => $dbForProject->find('databases', \array_merge($filterQueries, $queries)),
'databases' => $dbForProject->find('databases', $queries),
'total' => $dbForProject->count('databases', $filterQueries, APP_LIMIT_COUNT),
]), Response::MODEL_DATABASE_LIST);
});
@ -296,24 +294,23 @@ App::get('/v1/databases/:databaseId')
});
App::get('/v1/databases/:databaseId/logs')
->desc('List Collection Logs')
->desc('List Database Logs')
->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('scope', 'databases.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'listLogs')
->label('sdk.description', '/docs/references/databases/get-collection-logs.md')
->label('sdk.description', '/docs/references/databases/get-logs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_LOG_LIST)
->param('databaseId', '', new UID(), 'Database ID.')
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->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('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Only supported methods are limit and offset', true)
->inject('response')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->action(function (string $databaseId, int $limit, int $offset, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
->action(function (string $databaseId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
$database = $dbForProject->getDocument('databases', $databaseId);
@ -321,9 +318,17 @@ App::get('/v1/databases/:databaseId/logs')
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$queries = Query::parseQueries($queries);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);
$resource = 'database/' . $databaseId;
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$output = [];
foreach ($logs as $i => &$log) {
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
@ -542,15 +547,11 @@ App::get('/v1/databases/:databaseId/collections')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_COLLECTION_LIST)
->param('databaseId', '', new UID(), 'Database ID.')
->param('queries', [], new Collections(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Collections::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Maximum number of collection to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->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', 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 $databaseId, string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject) {
->action(function (string $databaseId, array $queries, string $search, Response $response, Database $dbForProject) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
@ -558,30 +559,30 @@ App::get('/v1/databases/:databaseId/collections')
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$filterQueries = [];
$queries = Query::parseQueries($queries);
if (!empty($search)) {
$filterQueries[] = Query::search('search', $search);
$queries[] = 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);
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
if ($cursor) {
/** @var Query $cursor */
$collectionId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId);
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Collection '{$cursor}' for the 'cursor' value not found.");
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Collection '{$collectionId}' for the 'cursor' value not found.");
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER
? Query::cursorAfter($cursorDocument)
: Query::cursorBefore($cursorDocument);
$cursor->setValue($cursorDocument);
}
$filterQueries = Query::groupByType($queries)['filters'];
$response->dynamic(new Document([
'collections' => $dbForProject->find('database_' . $database->getInternalId(), \array_merge($filterQueries, $queries)),
'collections' => $dbForProject->find('database_' . $database->getInternalId(), $queries),
'total' => $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT),
]), Response::MODEL_COLLECTION_LIST);
});
@ -637,13 +638,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs')
->label('sdk.response.model', Response::MODEL_LOG_LIST)
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->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('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Only supported methods are limit and offset', true)
->inject('response')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->action(function (string $databaseId, string $collectionId, int $limit, int $offset, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
@ -657,6 +657,11 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs')
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$queries = Query::parseQueries($queries);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);
$resource = 'database/' . $databaseId . '/collection/' . $collectionId;
$logs = $audit->getLogsByResource($resource, $limit, $offset);
@ -1937,17 +1942,11 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
->label('sdk.response.model', Response::MODEL_DOCUMENT_LIST)
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/database#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
->param('limit', 25, new Range(0, 100), 'Maximum number of documents to return in response. By default will return maximum 25 results. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' results allowed per request.', true)
->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 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([Database::ORDER_DESC, Database::ORDER_ASC], true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of order directions for sorting attributes. 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('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
->inject('response')
->inject('dbForProject')
->inject('mode')
->action(function (string $databaseId, string $collectionId, array $queries, int $limit, int $offset, string $cursor, string $cursorDirection, array $orderAttributes, array $orderTypes, Response $response, Database $dbForProject, string $mode) {
->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, string $mode) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
@ -1970,71 +1969,41 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if (!empty($queries)) {
$attributes = array_merge(
$collection->getAttribute('attributes', []),
[
new Document([
'key' => '$id',
'type' => Database::VAR_STRING,
'array' => false,
]),
new Document([
'key' => '$createdAt',
'type' => Database::VAR_DATETIME,
'array' => false,
]),
new Document([
'key' => '$updatedAt',
'type' => Database::VAR_DATETIME,
'array' => false,
]),
]
);
$validator = new IndexedQueries(
$attributes,
$collection->getAttribute('indexes', []),
new CursorQueryValidator(),
new FilterQueryValidator($attributes),
new LimitQueryValidator(),
new OffsetQueryValidator(),
new OrderQueryValidator(),
);
if (!$validator->isValid($queries)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
// Validate queries
$queriesValidator = new Documents($collection->getAttribute('attributes'), $collection->getAttribute('indexes'));
$validQueries = $queriesValidator->isValid($queries);
if (!$validQueries) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $queriesValidator->getDescription());
}
$filterQueries = Query::parseQueries($queries);
$queries = Query::parseQueries($queries);
$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] ?? '');
}
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
if ($cursor) {
/** @var Query $cursor */
$documentId = $cursor->getValue();
if (!empty($cursor)) {
if ($documentSecurity && !$valid) {
$cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $cursor);
$cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId);
} else {
$cursorDocument = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $cursor));
$cursorDocument = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId));
}
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Document '{$cursor}' for the 'cursor' value not found.");
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Document '{$documentId}' for the 'cursor' value not found.");
}
$otherQueries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
$cursor->setValue($cursorDocument);
}
$allQueries = \array_merge($filterQueries, $otherQueries);
$filterQueries = Query::groupByType($queries)['filters'];
if ($documentSecurity && !$valid) {
$documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $allQueries);
$documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries);
$total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
} else {
$documents = Authorization::skip(fn () => $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $allQueries));
$documents = Authorization::skip(fn () => $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries));
$total = Authorization::skip(fn () => $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $filterQueries, APP_LIMIT_COUNT));
}
@ -2127,13 +2096,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('documentId', null, new UID(), 'Document ID.')
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->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('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Only supported methods are limit and offset', true)
->inject('response')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->action(function (string $databaseId, string $collectionId, string $documentId, int $limit, int $offset, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
@ -2153,6 +2121,11 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
}
$queries = Query::parseQueries($queries);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);
$resource = 'database/' . $databaseId . '/collection/' . $collectionId . '/document/' . $document->getId();
$logs = $audit->getLogsByResource($resource, $limit, $offset);

View file

@ -22,6 +22,9 @@ use Utopia\Storage\Validator\Upload;
use Appwrite\Utopia\Response;
use Utopia\Swoole\Request;
use Appwrite\Task\Validator\Cron;
use Appwrite\Utopia\Database\Validator\Queries\Deployments;
use Appwrite\Utopia\Database\Validator\Queries\Executions;
use Appwrite\Utopia\Database\Validator\Queries\Functions;
use Utopia\App;
use Utopia\Database\Database;
use Utopia\Database\Document;
@ -102,38 +105,36 @@ App::get('/v1/functions')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION_LIST)
->param('queries', [], new Functions(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Functions::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Maximum number of functions to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->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', 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) {
->action(function (array $queries, string $search, Response $response, Database $dbForProject) {
$filterQueries = [];
$queries = Query::parseQueries($queries);
if (!empty($search)) {
$filterQueries[] = Query::search('search', $search);
$queries[] = 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);
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
if ($cursor) {
/** @var Query $cursor */
$functionId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('functions', $functionId);
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Function '{$cursor}' for the 'cursor' value not found.");
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Function '{$functionId}' for the 'cursor' value not found.");
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
$cursor->setValue($cursorDocument);
}
$filterQueries = Query::groupByType($queries)['filters'];
$response->dynamic(new Document([
'functions' => $dbForProject->find('functions', \array_merge($filterQueries, $queries)),
'functions' => $dbForProject->find('functions', $queries),
'total' => $dbForProject->count('functions', $filterQueries, APP_LIMIT_COUNT),
]), Response::MODEL_FUNCTION_LIST);
});
@ -768,15 +769,11 @@ App::get('/v1/functions/:functionId/deployments')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DEPLOYMENT_LIST)
->param('functionId', '', new UID(), 'Function ID.')
->param('queries', [], new Deployments(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Deployments::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Maximum number of deployments to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->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', 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) {
->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject) {
$function = $dbForProject->getDocument('functions', $functionId);
@ -784,30 +781,33 @@ App::get('/v1/functions/:functionId/deployments')
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$filterQueries = [];
$queries = Query::parseQueries($queries);
if (!empty($search)) {
$filterQueries[] = Query::search('search', $search);
$queries[] = Query::search('search', $search);
}
$filterQueries[] = Query::equal('resourceId', [$function->getId()]);
$filterQueries[] = Query::equal('resourceType', ['functions']);
// Set resource queries
$queries[] = Query::equal('resourceId', [$function->getId()]);
$queries[] = Query::equal('resourceType', ['functions']);
$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);
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
if ($cursor) {
/** @var Query $cursor */
$deploymentId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('deployments', $deploymentId);
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Tag '{$cursor}' for the 'cursor' value not found.");
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Deployment '{$deploymentId}' for the 'cursor' value not found.");
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
$cursor->setValue($cursorDocument);
}
$results = $dbForProject->find('deployments', \array_merge($filterQueries, $queries));
$filterQueries = Query::groupByType($queries)['filters'];
$results = $dbForProject->find('deployments', $queries);
$total = $dbForProject->count('deployments', $filterQueries, APP_LIMIT_COUNT);
foreach ($results as $result) {
@ -1123,14 +1123,11 @@ App::get('/v1/functions/:functionId/executions')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_EXECUTION_LIST)
->param('functionId', '', new UID(), 'Function ID.')
->param('limit', 25, new Range(0, 100), 'Maximum number of executions to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->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('queries', [], new Executions(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Executions::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', 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. [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)
->inject('response')
->inject('dbForProject')
->action(function (string $functionId, int $limit, int $offset, string $search, string $cursor, string $cursorDirection, Response $response, Database $dbForProject) {
->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject) {
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
@ -1138,29 +1135,32 @@ App::get('/v1/functions/:functionId/executions')
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$filterQueries = [
Query::equal('functionId', [$function->getId()])
];
$queries = Query::parseQueries($queries);
if (!empty($search)) {
$filterQueries[] = Query::search('search', $search);
$queries[] = Query::search('search', $search);
}
$queries = [];
$queries[] = Query::limit($limit);
$queries[] = Query::offset($offset);
$queries[] = Query::orderDesc('');
if (!empty($cursor)) {
$cursorDocument = $dbForProject->getDocument('executions', $cursor);
// Set internal queries
$queries[] = Query::equal('functionId', [$function->getId()]);
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
if ($cursor) {
/** @var Query $cursor */
$executionId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('executions', $executionId);
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Tag '{$cursor}' for the 'cursor' value not found.");
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Execution '{$executionId}' for the 'cursor' value not found.");
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
$cursor->setValue($cursorDocument);
}
$results = $dbForProject->find('executions', \array_merge($filterQueries, $queries));
$filterQueries = Query::groupByType($queries)['filters'];
$results = $dbForProject->find('executions', $queries);
$total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT);
$roles = Authorization::getRoles();

View file

@ -865,6 +865,8 @@ App::post('/v1/projects/:projectId/keys')
'name' => $name,
'scopes' => $scopes,
'expire' => $expire,
'sdks' => [],
'accessedAt' => null,
'secret' => \bin2hex(\random_bytes(128)),
]);

View file

@ -24,6 +24,8 @@ use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Permissions;
use Utopia\Database\Validator\UID;
use Appwrite\Extend\Exception;
use Appwrite\Utopia\Database\Validator\Queries\Buckets;
use Appwrite\Utopia\Database\Validator\Queries\Files;
use Utopia\Image\Image;
use Utopia\Storage\Compression\Algorithms\GZIP;
use Utopia\Storage\Compression\Algorithms\Zstd;
@ -150,38 +152,36 @@ App::get('/v1/storage/buckets')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_BUCKET_LIST)
->param('queries', [], new Buckets(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Buckets::ALLOWED_ATTRIBUTES), true)
->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, 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', 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) {
->action(function (array $queries, string $search, Response $response, Database $dbForProject) {
$filterQueries = [];
$queries = Query::parseQueries($queries);
if (!empty($search)) {
$filterQueries[] = Query::search('name', $search);
$queries[] = 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('buckets', $cursor);
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
if ($cursor) {
/** @var Query $cursor */
$bucketId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('buckets', $bucketId);
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Bucket '{$cursor}' for the 'cursor' value not found.");
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Bucket '{$bucketId}' for the 'cursor' value not found.");
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
$cursor->setValue($cursorDocument);
}
$filterQueries = Query::groupByType($queries)['filters'];
$response->dynamic(new Document([
'buckets' => $dbForProject->find('buckets', \array_merge($filterQueries, $queries)),
'buckets' => $dbForProject->find('buckets', $queries),
'total' => $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT),
]), Response::MODEL_BUCKET_LIST);
});
@ -659,16 +659,12 @@ App::get('/v1/storage/buckets/:bucketId/files')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE_LIST)
->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
->param('queries', [], new Files(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Files::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Maximum number of files to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->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', 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('mode')
->action(function (string $bucketId, string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject, string $mode) {
->action(function (string $bucketId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
@ -683,35 +679,38 @@ App::get('/v1/storage/buckets/:bucketId/files')
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$filterQueries = [];
$queries = Query::parseQueries($queries);
if (!empty($search)) {
$filterQueries[] = Query::search('name', $search);
$queries[] = 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)) {
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
if ($cursor) {
/** @var Query $cursor */
$fileId = $cursor->getValue();
if ($fileSecurity && !$valid) {
$cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $cursor);
$cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
} else {
$cursorDocument = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $cursor));
$cursorDocument = Authorization::skip(fn() => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
}
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "File '{$cursor}' for the 'cursor' value not found.");
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "File '{$fileId}' for the 'cursor' value not found.");
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
$cursor->setValue($cursorDocument);
}
$filterQueries = Query::groupByType($queries)['filters'];
if ($fileSecurity && !$valid) {
$files = $dbForProject->find('bucket_' . $bucket->getInternalId(), \array_merge($filterQueries, $queries));
$files = $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries);
$total = $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
} else {
$files = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), \array_merge($filterQueries, $queries)));
$files = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries));
$total = Authorization::skip(fn () => $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT));
}

View file

@ -10,6 +10,11 @@ use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\Host;
use Appwrite\Template\Template;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\Memberships;
use Appwrite\Utopia\Database\Validator\Queries\Teams;
use Appwrite\Utopia\Database\Validator\Query\Limit;
use Appwrite\Utopia\Database\Validator\Query\Offset;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use MaxMind\Db\Reader;
@ -126,37 +131,35 @@ App::get('/v1/teams')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEAM_LIST)
->param('queries', [], new Teams(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Teams::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Maximum number of teams to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->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 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. [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)
->inject('response')
->inject('dbForProject')
->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject) {
->action(function (array $queries, string $search, Response $response, Database $dbForProject) {
$filterQueries = [];
$queries = Query::parseQueries($queries);
if (!empty($search)) {
$filterQueries[] = Query::search('search', $search);
$queries[] = 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);
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
if ($cursor) {
/** @var Query $cursor */
$teamId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('teams', $teamId);
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Team '{$cursor}' for the 'cursor' value not found.");
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Team '{$teamId}' for the 'cursor' value not found.");
}
$queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
$cursor->setValue($cursorDocument);
}
$results = $dbForProject->find('teams', \array_merge($filterQueries, $queries));
$filterQueries = Query::groupByType($queries)['filters'];
$results = $dbForProject->find('teams', $queries);
$total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
@ -464,15 +467,11 @@ App::get('/v1/teams/:teamId/memberships')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MEMBERSHIP_LIST)
->param('teamId', '', new UID(), 'Team ID.')
->param('queries', [], new Memberships(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Memberships::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Maximum number of memberships to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->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', 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) {
->action(function (string $teamId, array $queries, string $search, Response $response, Database $dbForProject) {
$team = $dbForProject->getDocument('teams', $teamId);
@ -480,29 +479,34 @@ App::get('/v1/teams/:teamId/memberships')
throw new Exception(Exception::TEAM_NOT_FOUND);
}
$filterQueries = [Query::equal('teamId', [$teamId])];
$queries = Query::parseQueries($queries);
if (!empty($search)) {
$filterQueries[] = Query::search('search', $search);
$queries[] = 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);
// Set internal queries
$queries[] = Query::equal('teamId', [$teamId]);
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
if ($cursor) {
/** @var Query $cursor */
$membershipId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('memberships', $membershipId);
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Membership '{$cursor}' for the 'cursor' value not found.");
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Membership '{$membershipId}' for the 'cursor' value not found.");
}
$otherQueries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument);
$cursor->setValue($cursorDocument);
}
$filterQueries = Query::groupByType($queries)['filters'];
$memberships = $dbForProject->find(
collection: 'memberships',
queries: \array_merge($filterQueries, $otherQueries),
queries: $queries,
);
$total = $dbForProject->count(
@ -851,18 +855,12 @@ App::get('/v1/teams/:teamId/logs')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_LOG_LIST)
->param('teamId', null, new UID(), 'Team ID.')
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->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('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Only supported methods are limit and offset', true)
->inject('response')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->action(function ($teamId, $limit, $offset, $response, $dbForProject, $locale, $geodb) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */
->action(function (string $teamId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
$team = $dbForProject->getDocument('teams', $teamId);
@ -870,6 +868,11 @@ App::get('/v1/teams/:teamId/logs')
throw new Exception(Exception::TEAM_NOT_FOUND);
}
$queries = Query::parseQueries($queries);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);
$resource = 'team/' . $team->getId();
$logs = $audit->getLogsByResource($resource, $limit, $offset);

View file

@ -355,12 +355,9 @@ App::get('/v1/users')
$queries[] = Query::search('search', $search);
}
// Set default limit
$queries[] = Query::limit(25);
// Get cursor document if there was a cursor query
$cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE)[0] ?? null;
if ($cursor !== null) {
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
if ($cursor) {
/** @var Query $cursor */
$userId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('users', $userId);
@ -544,7 +541,7 @@ App::get('/v1/users/:userId/logs')
$queries = Query::parseQueries($queries);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? 25;
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);

View file

@ -32,6 +32,7 @@ use Appwrite\Utopia\Request\Filters\V12 as RequestV12;
use Appwrite\Utopia\Request\Filters\V13 as RequestV13;
use Appwrite\Utopia\Request\Filters\V14 as RequestV14;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
Config::setParam('domainVerification', false);
Config::setParam('cookieDomain', 'localhost');
@ -47,7 +48,8 @@ App::init()
->inject('user')
->inject('locale')
->inject('clients')
->action(function (App $utopia, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, Document $user, Locale $locale, array $clients) {
->inject('servers')
->action(function (App $utopia, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, Document $user, Locale $locale, array $clients, array $servers) {
/*
* Request format
*/
@ -303,6 +305,28 @@ App::init()
Authorization::setRole(Auth::USER_ROLE_APPS);
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
$accessedAt = $key->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCCESS)) > $accessedAt) {
$key->setAttribute('accessedAt', DateTime::now());
$dbForConsole->updateDocument('keys', $key->getId(), $key);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
}
$sdkValidator = new WhiteList($servers, true);
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
if ($sdkValidator->isValid($sdk)) {
$sdks = $key->getAttribute('sdks', []);
if (!in_array($sdk, $sdks)) {
array_push($sdks, $sdk);
$key->setAttribute('sdks', $sdks);
/** Update access time as well */
$key->setAttribute('accessedAt', Datetime::now());
$dbForConsole->updateDocument('keys', $key->getId(), $key);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
}
}
}
}

View file

@ -7,6 +7,7 @@ use Utopia\Database\Document;
use Appwrite\Network\Validator\Host;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
use Utopia\App;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Integer;
@ -395,6 +396,35 @@ App::get('/v1/mock/tests/general/empty')
$response->noContent();
});
/** Endpoint to test if required headers are sent from the SDK */
App::get('/v1/mock/tests/general/headers')
->desc('Get headers')
->groups(['mock'])
->label('scope', 'public')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'general')
->label('sdk.method', 'headers')
->label('sdk.description', 'Return headers from the request')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.model', Response::MODEL_MOCK)
->label('sdk.mock', true)
->inject('request')
->inject('response')
->action(function (Request $request, Response $response) {
$res = [
'x-sdk-name' => $request->getHeader('x-sdk-name'),
'x-sdk-platform' => $request->getHeader('x-sdk-platform'),
'x-sdk-language' => $request->getHeader('x-sdk-language'),
'x-sdk-version' => $request->getHeader('x-sdk-version'),
];
$res = array_map(function ($key, $value) {
return $key . ': ' . $value;
}, array_keys($res), $res);
$res = implode("; ", $res);
$response->dynamic(new Document(['result' => $res]), Response::MODEL_MOCK);
});
App::get('/v1/mock/tests/general/400-error')
->desc('400 Error')
->groups(['mock'])

View file

@ -294,6 +294,7 @@ App::get('/console/databases/collection')
$permissions
->setParam('method', 'databases.getCollection')
->setParam('events', 'load,databases.updateCollection')
->setParam('form', 'collectionPermissions')
->setParam('data', 'project-collection')
->setParam('params', [
'collection-id' => '{{router.params.id}}',
@ -329,7 +330,6 @@ App::get('/console/databases/document')
->action(function (string $databaseId, string $collection, View $layout) {
$logs = new View(__DIR__ . '/../../views/console/comps/logs.phtml');
$logs
->setParam('interval', App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 0))
->setParam('method', 'databases.listDocumentLogs')
@ -341,15 +341,16 @@ App::get('/console/databases/document')
;
$permissions = new View(__DIR__ . '/../../views/console/comps/permissions-matrix.phtml');
$permissions
->setParam('method', 'databases.getDocument')
->setParam('events', 'load,databases.updateDocument')
->setParam('form', 'documentPermissions')
->setParam('data', 'project-document')
->setParam('permissions', \array_filter(
Database::PERMISSIONS,
fn ($perm) => $perm != Database::PERMISSION_CREATE
))
->setParam('permissions', [
Database::PERMISSION_READ,
Database::PERMISSION_UPDATE,
Database::PERMISSION_DELETE,
])
->setParam('params', [
'collection-id' => '{{router.params.collection}}',
'database-id' => '{{router.params.databaseId}}',
@ -384,10 +385,12 @@ App::get('/console/databases/document/new')
$permissions
->setParam('data', 'project-document')
->setParam('permissions', \array_filter(
Database::PERMISSIONS,
fn ($perm) => $perm != Database::PERMISSION_CREATE
))
->setParam('form', 'documentPermissions')
->setParam('permissions', [
Database::PERMISSION_READ,
Database::PERMISSION_UPDATE,
Database::PERMISSION_DELETE,
])
->setParam('params', [
'collection-id' => '{{router.params.collection}}',
'database-id' => '{{router.params.databaseId}}',
@ -451,20 +454,22 @@ App::get('/console/storage/bucket')
$fileCreatePermissions = new View(__DIR__ . '/../../views/console/comps/permissions-matrix.phtml');
$fileCreatePermissions
->setParam('form', 'fileCreatePermissions')
->setParam('permissions', \array_filter(
Database::PERMISSIONS,
fn ($perm) => $perm != Database::PERMISSION_CREATE
));
->setParam('permissions', [
Database::PERMISSION_READ,
Database::PERMISSION_UPDATE,
Database::PERMISSION_DELETE,
]);
$fileUpdatePermissions = new View(__DIR__ . '/../../views/console/comps/permissions-matrix.phtml');
$fileUpdatePermissions
->setParam('method', 'storage.getFile')
->setParam('data', 'file')
->setParam('form', 'fileUpdatePermissions')
->setParam('permissions', \array_filter(
Database::PERMISSIONS,
fn ($perm) => $perm != Database::PERMISSION_CREATE
))
->setParam('permissions', [
Database::PERMISSION_READ,
Database::PERMISSION_UPDATE,
Database::PERMISSION_DELETE,
])
->setParam('params', [
'bucket-id' => '{{router.params.id}}',
]);

View file

@ -90,6 +90,7 @@ const APP_LIMIT_COMPRESSION = 20000000; //20MB
const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value
const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length.
const APP_LIMIT_SUBQUERY = 1000;
const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 402;
const APP_VERSION_STABLE = '0.15.3';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
@ -1015,3 +1016,14 @@ App::setResource('sms', function () {
default => null
};
});
App::setResource('servers', function () {
$platforms = Config::getParam('platforms');
$server = $platforms[APP_PLATFORM_SERVER];
$languages = array_map(function ($language) {
return strtolower($language['name']);
}, $server['languages']);
return $languages;
});

View file

@ -178,6 +178,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
->setLicense($license)
->setLicenseContent($licenseContent)
->setVersion($language['version'])
->setPlatform($key)
->setGitURL($language['url'])
->setGitRepo($language['gitUrl'])
->setGitRepoName($language['gitRepoName'])

View file

@ -7,7 +7,7 @@ $params = $this->getParam('params', []);
$events = $this->getParam('events', '');
$permissions = $this->getParam('permissions', Database::PERMISSIONS);
$data = $this->getParam('data', '');
$form = $this->getParam('form', 'form');
$form = $this->getParam('form');
$escapedPermissions = \array_map(function ($perm) {
// Alpine won't bind to a parameter named delete
@ -34,7 +34,7 @@ $escapedPermissions = \array_map(function ($perm) {
<?php if (!empty($data)): ?>
data-name="<?php echo $data; ?>"
<?php endif; ?>
@reset.window="permissions = rawPermissions = []">
@reset.window="permissions.length = rawPermissions.length = 0">
<input
type="hidden"
@ -79,7 +79,7 @@ $escapedPermissions = \array_map(function ($perm) {
</tr>
</template>
<tr x-data="permissionsRow"
@addrow.window="addPermission('<?php echo $form; ?>',role,{<?php echo \implode(',', $escapedPermissions) ?>})">
@addrow<?php echo \strtolower($form); ?>.window="addPermission('<?php echo $form; ?>', role, { <?php echo \implode(',', $escapedPermissions) ?> })">
<td>
<datalist id="types">
<option value="user:">
@ -91,7 +91,7 @@ $escapedPermissions = \array_map(function ($perm) {
<input
required
id="<?php echo $form; ?>"
id="<?php echo $form; ?>Input"
name="<?php echo $form; ?>"
form="<?php echo $form ?>"
list="types"
@ -109,7 +109,7 @@ $escapedPermissions = \array_map(function ($perm) {
<tfoot>
<tr>
<td colspan="<?php \count($permissions) + 2 ?>">
<button type="button" class="btn btn-primary margin-top-small" @click="$dispatch('addrow')">Add</button>
<button type="button" class="margin-top-small reverse" @click="$dispatch('addrow<?php echo \strtolower($form); ?>')">Add</button>
</td>
</tr>
</tfoot>

View file

@ -514,7 +514,7 @@ $permissions = $this->getParam('permissions', null);
<div class="row responsive margin-top-negative">
<div class="col span-8 margin-bottom">
<form id="<?php echo $permissions->getParam('form', 'permissions') ?>"></form>
<form id="<?php echo $permissions->getParam('form') ?>"></form>
<form
data-analytics

View file

@ -53,7 +53,7 @@ $permissions = $this->getParam('permissions', null);
<div class="row responsive">
<div class="col span-8 margin-bottom">
<form id="<?php echo $permissions->getParam('form', 'permissions') ?>"></form>
<form id="<?php echo $permissions->getParam('form') ?>"></form>
<form
data-analytics

View file

@ -108,7 +108,7 @@ $fileUpdatePermissions = $this->getParam('fileUpdatePermissions', null);
<td class="col-name" data-title="Name: " data-ls-attrs="title={{file.name}}" >
<div class="trim-inner-text">
<span data-ls-ui-trigger="modal-file-update-{{file.$id}}" class="link text-one-liner" data-ls-bind="{{file.name}}"></span>
<div data-ls-attrs="data-open-event=modal-file-update-{{file.$id}}" data-button-hide="1" data-ui-modal class="box modal sticky-footer width-large close" >
<div data-ls-attrs="data-open-event=modal-file-update-{{file.$id}}" data-button-hide="1" data-ui-modal class="box modal width-xlarge sticky-footer close" >
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Update File</h1>
@ -139,12 +139,11 @@ $fileUpdatePermissions = $this->getParam('fileUpdatePermissions', null);
</div>
<input type="hidden" data-ls-attrs="id=file-bucketId-{{file.$id}}" name="bucketId" data-ls-bind="{{file.bucketId}}">
<div class="toggle margin-bottom" data-ls-if="{{project-bucket.fileSecurity}}" data-ls-ui-open data-button-aria="Open Permissions">
<i class="icon-plus pull-end margin-top-tiny"></i>
<i class="icon-minus pull-end margin-top-tiny"></i>
<hr class="margin-start margin-end" />
<h3 class="margin-bottom-large">Permissions</h3>
<h3 class="margin-bottom">Permissions</h3>
<div class="margin-start margin-end margin-bottom">
<?php echo $fileUpdatePermissions->render(); ?>
</div>
</form>

View file

@ -77,8 +77,8 @@
}
],
"require-dev": {
"appwrite/sdk-generator": "dev-feat-new-headers",
"ext-fileinfo": "*",
"appwrite/sdk-generator": "dev-master as 0.19.5",
"phpunit/phpunit": "9.5.20",
"squizlabs/php_codesniffer": "^3.6",
"swoole/ide-helper": "4.8.9",

32
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": "b1491fd16c44267bc71fb96581278b3f",
"content-hash": "26191185928bb538d77ec04e4e686b82",
"packages": [
{
"name": "adhocore/jwt",
@ -2560,12 +2560,12 @@
"source": {
"type": "git",
"url": "https://github.com/utopia-php/storage.git",
"reference": "e049f2c10b9cb84b4490850d7b9e7360352c36d2"
"reference": "ef483d34eb8bd361495498184a341bee3861b24a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/e049f2c10b9cb84b4490850d7b9e7360352c36d2",
"reference": "e049f2c10b9cb84b4490850d7b9e7360352c36d2",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/ef483d34eb8bd361495498184a341bee3861b24a",
"reference": "ef483d34eb8bd361495498184a341bee3861b24a",
"shasum": ""
},
"require": {
@ -2607,7 +2607,7 @@
"issues": "https://github.com/utopia-php/storage/issues",
"source": "https://github.com/utopia-php/storage/tree/feat-zstd-compression"
},
"time": "2022-08-22T09:19:07+00:00"
"time": "2022-08-31T02:09:02+00:00"
},
{
"name": "utopia-php/swoole",
@ -2840,16 +2840,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "dev-master",
"version": "dev-feat-new-headers",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "6e630a62f522ac68a7056bebf81cd032c7a053ba"
"reference": "532d4d15ec8f11539972f2848c2e230c49b24f65"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/6e630a62f522ac68a7056bebf81cd032c7a053ba",
"reference": "6e630a62f522ac68a7056bebf81cd032c7a053ba",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/532d4d15ec8f11539972f2848c2e230c49b24f65",
"reference": "532d4d15ec8f11539972f2848c2e230c49b24f65",
"shasum": ""
},
"require": {
@ -2864,7 +2864,6 @@
"brianium/paratest": "^6.4",
"phpunit/phpunit": "^9.5.21"
},
"default-branch": true,
"type": "library",
"autoload": {
"psr-4": {
@ -2885,9 +2884,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": {
"issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/master"
"source": "https://github.com/appwrite/sdk-generator/tree/feat-new-headers"
},
"time": "2022-08-29T10:43:33+00:00"
"time": "2022-08-20T07:42:55+00:00"
},
{
"name": "doctrine/instantiator",
@ -5359,14 +5358,7 @@
"time": "2022-08-12T06:47:24+00:00"
}
],
"aliases": [
{
"package": "appwrite/sdk-generator",
"version": "9999999-dev",
"alias": "0.19.5",
"alias_normalized": "0.19.5.0"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"utopia-php/storage": 20,

View file

@ -0,0 +1 @@
Get the database activity logs list by its unique ID.

View file

@ -4054,11 +4054,13 @@ if(this.attribute){event+=`.${this.attribute}`;}
this.events.add(event);this.reset();},removeEvent(value){this.events.delete(value);}}));});})(window);(function(window){document.addEventListener('alpine:init',()=>{Alpine.data('permissionsMatrix',()=>({permissions:[],rawPermissions:[],load(permissions){if(permissions===undefined){return;}
this.rawPermissions=permissions;permissions.map(p=>{let{type,role}=this.parsePermission(p);type=this.parseInputPermission(type);let index=-1;let existing=this.permissions.find((p,idx)=>{if(p.role===role){index=idx;return true;}})
if(existing===undefined){this.permissions.push({role,[type]:true,});}
if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId,role,permissions){if(!document.getElementById(formId).reportValidity()){return;}
if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId,role,permissions){if(!this.validate(formId,role,permissions)){return;}
Object.entries(permissions).forEach(entry=>{let[type,enabled]=entry;type=this.parseOutputPermission(type);if(enabled){this.rawPermissions.push(this.buildPermission(type,role));}});this.permissions.push({role,...permissions,});this.reset();},updatePermission(index){setTimeout(()=>{const permission=this.permissions[index];Object.keys(permission).forEach(key=>{if(key==='role'){return;}
const parsedKey=this.parseOutputPermission(key);const permissionString=this.buildPermission(parsedKey,permission.role);if(permission[key]){if(!this.rawPermissions.includes(permissionString)){this.rawPermissions.push(permissionString);}}else{this.rawPermissions=this.rawPermissions.filter(p=>{return!p.includes(permissionString);});}});});},removePermission(index){let row=this.permissions.splice(index,1);if(row.length===1){this.rawPermissions=this.rawPermissions.filter(p=>!p.includes(row[0].role));}},parsePermission(permission){let parts=permission.split('(');let type=parts[0];let role=parts[1].replace(')','').replace(' ','').replaceAll('"','');return{type,role};},buildPermission(type,role){return`${type}("${role}")`},parseInputPermission(key){if(key==='delete'){return'xdelete';}
return key;},parseOutputPermission(key){if(key==='xdelete'){return'delete';}
return key;}}));Alpine.data('permissionsRow',()=>({role:'',read:false,create:false,update:false,xdelete:false,reset(){this.role='';this.read=this.create=this.update=this.xdelete=false;}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
return key;},validate(formId,role,permissions){const form=document.getElementById(formId);const input=document.getElementById(`${formId}Input`);input.setCustomValidity('');if(!Object.values(permissions).some(p=>p)){input.setCustomValidity('No permissions selected');}
if(this.permissions.some(p=>p.role===role)){input.setCustomValidity('Role entry already exists');}
return form.reportValidity();}}));Alpine.data('permissionsRow',()=>({role:'',read:false,create:false,update:false,xdelete:false,reset(){this.role='';this.read=this.create=this.update=this.xdelete=false;}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},6000);};},redirect:function(url){return function(router){if(url==="/console"){window.location=url;return;}
router.change(url||"/");};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
return url;}}else{if(typeof value!=="undefined"&&value!==null){var separator=url.indexOf("?")!==-1?"&":"?";hash=url.split("#");url=hash[0]+separator+key+"="+value;if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}

View file

@ -609,11 +609,13 @@ if(this.attribute){event+=`.${this.attribute}`;}
this.events.add(event);this.reset();},removeEvent(value){this.events.delete(value);}}));});})(window);(function(window){document.addEventListener('alpine:init',()=>{Alpine.data('permissionsMatrix',()=>({permissions:[],rawPermissions:[],load(permissions){if(permissions===undefined){return;}
this.rawPermissions=permissions;permissions.map(p=>{let{type,role}=this.parsePermission(p);type=this.parseInputPermission(type);let index=-1;let existing=this.permissions.find((p,idx)=>{if(p.role===role){index=idx;return true;}})
if(existing===undefined){this.permissions.push({role,[type]:true,});}
if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId,role,permissions){if(!document.getElementById(formId).reportValidity()){return;}
if(index!==-1){existing[type]=true;this.permissions[index]=existing;}});},addPermission(formId,role,permissions){if(!this.validate(formId,role,permissions)){return;}
Object.entries(permissions).forEach(entry=>{let[type,enabled]=entry;type=this.parseOutputPermission(type);if(enabled){this.rawPermissions.push(this.buildPermission(type,role));}});this.permissions.push({role,...permissions,});this.reset();},updatePermission(index){setTimeout(()=>{const permission=this.permissions[index];Object.keys(permission).forEach(key=>{if(key==='role'){return;}
const parsedKey=this.parseOutputPermission(key);const permissionString=this.buildPermission(parsedKey,permission.role);if(permission[key]){if(!this.rawPermissions.includes(permissionString)){this.rawPermissions.push(permissionString);}}else{this.rawPermissions=this.rawPermissions.filter(p=>{return!p.includes(permissionString);});}});});},removePermission(index){let row=this.permissions.splice(index,1);if(row.length===1){this.rawPermissions=this.rawPermissions.filter(p=>!p.includes(row[0].role));}},parsePermission(permission){let parts=permission.split('(');let type=parts[0];let role=parts[1].replace(')','').replace(' ','').replaceAll('"','');return{type,role};},buildPermission(type,role){return`${type}("${role}")`},parseInputPermission(key){if(key==='delete'){return'xdelete';}
return key;},parseOutputPermission(key){if(key==='xdelete'){return'delete';}
return key;}}));Alpine.data('permissionsRow',()=>({role:'',read:false,create:false,update:false,xdelete:false,reset(){this.role='';this.read=this.create=this.update=this.xdelete=false;}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
return key;},validate(formId,role,permissions){const form=document.getElementById(formId);const input=document.getElementById(`${formId}Input`);input.setCustomValidity('');if(!Object.values(permissions).some(p=>p)){input.setCustomValidity('No permissions selected');}
if(this.permissions.some(p=>p.role===role)){input.setCustomValidity('Role entry already exists');}
return form.reportValidity();}}));Alpine.data('permissionsRow',()=>({role:'',read:false,create:false,update:false,xdelete:false,reset(){this.role='';this.read=this.create=this.update=this.xdelete=false;}}));});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},6000);};},redirect:function(url){return function(router){if(url==="/console"){window.location=url;return;}
router.change(url||"/");};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
return url;}}else{if(typeof value!=="undefined"&&value!==null){var separator=url.indexOf("?")!==-1?"&":"?";hash=url.split("#");url=hash[0]+separator+key+"="+value;if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -34,7 +34,7 @@
});
},
addPermission(formId, role, permissions) {
if (!document.getElementById(formId).reportValidity()) {
if (!this.validate(formId, role, permissions)) {
return;
}
Object.entries(permissions).forEach(entry => {
@ -105,6 +105,21 @@
return 'delete';
}
return key;
},
validate(formId, role, permissions) {
const form = document.getElementById(formId);
const input = document.getElementById(`${formId}Input`);
input.setCustomValidity('');
if (!Object.values(permissions).some(p => p)) {
input.setCustomValidity('No permissions selected');
}
if (this.permissions.some(p => p.role === role)) {
input.setCustomValidity('Role entry already exists');
}
return form.reportValidity();
}
}));
Alpine.data('permissionsRow', () => ({

View file

@ -89,6 +89,10 @@
max-width: 800px;
}
&.width-xlarge {
max-width: 950px;
}
&.open {
display: block;
}

View file

@ -1,13 +1,12 @@
.permissions-matrix {
th:first-child, td:first-child {
width: 100px;
width: 35%;
}
th:not(:first-child):not(:last-child), td:not(:first-child):not(:last-child) {
width: 50px;
text-align: center;
}
th:last-child, td:last-child {
width: 20px;
width: 10%;
}
td {
vertical-align: middle;

View file

@ -2,7 +2,7 @@
namespace Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\IndexedQueries;
use Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Query\Limit;
use Appwrite\Utopia\Database\Validator\Query\Offset;
use Appwrite\Utopia\Database\Validator\Query\Cursor;
@ -12,7 +12,7 @@ use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
class Collection extends IndexedQueries
class Base extends Queries
{
/**
* Expression constructor
@ -49,30 +49,16 @@ class Collection extends IndexedQueries
'array' => false,
]);
$attributes[] = new Document([
'$id' => '$createdAt',
'key' => '$createdAt',
'type' => Database::VAR_DATETIME,
'array' => false,
]);
$attributes[] = new Document([
'$id' => '$updatedAt',
'key' => '$updatedAt',
'type' => Database::VAR_DATETIME,
'array' => false,
]);
$indexes = [];
foreach ($allowedAttributes as $attribute) {
$indexes[] = new Document([
'status' => 'available',
'type' => Database::INDEX_KEY,
'attributes' => [$attribute]
]);
}
$indexes[] = new Document([
'status' => 'available',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search']
]);
$validators = [
new Limit(),
new Offset(),
@ -81,6 +67,6 @@ class Collection extends IndexedQueries
new Order($attributes),
];
parent::__construct($attributes, $indexes, ...$validators);
parent::__construct(...$validators);
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Buckets extends Base
{
public const ALLOWED_ATTRIBUTES = [
'enabled',
'name',
'fileSecurity',
'maximumFileSize',
'encryption',
'antivirus'
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('buckets', self::ALLOWED_ATTRIBUTES);
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Collections extends Base
{
public const ALLOWED_ATTRIBUTES = [
'name',
'enabled',
'documentSecurity'
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('collections', self::ALLOWED_ATTRIBUTES);
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Databases extends Base
{
public const ALLOWED_ATTRIBUTES = [
'name'
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('databases', self::ALLOWED_ATTRIBUTES);
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Deployments extends Base
{
public const ALLOWED_ATTRIBUTES = [
'entrypoint',
'size',
'buildId',
'activate',
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('deployments', self::ALLOWED_ATTRIBUTES);
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\IndexedQueries;
use Appwrite\Utopia\Database\Validator\Query\Limit;
use Appwrite\Utopia\Database\Validator\Query\Offset;
use Appwrite\Utopia\Database\Validator\Query\Cursor;
use Appwrite\Utopia\Database\Validator\Query\Filter;
use Appwrite\Utopia\Database\Validator\Query\Order;
use Utopia\Database\Database;
use Utopia\Database\Document;
class Documents extends IndexedQueries
{
/**
* Expression constructor
*
* @param Document[] $attributes
* @param Document[] $indexes
*/
public function __construct(array $attributes, array $indexes)
{
$attributes[] = new Document([
'key' => '$id',
'type' => Database::VAR_STRING,
'array' => false,
]);
$attributes[] = new Document([
'key' => '$createdAt',
'type' => Database::VAR_DATETIME,
'array' => false,
]);
$attributes[] = new Document([
'key' => '$updatedAt',
'type' => Database::VAR_DATETIME,
'array' => false,
]);
$validators = [
new Limit(),
new Offset(),
new Cursor(),
new Filter($attributes),
new Order($attributes),
];
parent::__construct($attributes, $indexes, ...$validators);
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Executions extends Base
{
public const ALLOWED_ATTRIBUTES = [
'trigger',
'status',
'statusCode',
'time'
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('executions', self::ALLOWED_ATTRIBUTES);
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Files extends Base
{
public const ALLOWED_ATTRIBUTES = [
'name',
'signature',
'mimeType',
'sizeOriginal',
'chunksTotal',
'chunksUploaded'
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('files', self::ALLOWED_ATTRIBUTES);
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Functions extends Base
{
public const ALLOWED_ATTRIBUTES = [
'name',
'status',
'runtime',
'deployment',
'schedule',
'scheduleNext',
'schedulePrevious',
'timeout'
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('functions', self::ALLOWED_ATTRIBUTES);
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Memberships extends Base
{
public const ALLOWED_ATTRIBUTES = [
'userId',
'teamId',
'invited',
'joined',
'confirm'
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('memberships', self::ALLOWED_ATTRIBUTES);
}
}

View file

@ -0,0 +1,20 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
class Teams extends Base
{
public const ALLOWED_ATTRIBUTES = [
'name',
'total'
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('teams', self::ALLOWED_ATTRIBUTES);
}
}

View file

@ -2,9 +2,7 @@
namespace Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\Collection;
class Users extends Collection
class Users extends Base
{
public const ALLOWED_ATTRIBUTES = [
'name',
@ -14,8 +12,7 @@ class Users extends Collection
'passwordUpdate',
'registration',
'emailVerification',
'phoneVerification',
'search',
'phoneVerification'
];
/**

View file

@ -58,6 +58,19 @@ class Key extends Model
'default' => '',
'example' => '919c2d18fb5d4...a2ae413da83346ad2',
])
->addRule('accessedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Most recent access date in Unix timestamp.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE
])
->addRule('sdks', [
'type' => self::TYPE_STRING,
'description' => 'List of SDK user agents that used this key.',
'default' => null,
'example' => 'appwrite:flutter',
'array' => true
])
;
}

View file

@ -65,7 +65,7 @@ class User extends Model
->addRule('registration', [
'type' => self::TYPE_DATETIME,
'description' => 'User registration date in Datetime.',
'default' => null,
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('status', [

View file

@ -391,7 +391,7 @@ trait AccountBase
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [
'limit' => 1
'queries' => [ 'limit(1)' ],
]);
$this->assertEquals($responseLimit['headers']['status-code'], 200);
@ -408,7 +408,7 @@ trait AccountBase
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [
'offset' => 1
'queries' => [ 'offset(1)' ],
]);
$this->assertEquals($responseOffset['headers']['status-code'], 200);
@ -425,8 +425,7 @@ trait AccountBase
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
]), [
'limit' => 1,
'offset' => 1
'queries' => [ 'limit(1)', 'offset(1)' ],
]);
$this->assertEquals($responseLimitOffset['headers']['status-code'], 200);

View file

@ -985,8 +985,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['ASC'],
'queries' => [ 'orderAsc("releaseYear")' ],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@ -1006,8 +1005,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['DESC'],
'queries' => [ 'orderDesc("releaseYear")' ],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@ -1122,7 +1120,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['documents'][0]['$id']
'queries' => [ 'cursorAfter("' . $base['body']['documents'][0]['$id'] . '")' ],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@ -1134,7 +1132,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['documents'][2]['$id']
'queries' => [ 'cursorAfter("' . $base['body']['documents'][2]['$id'] . '")' ],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@ -1147,8 +1145,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['ASC'],
'queries' => [ 'orderAsc("releaseYear")' ],
]);
$this->assertEquals(200, $base['headers']['status-code']);
@ -1161,9 +1158,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['ASC'],
'cursor' => $base['body']['documents'][1]['$id']
'queries' => [ 'cursorAfter("' . $base['body']['documents'][1]['$id'] . '")', 'orderAsc("releaseYear")' ],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@ -1177,8 +1172,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['DESC'],
'queries' => [ 'orderDesc("releaseYear")' ],
]);
$this->assertEquals(200, $base['headers']['status-code']);
@ -1191,9 +1185,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['DESC'],
'cursor' => $base['body']['documents'][1]['$id']
'queries' => [ 'cursorAfter("' . $base['body']['documents'][1]['$id'] . '")', 'orderDesc("releaseYear")' ],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@ -1207,7 +1199,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => 'unknown'
'queries' => [ 'cursorAfter("unknown")' ],
]);
$this->assertEquals(400, $documents['headers']['status-code']);
@ -1239,8 +1231,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['documents'][2]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
'queries' => [ 'cursorBefore("' . $base['body']['documents'][2]['$id'] . '")' ],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@ -1252,8 +1243,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['documents'][0]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
'queries' => [ 'cursorBefore("' . $base['body']['documents'][0]['$id'] . '")' ],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@ -1266,8 +1256,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['ASC'],
'queries' => [ 'orderAsc("releaseYear")' ],
]);
$this->assertEquals(200, $base['headers']['status-code']);
@ -1280,10 +1269,7 @@ trait DatabasesBase
'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
'queries' => [ 'cursorBefore("' . $base['body']['documents'][1]['$id'] . '")', 'orderAsc("releaseYear")' ],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@ -1297,8 +1283,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['DESC'],
'queries' => [ 'orderDesc("releaseYear")' ],
]);
$this->assertEquals(200, $base['headers']['status-code']);
@ -1311,10 +1296,7 @@ trait DatabasesBase
'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
'queries' => [ 'cursorBefore("' . $base['body']['documents'][1]['$id'] . '")', 'orderDesc("releaseYear")' ],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@ -1334,9 +1316,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 1,
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['ASC'],
'queries' => [ 'limit(1)', 'orderAsc("releaseYear")' ],
]);
$this->assertEquals(200, $documents['headers']['status-code']);
@ -1347,10 +1327,7 @@ trait DatabasesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 2,
'offset' => 1,
'orderAttributes' => ['releaseYear'],
'orderTypes' => ['ASC'],
'queries' => [ 'limit(2)', 'offset(1)', 'orderAsc("releaseYear")' ],
]);
$this->assertEquals(200, $documents['headers']['status-code']);

View file

@ -50,15 +50,61 @@ class DatabasesCustomServerTest extends Scope
$this->assertEquals($test1['body']['$id'], $databases['body']['databases'][0]['$id']);
$this->assertEquals($test2['body']['$id'], $databases['body']['databases'][1]['$id']);
/**
* Test for Order
*/
$base = array_reverse($databases['body']['databases']);
$databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'orderType' => 'DESC'
'queries' => [ 'limit(1)' ],
]);
$this->assertEquals(200, $databases['headers']['status-code']);
$this->assertCount(1, $databases['body']['databases']);
$databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(1)' ],
]);
$this->assertEquals(200, $databases['headers']['status-code']);
$this->assertCount(1, $databases['body']['databases']);
$databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("name", ["Test 1", "Test 2"])' ],
]);
$this->assertEquals(200, $databases['headers']['status-code']);
$this->assertCount(2, $databases['body']['databases']);
$databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("name", "Test 2")' ],
]);
$this->assertEquals(200, $databases['headers']['status-code']);
$this->assertCount(1, $databases['body']['databases']);
$databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("$id", "first")' ],
]);
$this->assertEquals(200, $databases['headers']['status-code']);
$this->assertCount(1, $databases['body']['databases']);
/**
* Test for Order
*/
$databases = $this->client->call(Client::METHOD_GET, '/databases', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'orderDesc("")' ],
]);
$this->assertEquals(2, $databases['body']['total']);
@ -77,7 +123,7 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['databases'][0]['$id']
'queries' => [ 'cursorAfter("' . $base['body']['databases'][0]['$id'] . '")' ],
]);
$this->assertCount(1, $databases['body']['databases']);
@ -87,7 +133,7 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['databases'][1]['$id']
'queries' => [ 'cursorAfter("' . $base['body']['databases'][1]['$id'] . '")' ],
]);
$this->assertCount(0, $databases['body']['databases']);
@ -105,8 +151,7 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['databases'][1]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
'queries' => [ 'cursorBefore("' . $base['body']['databases'][1]['$id'] . '")' ],
]);
$this->assertCount(1, $databases['body']['databases']);
@ -116,8 +161,7 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['databases'][0]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
'queries' => [ 'cursorBefore("' . $base['body']['databases'][0]['$id'] . '")' ],
]);
$this->assertCount(0, $databases['body']['databases']);
@ -163,7 +207,7 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => 'unknown',
'queries' => [ 'cursorAfter("unknown")' ],
]);
$this->assertEquals(400, $response['headers']['status-code']);
@ -287,15 +331,56 @@ class DatabasesCustomServerTest extends Scope
$this->assertEquals($test1['body']['$id'], $collections['body']['collections'][0]['$id']);
$this->assertEquals($test2['body']['$id'], $collections['body']['collections'][1]['$id']);
/**
* Test for Order
*/
$base = array_reverse($collections['body']['collections']);
$collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'orderType' => 'DESC'
'queries' => [ 'limit(1)' ]
]);
$this->assertEquals(200, $collections['headers']['status-code']);
$this->assertCount(1, $collections['body']['collections']);
$collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(1)' ]
]);
$this->assertEquals(200, $collections['headers']['status-code']);
$this->assertCount(1, $collections['body']['collections']);
$collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("enabled", true)' ]
]);
$this->assertEquals(200, $collections['headers']['status-code']);
$this->assertCount(2, $collections['body']['collections']);
$collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("enabled", false)' ]
]);
$this->assertEquals(200, $collections['headers']['status-code']);
$this->assertCount(0, $collections['body']['collections']);
/**
* Test for Order
*/
$collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'orderDesc("")' ],
]);
$this->assertEquals(2, $collections['body']['total']);
@ -314,7 +399,7 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['collections'][0]['$id']
'queries' => [ 'cursorAfter("' . $base['body']['collections'][0]['$id'] . '")' ],
]);
$this->assertCount(1, $collections['body']['collections']);
@ -324,7 +409,7 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['collections'][1]['$id']
'queries' => [ 'cursorAfter("' . $base['body']['collections'][1]['$id'] . '")' ],
]);
$this->assertCount(0, $collections['body']['collections']);
@ -342,8 +427,7 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['collections'][1]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
'queries' => [ 'cursorBefore("' . $base['body']['collections'][1]['$id'] . '")' ],
]);
$this->assertCount(1, $collections['body']['collections']);
@ -353,8 +437,7 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $base['body']['collections'][0]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
'queries' => [ 'cursorBefore("' . $base['body']['collections'][0]['$id'] . '")' ],
]);
$this->assertCount(0, $collections['body']['collections']);
@ -400,7 +483,7 @@ class DatabasesCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => 'unknown',
'queries' => [ 'cursorAfter("unknown")' ],
]);
$this->assertEquals(400, $response['headers']['status-code']);

View file

@ -361,7 +361,51 @@ class FunctionsCustomClientTest extends Scope
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'cursor' => $base['body']['executions'][0]['$id']
'queries' => [ 'limit(1)' ]
]);
$this->assertEquals(200, $executions['headers']['status-code']);
$this->assertCount(1, $executions['body']['executions']);
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'queries' => [ 'offset(1)' ]
]);
$this->assertEquals(200, $executions['headers']['status-code']);
$this->assertCount(1, $executions['body']['executions']);
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'queries' => [ 'equal("status", ["completed"])' ]
]);
$this->assertEquals(200, $executions['headers']['status-code']);
$this->assertCount(2, $executions['body']['executions']);
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'queries' => [ 'equal("status", ["failed"])' ]
]);
$this->assertEquals(200, $executions['headers']['status-code']);
$this->assertCount(0, $executions['body']['executions']);
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'queries' => [ 'cursorAfter("' . $base['body']['executions'][0]['$id'] . '")' ],
]);
$this->assertCount(1, $executions['body']['executions']);
@ -372,8 +416,7 @@ class FunctionsCustomClientTest extends Scope
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'cursor' => $base['body']['executions'][1]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
'queries' => [ 'cursorBefore("' . $base['body']['executions'][1]['$id'] . '")' ],
]);
// Cleanup : Delete function

View file

@ -97,6 +97,46 @@ class FunctionsCustomServerTest extends Scope
$this->assertCount(1, $response['body']['functions']);
$this->assertEquals($response['body']['functions'][0]['name'], 'Test');
$response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(0)' ]
]);
$this->assertEquals($response['headers']['status-code'], 200);
$this->assertCount(0, $response['body']['functions']);
$response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(1)' ]
]);
$this->assertEquals($response['headers']['status-code'], 200);
$this->assertCount(0, $response['body']['functions']);
$response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("status", "disabled")' ]
]);
$this->assertEquals($response['headers']['status-code'], 200);
$this->assertCount(1, $response['body']['functions']);
$response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("status", "enabled")' ]
]);
$this->assertEquals($response['headers']['status-code'], 200);
$this->assertCount(0, $response['body']['functions']);
$response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@ -159,7 +199,7 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $functions['body']['functions'][0]['$id']
'queries' => [ 'cursorAfter("' . $functions['body']['functions'][0]['$id'] . '")' ],
]);
$this->assertEquals($response['headers']['status-code'], 200);
@ -170,8 +210,7 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $functions['body']['functions'][1]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
'queries' => [ 'cursorBefore("' . $functions['body']['functions'][1]['$id'] . '")' ],
]);
$this->assertEquals($response['headers']['status-code'], 200);
@ -185,7 +224,7 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => 'unknown',
'queries' => [ 'cursorAfter("unknown")' ],
]);
$this->assertEquals($response['headers']['status-code'], 400);
@ -413,6 +452,46 @@ class FunctionsCustomServerTest extends Scope
$this->assertCount(2, $function['body']['deployments']);
$this->assertEquals($function['body']['deployments'][0]['$id'], $data['deploymentId']);
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(1)' ]
]);
$this->assertEquals($function['headers']['status-code'], 200);
$this->assertCount(1, $function['body']['deployments']);
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(1)' ]
]);
$this->assertEquals($function['headers']['status-code'], 200);
$this->assertCount(1, $function['body']['deployments']);
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("entrypoint", "index.php")' ]
]);
$this->assertEquals($function['headers']['status-code'], 200);
$this->assertCount(2, $function['body']['deployments']);
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("entrypoint", "index.js")' ]
]);
$this->assertEquals($function['headers']['status-code'], 200);
$this->assertCount(0, $function['body']['deployments']);
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@ -549,6 +628,36 @@ class FunctionsCustomServerTest extends Scope
$this->assertCount(1, $function['body']['executions']);
$this->assertEquals($function['body']['executions'][0]['$id'], $data['executionId']);
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(0)' ]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(0, $response['body']['executions']);
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(1)' ]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(0, $response['body']['executions']);
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("trigger", "http")' ]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body']['executions']);
/**
* Test search queries
*/

View file

@ -851,6 +851,7 @@ class ProjectsConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $id,
'x-appwrite-key' => $keySecret,
'x-sdk-name' => 'python'
]));
$this->assertEquals(200, $response['headers']['status-code']);
@ -859,6 +860,7 @@ class ProjectsConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $id,
'x-appwrite-key' => $keySecret,
'x-sdk-name' => 'php'
]), [
'teamId' => ID::unique(),
'name' => 'Arsenal'
@ -866,6 +868,21 @@ class ProjectsConsoleClientTest extends Scope
$this->assertEquals(201, $response['headers']['status-code']);
/** Check that the API key has been updated */
$response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/keys/' . $keyId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
]), []);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertArrayHasKey('sdks', $response['body']);
$this->assertCount(2, $response['body']['sdks']);
$this->assertContains('python', $response['body']['sdks']);
$this->assertContains('php', $response['body']['sdks']);
$this->assertArrayHasKey('accessedAt', $response['body']);
$this->assertNotEmpty($response['body']['accessedAt']);
// Cleanup
$response = $this->client->call(Client::METHOD_DELETE, '/projects/' . $id . '/keys/' . $keyId, array_merge([
@ -1183,6 +1200,10 @@ class ProjectsConsoleClientTest extends Scope
$this->assertContains('teams.read', $response['body']['scopes']);
$this->assertContains('teams.write', $response['body']['scopes']);
$this->assertNotEmpty($response['body']['secret']);
$this->assertArrayHasKey('sdks', $response['body']);
$this->assertEmpty($response['body']['sdks']);
$this->assertArrayHasKey('accessedAt', $response['body']);
$this->assertEmpty($response['body']['accessedAt']);
$data = array_merge($data, [
'keyId' => $response['body']['$id'],
@ -1252,6 +1273,10 @@ class ProjectsConsoleClientTest extends Scope
$this->assertContains('teams.write', $response['body']['scopes']);
$this->assertCount(2, $response['body']['scopes']);
$this->assertNotEmpty($response['body']['secret']);
$this->assertArrayHasKey('sdks', $response['body']);
$this->assertEmpty($response['body']['sdks']);
$this->assertArrayHasKey('accessedAt', $response['body']);
$this->assertEmpty($response['body']['accessedAt']);
/**
* Test for FAILURE
@ -1360,6 +1385,10 @@ class ProjectsConsoleClientTest extends Scope
$this->assertContains('users.write', $response['body']['scopes']);
$this->assertContains('collections.read', $response['body']['scopes']);
$this->assertCount(3, $response['body']['scopes']);
$this->assertArrayHasKey('sdks', $response['body']);
$this->assertEmpty($response['body']['sdks']);
$this->assertArrayHasKey('accessedAt', $response['body']);
$this->assertEmpty($response['body']['accessedAt']);
$response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/keys/' . $keyId, array_merge([
'content-type' => 'application/json',
@ -1374,6 +1403,10 @@ class ProjectsConsoleClientTest extends Scope
$this->assertContains('users.write', $response['body']['scopes']);
$this->assertContains('collections.read', $response['body']['scopes']);
$this->assertCount(3, $response['body']['scopes']);
$this->assertArrayHasKey('sdks', $response['body']);
$this->assertEmpty($response['body']['sdks']);
$this->assertArrayHasKey('accessedAt', $response['body']);
$this->assertEmpty($response['body']['accessedAt']);
/**
* Test for FAILURE

View file

@ -307,6 +307,42 @@ trait StorageBase
$this->assertGreaterThan(0, $files['body']['total']);
$this->assertGreaterThan(0, count($files['body']['files']));
$files = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(0)' ]
]);
$this->assertEquals(200, $files['headers']['status-code']);
$this->assertEquals(0, count($files['body']['files']));
$files = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(1)' ]
]);
$this->assertEquals(200, $files['headers']['status-code']);
$this->assertEquals(0, count($files['body']['files']));
$files = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("mimeType", "image/png")' ]
]);
$this->assertEquals(200, $files['headers']['status-code']);
$this->assertEquals(1, count($files['body']['files']));
$files = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("mimeType", "image/jpeg")' ]
]);
$this->assertEquals(200, $files['headers']['status-code']);
$this->assertEquals(0, count($files['body']['files']));
/**
* Test for FAILURE unknown Bucket
*/

View file

@ -98,7 +98,47 @@ class StorageCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $response['body']['buckets'][0]['$id'],
'queries' => [ 'limit(1)' ],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body']['buckets']);
$response = $this->client->call(Client::METHOD_GET, '/storage/buckets', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(1)' ],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body']['buckets']);
$response = $this->client->call(Client::METHOD_GET, '/storage/buckets', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("$id", "bucket1")' ],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body']['buckets']);
$response = $this->client->call(Client::METHOD_GET, '/storage/buckets', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("fileSecurity", true)' ],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(2, $response['body']['buckets']);
$response = $this->client->call(Client::METHOD_GET, '/storage/buckets', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'cursorAfter("' . $response['body']['buckets'][0]['$id'] . '")' ],
]);
$this->assertEquals(200, $response['headers']['status-code']);

View file

@ -129,25 +129,41 @@ trait TeamsBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 2,
'queries' => [ 'limit(2)' ],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertGreaterThan(0, $response['body']['total']);
$this->assertIsInt($response['body']['total']);
$this->assertCount(2, $response['body']['teams']);
$this->assertEquals(2, count($response['body']['teams']));
$response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'offset' => 1,
'queries' => [ 'offset(1)' ],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertGreaterThan(0, $response['body']['total']);
$this->assertIsInt($response['body']['total']);
$this->assertGreaterThan(2, $response['body']['teams']);
$this->assertGreaterThan(1, count($response['body']['teams']));
$response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'greaterThanEqual("total", 0)' ],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertGreaterThan(2, count($response['body']['teams']));
$response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("name", ["Arsenal", "Newcastle"])' ],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(2, count($response['body']['teams']));
$response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([
'content-type' => 'application/json',
@ -192,7 +208,7 @@ trait TeamsBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 2,
'queries' => [ 'limit(2)' ],
]);
$this->assertEquals(200, $teams['headers']['status-code']);
@ -204,8 +220,7 @@ trait TeamsBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 1,
'cursor' => $teams['body']['teams'][0]['$id']
'queries' => [ 'limit(1)', 'cursorAfter("' . $teams['body']['teams'][0]['$id'] . '")' ],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@ -218,9 +233,7 @@ trait TeamsBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'limit' => 1,
'cursor' => $teams['body']['teams'][1]['$id'],
'cursorDirection' => Database::CURSOR_BEFORE
'queries' => [ 'limit(1)', 'cursorBefore("' . $teams['body']['teams'][1]['$id'] . '")' ],
]);
$this->assertEquals(200, $response['headers']['status-code']);
@ -236,7 +249,7 @@ trait TeamsBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => 'unknown'
'queries' => [ 'cursorAfter("unknown")' ],
]);
$this->assertEquals(400, $response['headers']['status-code']);

View file

@ -35,6 +35,46 @@ trait TeamsBaseClient
$membershipId = $response['body']['memberships'][0]['$id'];
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'limit(0)' ]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(0, $response['body']['memberships']);
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'offset(1)' ]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(0, $response['body']['memberships']);
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("confirm", true)' ]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body']['memberships']);
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [ 'equal("confirm", false)' ]
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(0, $response['body']['memberships']);
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@ -212,7 +252,7 @@ trait TeamsBaseClient
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'cursor' => $memberships['body']['memberships'][0]['$id']
'queries' => [ 'cursorAfter("' . $memberships['body']['memberships'][0]['$id'] . '")' ]
]);
$this->assertEquals(200, $response['headers']['status-code']);

View file

@ -1,6 +1,6 @@
<?php
namespace Tests\Unit\Stats;
namespace Tests\Unit\Usage;
use Appwrite\Usage\Stats;
use PHPUnit\Framework\TestCase;

View file

@ -2,7 +2,7 @@
namespace Tests\Unit\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\Collection;
use Appwrite\Utopia\Database\Validator\Queries\Base;
use PHPUnit\Framework\TestCase;
class CollectionTest extends TestCase
@ -17,27 +17,25 @@ class CollectionTest extends TestCase
public function testEmptyQueries(): void
{
$validator = new Collection('users', []);
$validator = new Base('users', []);
$this->assertEquals($validator->isValid([]), true);
}
public function testValid(): void
{
$validator = new Collection('users', ['name', 'search']);
$validator = new Base('users', ['name', 'search']);
$this->assertEquals(true, $validator->isValid(['cursorAfter("asdf")']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['equal("name", "value")']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['limit(10)']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['offset(10)']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['orderAsc("name")']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['search("search", "value")']), $validator->getDescription());
}
public function testMissingIndex(): void
{
$validator = new Collection('users', ['name']);
$validator = new Base('users', ['name']);
$this->assertEquals(false, $validator->isValid(['equal("dne", "value")']), $validator->getDescription());
$this->assertEquals(false, $validator->isValid(['orderAsc("dne")']), $validator->getDescription());
$this->assertEquals(false, $validator->isValid(['search("search", "value")']), $validator->getDescription());
}
}

View file

@ -30,7 +30,6 @@ class UsersTest extends TestCase
$this->assertEquals(true, $validator->isValid(['greaterThan("registration", "2020-10-15 06:38")']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['equal("emailVerification", true)']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['equal("phoneVerification", true)']), $validator->getDescription());
$this->assertEquals(true, $validator->isValid(['search("search", "value")']), $validator->getDescription());
/**
* Test for Failure