Merge branch '0.16.x' of https://github.com/appwrite/appwrite into feat-improve-keys
This commit is contained in:
commit
fe8295c2ec
|
@ -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],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
@ -1326,6 +1354,13 @@ $collections = [
|
|||
]
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => ID::custom('_key_name'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['name'],
|
||||
'lengths' => [256],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_email'),
|
||||
'type' => Database::INDEX_UNIQUE,
|
||||
|
@ -1340,6 +1375,41 @@ $collections = [
|
|||
'lengths' => [16],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_status'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['status'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_passwordUpdate'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['passwordUpdate'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_registration'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['registration'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_emailVerification'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['emailVerification'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_phoneVerification'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['phoneVerification'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_search'),
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
|
@ -1779,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],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
@ -1927,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],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
@ -2075,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],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
@ -2228,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],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
@ -2500,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],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
@ -2691,6 +2922,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],
|
||||
],
|
||||
]
|
||||
],
|
||||
|
||||
|
@ -3093,6 +3366,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],
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
|
|
@ -121,6 +121,16 @@ return [ // Ordered by ABC.
|
|||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'etsy' => [
|
||||
'name' => 'Etsy',
|
||||
'developers' => 'https://developers.etsy.com/',
|
||||
'icon' => 'icon-etsy',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'facebook' => [
|
||||
'name' => 'Facebook',
|
||||
'developers' => 'https://developers.facebook.com/',
|
||||
|
|
|
@ -59,9 +59,12 @@ return [
|
|||
'home',
|
||||
'console',
|
||||
'documents.read',
|
||||
'documents.write',
|
||||
'files.read',
|
||||
'files.write',
|
||||
'locale.read',
|
||||
'avatars.read',
|
||||
'execution.write',
|
||||
],
|
||||
],
|
||||
Auth::USER_ROLE_USERS => [
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ use Utopia\Database\Adapter\MariaDB;
|
|||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\Permissions;
|
||||
use Utopia\Database\Validator\Query as QueryValidator;
|
||||
use Utopia\Database\Validator\Structure;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Database\Exception\Authorization as AuthorizationException;
|
||||
|
@ -37,11 +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\Queries as QueriesValidator;
|
||||
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;
|
||||
|
||||
|
@ -229,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);
|
||||
});
|
||||
|
@ -292,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);
|
||||
|
||||
|
@ -317,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';
|
||||
|
||||
|
@ -538,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));
|
||||
|
||||
|
@ -554,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);
|
||||
});
|
||||
|
@ -633,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));
|
||||
|
||||
|
@ -653,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);
|
||||
|
@ -1802,6 +1811,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('usage.metric', 'documents.{scope}.requests.create')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}'])
|
||||
->label('abuse-limit', 120)
|
||||
->label('abuse-time', 60)
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'createDocument')
|
||||
|
@ -1931,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));
|
||||
|
||||
|
@ -1964,52 +1969,41 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
|
|||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
$filterQueries = \array_map(function ($query) {
|
||||
$query = Query::parse($query);
|
||||
|
||||
if (\count($query->getValues()) > 100) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_LIMIT_EXCEEDED, "You cannot use more than 100 query values on attribute '{$query->getAttribute()}'");
|
||||
// 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());
|
||||
}
|
||||
|
||||
return $query;
|
||||
}, $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);
|
||||
|
||||
if (!empty($allQueries)) {
|
||||
$attributes = $collection->getAttribute('attributes', []);
|
||||
$validator = new QueriesValidator(new QueryValidator($attributes), $attributes, $collection->getAttribute('indexes', []), true);
|
||||
if (!$validator->isValid($allQueries)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
}
|
||||
$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));
|
||||
}
|
||||
|
||||
|
@ -2102,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));
|
||||
|
||||
|
@ -2128,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);
|
||||
|
@ -2191,6 +2189,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
|
|||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{response.$id}')
|
||||
->label('usage.metric', 'documents.{scope}.requests.update')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}'])
|
||||
->label('abuse-limit', 60)
|
||||
->label('abuse-time', 60)
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'updateDocument')
|
||||
|
@ -2320,6 +2320,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
|
|||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{request.documentId}')
|
||||
->label('usage.metric', 'documents.{scope}.requests.delete')
|
||||
->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}'])
|
||||
->label('abuse-limit', 60)
|
||||
->label('abuse-time', 60)
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'databases')
|
||||
->label('sdk.method', 'deleteDocument')
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -25,6 +25,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\Device;
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -326,6 +326,8 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
->label('audits.resource', 'files/{response.$id}')
|
||||
->label('usage.metric', 'files.{scope}.requests.create')
|
||||
->label('usage.params', ['bucketId:{request.bucketId}'])
|
||||
->label('abuse-limit', 60)
|
||||
->label('abuse-time', 60)
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'createFile')
|
||||
|
@ -649,16 +651,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));
|
||||
|
||||
|
@ -673,35 +671,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));
|
||||
}
|
||||
|
||||
|
@ -1204,6 +1205,8 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
->label('audits.resource', 'files/{response.$id}')
|
||||
->label('usage.metric', 'files.{scope}.requests.update')
|
||||
->label('usage.params', ['bucketId:{request.bucketId}'])
|
||||
->label('abuse-limit', 60)
|
||||
->label('abuse-time', 60)
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'updateFile')
|
||||
|
@ -1303,6 +1306,8 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
->label('audits.resource', 'file/{request.fileId}')
|
||||
->label('usage.metric', 'files.{scope}.requests.delete')
|
||||
->label('usage.params', ['bucketId:{request.bucketId}'])
|
||||
->label('abuse-limit', 60)
|
||||
->label('abuse-time', 60)
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'storage')
|
||||
->label('sdk.method', 'deleteFile')
|
||||
|
@ -1359,9 +1364,12 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
->setResource('file/' . $fileId)
|
||||
;
|
||||
|
||||
// Don't need to check valid here because we already ensured validity
|
||||
if ($fileSecurity) {
|
||||
if ($fileSecurity && !$valid) {
|
||||
try {
|
||||
$deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
} catch (AuthorizationException) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
} else {
|
||||
$deleted = Authorization::skip(fn() => $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -74,6 +79,10 @@ App::post('/v1/teams')
|
|||
])));
|
||||
|
||||
if (!$isPrivilegedUser && !$isAppUser) { // Don't add user on server mode
|
||||
if (!\in_array('owner', $roles)) {
|
||||
$roles[] = 'owner';
|
||||
}
|
||||
|
||||
$membershipId = ID::unique();
|
||||
$membership = new Document([
|
||||
'$id' => $membershipId,
|
||||
|
@ -122,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([
|
||||
|
@ -460,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);
|
||||
|
||||
|
@ -476,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(
|
||||
|
@ -847,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);
|
||||
|
||||
|
@ -866,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);
|
||||
|
|
|
@ -8,6 +8,10 @@ use Appwrite\Event\Delete;
|
|||
use Appwrite\Event\Event;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Database\Validator\Queries;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Users;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Limit;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Offset;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
|
@ -27,7 +31,6 @@ use Utopia\Database\Validator\Authorization;
|
|||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\Boolean;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\Validator\Integer;
|
||||
|
@ -340,38 +343,36 @@ App::get('/v1/users')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER_LIST)
|
||||
->param('queries', [], new Users(), '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(', ', Users::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 users 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 user used as the starting point for the query, excluding the user itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor, can be either \'before\' or \'after\'.', true)
|
||||
->param('orderType', Database::ORDER_ASC, new WhiteList([Database::ORDER_ASC, Database::ORDER_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('users', $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 */
|
||||
$userId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "User '{$cursor}' for the 'cursor' value not found.");
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "User '{$userId}' 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([
|
||||
'users' => $dbForProject->find('users', \array_merge($filterQueries, $queries)),
|
||||
'users' => $dbForProject->find('users', $queries),
|
||||
'total' => $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT),
|
||||
]), Response::MODEL_USER_LIST);
|
||||
});
|
||||
|
@ -525,13 +526,12 @@ App::get('/v1/users/:userId/logs')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_LOG_LIST)
|
||||
->param('userId', '', new UID(), 'User 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 $userId, int $limit, int $offset, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
||||
->action(function (string $userId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
@ -539,6 +539,11 @@ App::get('/v1/users/:userId/logs')
|
|||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$queries = Query::parseQueries($queries);
|
||||
$grouped = Query::groupByType($queries);
|
||||
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
|
||||
$offset = $grouped['offset'] ?? 0;
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
|
||||
$logs = $audit->getLogsByUser($user->getId(), $limit, $offset);
|
||||
|
@ -621,8 +626,7 @@ App::patch('/v1/users/:userId/status')
|
|||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('status', (bool) $status));
|
||||
|
||||
$events
|
||||
->setParam('userId', $user->getId())
|
||||
;
|
||||
->setParam('userId', $user->getId());
|
||||
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
@ -657,8 +661,7 @@ App::patch('/v1/users/:userId/verification')
|
|||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', $emailVerification));
|
||||
|
||||
$events
|
||||
->setParam('userId', $user->getId())
|
||||
;
|
||||
->setParam('userId', $user->getId());
|
||||
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
@ -693,8 +696,7 @@ App::patch('/v1/users/:userId/verification/phone')
|
|||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('phoneVerification', $phoneVerification));
|
||||
|
||||
$events
|
||||
->setParam('userId', $user->getId())
|
||||
;
|
||||
->setParam('userId', $user->getId());
|
||||
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
@ -813,8 +815,7 @@ App::patch('/v1/users/:userId/email')
|
|||
$user
|
||||
->setAttribute('email', $email)
|
||||
->setAttribute('emailVerification', false)
|
||||
->setAttribute('search', \implode(' ', [$user->getId(), $email, $user->getAttribute('name', ''), $user->getAttribute('phone', '')]))
|
||||
;
|
||||
->setAttribute('search', \implode(' ', [$user->getId(), $email, $user->getAttribute('name', ''), $user->getAttribute('phone', '')]));
|
||||
|
||||
try {
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
|
@ -935,8 +936,7 @@ App::patch('/v1/users/:userId/prefs')
|
|||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs));
|
||||
|
||||
$events
|
||||
->setParam('userId', $user->getId())
|
||||
;
|
||||
->setParam('userId', $user->getId());
|
||||
|
||||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||
});
|
||||
|
@ -978,8 +978,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
|
|||
|
||||
$events
|
||||
->setParam('userId', $user->getId())
|
||||
->setParam('sessionId', $sessionId)
|
||||
;
|
||||
->setParam('sessionId', $sessionId);
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
@ -1011,7 +1010,8 @@ App::delete('/v1/users/:userId/sessions')
|
|||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
|
||||
foreach ($sessions as $key => $session) { /** @var Document $session */
|
||||
foreach ($sessions as $key => $session) {
|
||||
/** @var Document $session */
|
||||
$dbForProject->deleteDocument('sessions', $session->getId());
|
||||
//TODO: fix this
|
||||
}
|
||||
|
@ -1020,8 +1020,7 @@ App::delete('/v1/users/:userId/sessions')
|
|||
|
||||
$events
|
||||
->setParam('userId', $user->getId())
|
||||
->setPayload($response->output($user, Response::MODEL_USER))
|
||||
;
|
||||
->setPayload($response->output($user, Response::MODEL_USER));
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
@ -1059,13 +1058,11 @@ App::delete('/v1/users/:userId')
|
|||
|
||||
$deletes
|
||||
->setType(DELETE_TYPE_DOCUMENT)
|
||||
->setDocument($clone)
|
||||
;
|
||||
->setDocument($clone);
|
||||
|
||||
$events
|
||||
->setParam('userId', $user->getId())
|
||||
->setPayload($response->output($clone, Response::MODEL_USER))
|
||||
;
|
||||
->setPayload($response->output($clone, Response::MODEL_USER));
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
@ -1081,7 +1078,7 @@ App::get('/v1/users/usage')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USAGE_USERS)
|
||||
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
|
||||
->param('provider', '', new WhiteList(\array_merge(['email', 'anonymous'], \array_map(fn($value) => "oauth-" . $value, \array_keys(Config::getParam('providers', [])))), true), 'Provider Name.', true)
|
||||
->param('provider', '', new WhiteList(\array_merge(['email', 'anonymous'], \array_map(fn ($value) => "oauth-" . $value, \array_keys(Config::getParam('providers', [])))), true), 'Provider Name.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('register')
|
||||
|
|
|
@ -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}}',
|
||||
]);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
10
composer.lock
generated
10
composer.lock
generated
|
@ -2841,12 +2841,12 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "532d4d15ec8f11539972f2848c2e230c49b24f65"
|
||||
"reference": "6e630a62f522ac68a7056bebf81cd032c7a053ba"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/532d4d15ec8f11539972f2848c2e230c49b24f65",
|
||||
"reference": "532d4d15ec8f11539972f2848c2e230c49b24f65",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/6e630a62f522ac68a7056bebf81cd032c7a053ba",
|
||||
"reference": "6e630a62f522ac68a7056bebf81cd032c7a053ba",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2883,7 +2883,7 @@
|
|||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/feat-new-headers"
|
||||
},
|
||||
"time": "2022-08-20T07:42:55+00:00"
|
||||
"time": "2022-08-29T10:43:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/instantiator",
|
||||
|
@ -5383,5 +5383,5 @@
|
|||
"platform-overrides": {
|
||||
"php": "8.0"
|
||||
},
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.2.0"
|
||||
}
|
||||
|
|
1
docs/references/databases/get-logs.md
Normal file
1
docs/references/databases/get-logs.md
Normal file
|
@ -0,0 +1 @@
|
|||
Get the database activity logs list by its unique ID.
|
6
public/dist/scripts/app-all.js
vendored
6
public/dist/scripts/app-all.js
vendored
|
@ -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];}
|
||||
|
|
6
public/dist/scripts/app.js
vendored
6
public/dist/scripts/app.js
vendored
|
@ -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];}
|
||||
|
|
2
public/dist/styles/default-ltr.css
vendored
2
public/dist/styles/default-ltr.css
vendored
File diff suppressed because one or more lines are too long
2
public/dist/styles/default-rtl.css
vendored
2
public/dist/styles/default-rtl.css
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 882 B After Width: | Height: | Size: 1.2 KiB |
BIN
public/images/users/etsy.png
Normal file
BIN
public/images/users/etsy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.6 KiB |
|
@ -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', () => ({
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
return null;
|
||||
}
|
||||
|
||||
return new Intl.DateTimeFormat('en-US', {
|
||||
timeZone: 'UTC',
|
||||
return new Intl.DateTimeFormat(navigator.languages, {
|
||||
hourCycle: 'h24',
|
||||
...format
|
||||
}).format(new Date(datetime));
|
||||
|
|
|
@ -89,6 +89,10 @@
|
|||
max-width: 800px;
|
||||
}
|
||||
|
||||
&.width-xlarge {
|
||||
max-width: 950px;
|
||||
}
|
||||
|
||||
&.open {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
200
src/Appwrite/Auth/OAuth2/Etsy.php
Normal file
200
src/Appwrite/Auth/OAuth2/Etsy.php
Normal file
|
@ -0,0 +1,200 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Auth\OAuth2;
|
||||
|
||||
use Appwrite\Auth\OAuth2;
|
||||
|
||||
class Etsy extends OAuth2
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $endpoint = 'https://api.etsy.com/v3/public';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $version = '2022-07-14';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $user = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $tokens = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
"email_r",
|
||||
"profile_r",
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $pkce = '';
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function getPKCE(): string
|
||||
{
|
||||
if (empty($this->pkce)) {
|
||||
$this->pkce = \bin2hex(\random_bytes(rand(43, 128)));
|
||||
}
|
||||
|
||||
return $this->pkce;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'etsy';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
return 'https://www.etsy.com/oauth/connect/oauth/authorize?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'response_type' => 'code',
|
||||
'state' => \json_encode($this->state),
|
||||
'scope' => $this->scopes,
|
||||
'code_challenge' => $this->getPKCE(),
|
||||
'code_challenge_method' => 'S256',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if (empty($this->tokens)) {
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
$this->endpoint . '/oauth/token',
|
||||
$headers,
|
||||
\http_build_query([
|
||||
'grant_type' => 'authorization_code',
|
||||
'client_id' => $this->appID,
|
||||
'redirect_uri' => $this->callback,
|
||||
'code' => $code,
|
||||
'code_verifier' => $this->getPKCE(),
|
||||
])
|
||||
), true);
|
||||
}
|
||||
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $refreshToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$headers = ['Content-Type: application/x-www-form-urlencoded'];
|
||||
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
$this->endpoint . '/oauth/token',
|
||||
$headers,
|
||||
\http_build_query([
|
||||
'grant_type' => 'refresh_token',
|
||||
'client_id' => $this->appID,
|
||||
'refresh_token' => $refreshToken,
|
||||
])
|
||||
), true);
|
||||
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserID(string $accessToken): string
|
||||
{
|
||||
$components = explode('.', $accessToken);
|
||||
|
||||
return $components[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserEmail(string $accessToken): string
|
||||
{
|
||||
return $this->getUser($accessToken)['primary_email'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* OAuth is only allowed if account has been verified through Etsy, itself.
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
{
|
||||
$email = $this->getUserEmail($accessToken);
|
||||
|
||||
return !empty($email);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $accessToken
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(string $accessToken): string
|
||||
{
|
||||
return $this->getUser($accessToken)['login_name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getUser(string $accessToken): array
|
||||
{
|
||||
if (!empty($this->user)) {
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
$headers = ['Authorization: Bearer ' . $accessToken];
|
||||
|
||||
$this->user = \json_decode($this->request(
|
||||
'GET',
|
||||
'https://api.etsy.com/v3/application/users/' . $this->getUserID($accessToken),
|
||||
), true);
|
||||
|
||||
return $this->user;
|
||||
}
|
||||
}
|
|
@ -304,7 +304,7 @@ class OpenAPI3 extends Format
|
|||
case 'Utopia\Database\Validator\DatetimeValidator':
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
$node['schema']['format'] = 'datetime';
|
||||
$node['schema']['x-example'] = '2022-06-15T13:45:30.496';
|
||||
$node['schema']['x-example'] = Model::TYPE_DATETIME_EXAMPLE;
|
||||
break;
|
||||
case 'Appwrite\Network\Validator\Email':
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
|
|
|
@ -300,7 +300,7 @@ class Swagger2 extends Format
|
|||
case 'Utopia\Database\Validator\DatetimeValidator':
|
||||
$node['type'] = $validator->getType();
|
||||
$node['format'] = 'datetime';
|
||||
$node['x-example'] = '2022-06-15T13:45:30.496';
|
||||
$node['x-example'] = Model::TYPE_DATETIME_EXAMPLE;
|
||||
break;
|
||||
case 'Appwrite\Network\Validator\Email':
|
||||
$node['type'] = $validator->getType();
|
||||
|
|
153
src/Appwrite/Utopia/Database/Validator/IndexedQueries.php
Normal file
153
src/Appwrite/Utopia/Database/Validator/IndexedQueries.php
Normal file
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Query\Base;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
|
||||
class IndexedQueries extends Queries
|
||||
{
|
||||
/**
|
||||
* @var Document[]
|
||||
*/
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* @var Document[]
|
||||
*/
|
||||
protected $indexes = [];
|
||||
|
||||
/**
|
||||
* Expression constructor
|
||||
*
|
||||
* This Queries Validator filters indexes for only available indexes
|
||||
*
|
||||
* @param Document[] $attributes
|
||||
* @param Document[] $indexes
|
||||
* @param Base ...$validators
|
||||
* @param bool $strict
|
||||
*/
|
||||
public function __construct($attributes = [], $indexes = [], Base ...$validators)
|
||||
{
|
||||
$this->attributes = $attributes;
|
||||
|
||||
$this->indexes[] = new Document([
|
||||
'type' => Database::INDEX_UNIQUE,
|
||||
'attributes' => ['$id']
|
||||
]);
|
||||
|
||||
$this->indexes[] = new Document([
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['$createdAt']
|
||||
]);
|
||||
|
||||
$this->indexes[] = new Document([
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['$updatedAt']
|
||||
]);
|
||||
|
||||
foreach ($indexes ?? [] as $index) {
|
||||
$this->indexes[] = $index;
|
||||
}
|
||||
|
||||
parent::__construct(...$validators);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if indexed array $indexes matches $queries
|
||||
*
|
||||
* @param array $indexes
|
||||
* @param array $queries
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function arrayMatch(array $indexes, array $queries): bool
|
||||
{
|
||||
// Check the count of indexes first for performance
|
||||
if (count($queries) !== count($indexes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sort them for comparison, the order is not important here anymore.
|
||||
sort($indexes, SORT_STRING);
|
||||
sort($queries, SORT_STRING);
|
||||
|
||||
// Only matching arrays will have equal diffs in both directions
|
||||
if (array_diff_assoc($indexes, $queries) !== array_diff_assoc($queries, $indexes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is valid.
|
||||
*
|
||||
* Returns false if:
|
||||
* 1. any query in $value is invalid based on $validator
|
||||
* 2. there is no index with an exact match of the filters
|
||||
* 3. there is no index with an exact match of the order attributes
|
||||
*
|
||||
* Otherwise, returns true.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value): bool
|
||||
{
|
||||
if (!parent::isValid($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$queries = [];
|
||||
foreach ($value as $query) {
|
||||
if (!$query instanceof Query) {
|
||||
$query = Query::parse($query);
|
||||
}
|
||||
|
||||
$queries[] = $query;
|
||||
}
|
||||
|
||||
$grouped = Query::groupByType($queries);
|
||||
/** @var Query[] */ $filters = $grouped['filters'];
|
||||
/** @var string[] */ $orderAttributes = $grouped['orderAttributes'];
|
||||
|
||||
// Check filter queries for exact index match
|
||||
if (count($filters) > 0) {
|
||||
$filtersByAttribute = [];
|
||||
foreach ($filters as $filter) {
|
||||
$filtersByAttribute[$filter->getAttribute()] = $filter->getMethod();
|
||||
}
|
||||
|
||||
$found = null;
|
||||
|
||||
foreach ($this->indexes as $index) {
|
||||
if ($this->arrayMatch($index->getAttribute('attributes'), array_keys($filtersByAttribute))) {
|
||||
$found = $index;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
$this->message = 'Index not found: ' . implode(",", array_keys($filtersByAttribute));
|
||||
return false;
|
||||
}
|
||||
|
||||
// search method requires fulltext index
|
||||
if (in_array(Query::TYPE_SEARCH, array_values($filtersByAttribute)) && $found['type'] !== Database::INDEX_FULLTEXT) {
|
||||
$this->message = 'Search method requires fulltext index: ' . implode(",", array_keys($filtersByAttribute));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check order attributes for exact index match
|
||||
$validator = new OrderAttributes($this->attributes, $this->indexes, true);
|
||||
if (count($orderAttributes) > 0 && !$validator->isValid($orderAttributes)) {
|
||||
$this->message = $validator->getDescription();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -2,28 +2,140 @@
|
|||
|
||||
namespace Appwrite\Utopia\Database\Validator;
|
||||
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Queries as ValidatorQueries;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Base;
|
||||
use Utopia\Validator;
|
||||
use Utopia\Database\Query;
|
||||
|
||||
class Queries extends ValidatorQueries
|
||||
class Queries extends Validator
|
||||
{
|
||||
/**
|
||||
* Expression constructor
|
||||
*
|
||||
* This Queries Validator that filters indexes for only available indexes
|
||||
*
|
||||
* @param QueryValidator $validator
|
||||
* @param Document[] $attributes
|
||||
* @param Document[] $indexes
|
||||
* @param bool $strict
|
||||
* @var string
|
||||
*/
|
||||
public function __construct($validator, $attributes = [], $indexes = [], $strict = true)
|
||||
{
|
||||
// Remove failed/stuck/processing indexes
|
||||
$availableIndexes = \array_filter($indexes, function ($index) {
|
||||
return $index->getAttribute('status') === 'available';
|
||||
});
|
||||
protected $message = 'Invalid queries';
|
||||
|
||||
parent::__construct($validator, $attributes, $availableIndexes, $strict);
|
||||
/**
|
||||
* @var Base[]
|
||||
*/
|
||||
protected $validators;
|
||||
|
||||
/**
|
||||
* Queries constructor
|
||||
*
|
||||
* @param Base ...$validators a list of validators
|
||||
*/
|
||||
public function __construct(Base ...$validators)
|
||||
{
|
||||
$this->validators = $validators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Description.
|
||||
*
|
||||
* Returns validator description
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is valid.
|
||||
*
|
||||
* Returns false if:
|
||||
* 1. any query in $value is invalid based on $validator
|
||||
*
|
||||
* Otherwise, returns true.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($value): bool
|
||||
{
|
||||
foreach ($value as $query) {
|
||||
if (!$query instanceof Query) {
|
||||
try {
|
||||
$query = Query::parse($query);
|
||||
} catch (\Throwable $th) {
|
||||
$this->message = 'Invalid query: ${query}';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$method = $query->getMethod();
|
||||
$methodType = '';
|
||||
switch ($method) {
|
||||
case Query::TYPE_LIMIT:
|
||||
$methodType = Base::METHOD_TYPE_LIMIT;
|
||||
break;
|
||||
case Query::TYPE_OFFSET:
|
||||
$methodType = Base::METHOD_TYPE_OFFSET;
|
||||
break;
|
||||
case Query::TYPE_CURSORAFTER:
|
||||
case Query::TYPE_CURSORBEFORE:
|
||||
$methodType = Base::METHOD_TYPE_CURSOR;
|
||||
break;
|
||||
case Query::TYPE_ORDERASC:
|
||||
case Query::TYPE_ORDERDESC:
|
||||
$methodType = Base::METHOD_TYPE_ORDER;
|
||||
break;
|
||||
case Query::TYPE_EQUAL:
|
||||
case Query::TYPE_NOTEQUAL:
|
||||
case Query::TYPE_LESSER:
|
||||
case Query::TYPE_LESSEREQUAL:
|
||||
case Query::TYPE_GREATER:
|
||||
case Query::TYPE_GREATEREQUAL:
|
||||
case Query::TYPE_SEARCH:
|
||||
$methodType = Base::METHOD_TYPE_FILTER;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$methodIsValid = false;
|
||||
foreach ($this->validators as $validator) {
|
||||
if ($validator->getMethodType() !== $methodType) {
|
||||
continue;
|
||||
}
|
||||
if (!$validator->isValid($query)) {
|
||||
$this->message = 'Query not valid: ' . $validator->getDescription();
|
||||
return false;
|
||||
}
|
||||
|
||||
$methodIsValid = true;
|
||||
}
|
||||
|
||||
if (!$methodIsValid) {
|
||||
$this->message = 'Query method not valid: ' . $method;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is array
|
||||
*
|
||||
* Function will return true if object is array.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isArray(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* Returns validator type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return self::TYPE_OBJECT;
|
||||
}
|
||||
}
|
||||
|
|
72
src/Appwrite/Utopia/Database/Validator/Queries/Base.php
Normal file
72
src/Appwrite/Utopia/Database/Validator/Queries/Base.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator\Queries;
|
||||
|
||||
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;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Filter;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Order;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class Base extends Queries
|
||||
{
|
||||
/**
|
||||
* Expression constructor
|
||||
*
|
||||
* @param string $collection
|
||||
* @param string[] $allowedAttributes
|
||||
*/
|
||||
public function __construct(string $collection, array $allowedAttributes)
|
||||
{
|
||||
$collection = Config::getParam('collections', [])[$collection];
|
||||
// array for constant lookup time
|
||||
$allowedAttributesLookup = [];
|
||||
foreach ($allowedAttributes as $attribute) {
|
||||
$allowedAttributesLookup[$attribute] = true;
|
||||
}
|
||||
|
||||
$attributes = [];
|
||||
foreach ($collection['attributes'] as $attribute) {
|
||||
$key = $attribute['$id'];
|
||||
if (!isset($allowedAttributesLookup[$key])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attributes[] = new Document([
|
||||
'key' => $key,
|
||||
'type' => $attribute['type'],
|
||||
'array' => $attribute['array'],
|
||||
]);
|
||||
}
|
||||
|
||||
$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(...$validators);
|
||||
}
|
||||
}
|
24
src/Appwrite/Utopia/Database/Validator/Queries/Buckets.php
Normal file
24
src/Appwrite/Utopia/Database/Validator/Queries/Buckets.php
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
19
src/Appwrite/Utopia/Database/Validator/Queries/Databases.php
Normal file
19
src/Appwrite/Utopia/Database/Validator/Queries/Databases.php
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
50
src/Appwrite/Utopia/Database/Validator/Queries/Documents.php
Normal file
50
src/Appwrite/Utopia/Database/Validator/Queries/Documents.php
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
24
src/Appwrite/Utopia/Database/Validator/Queries/Files.php
Normal file
24
src/Appwrite/Utopia/Database/Validator/Queries/Files.php
Normal 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);
|
||||
}
|
||||
}
|
26
src/Appwrite/Utopia/Database/Validator/Queries/Functions.php
Normal file
26
src/Appwrite/Utopia/Database/Validator/Queries/Functions.php
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
20
src/Appwrite/Utopia/Database/Validator/Queries/Teams.php
Normal file
20
src/Appwrite/Utopia/Database/Validator/Queries/Teams.php
Normal 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);
|
||||
}
|
||||
}
|
26
src/Appwrite/Utopia/Database/Validator/Queries/Users.php
Normal file
26
src/Appwrite/Utopia/Database/Validator/Queries/Users.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator\Queries;
|
||||
|
||||
class Users extends Base
|
||||
{
|
||||
public const ALLOWED_ATTRIBUTES = [
|
||||
'name',
|
||||
'email',
|
||||
'phone',
|
||||
'status',
|
||||
'passwordUpdate',
|
||||
'registration',
|
||||
'emailVerification',
|
||||
'phoneVerification'
|
||||
];
|
||||
|
||||
/**
|
||||
* Expression constructor
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('users', self::ALLOWED_ATTRIBUTES);
|
||||
}
|
||||
}
|
61
src/Appwrite/Utopia/Database/Validator/Query/Base.php
Normal file
61
src/Appwrite/Utopia/Database/Validator/Query/Base.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator\Query;
|
||||
|
||||
use Utopia\Validator;
|
||||
use Utopia\Database\Query;
|
||||
|
||||
abstract class Base extends Validator
|
||||
{
|
||||
public const METHOD_TYPE_LIMIT = 'limit';
|
||||
public const METHOD_TYPE_OFFSET = 'offset';
|
||||
public const METHOD_TYPE_CURSOR = 'cursor';
|
||||
public const METHOD_TYPE_ORDER = 'order';
|
||||
public const METHOD_TYPE_FILTER = 'filter';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = 'Invalid query';
|
||||
|
||||
/**
|
||||
* Get Description.
|
||||
*
|
||||
* Returns validator description
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is array
|
||||
*
|
||||
* Function will return true if object is array.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isArray(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* Returns validator type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return self::TYPE_OBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns what type of query this Validator is for
|
||||
*/
|
||||
abstract public function getMethodType(): string;
|
||||
}
|
44
src/Appwrite/Utopia/Database/Validator/Query/Cursor.php
Normal file
44
src/Appwrite/Utopia/Database/Validator/Query/Cursor.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator\Query;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Query\Base;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\UID;
|
||||
|
||||
class Cursor extends Base
|
||||
{
|
||||
/**
|
||||
* Is valid.
|
||||
*
|
||||
* Returns true if method is cursorBefore or cursorAfter and value is not null
|
||||
*
|
||||
* Otherwise, returns false
|
||||
*
|
||||
* @param Query $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($query): bool
|
||||
{
|
||||
// Validate method
|
||||
$method = $query->getMethod();
|
||||
|
||||
if ($method === Query::TYPE_CURSORAFTER || $method === Query::TYPE_CURSORBEFORE) {
|
||||
$cursor = $query->getValue();
|
||||
$validator = new UID();
|
||||
if ($validator->isValid($cursor)) {
|
||||
return true;
|
||||
}
|
||||
$this->message = 'Invalid cursor: ' . $validator->getDescription();
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getMethodType(): string
|
||||
{
|
||||
return self::METHOD_TYPE_CURSOR;
|
||||
}
|
||||
}
|
114
src/Appwrite/Utopia/Database/Validator/Query/Filter.php
Normal file
114
src/Appwrite/Utopia/Database/Validator/Query/Filter.php
Normal file
|
@ -0,0 +1,114 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator\Query;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Query\Base;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Query;
|
||||
|
||||
class Filter extends Base
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = 'Invalid query';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $schema = [];
|
||||
|
||||
/**
|
||||
* Query constructor
|
||||
*
|
||||
* @param int $maxValuesCount
|
||||
*/
|
||||
public function __construct(array $attributes = [], int $maxValuesCount = 100)
|
||||
{
|
||||
foreach ($attributes as $attribute) {
|
||||
$this->schema[$attribute->getAttribute('key')] = $attribute->getArrayCopy();
|
||||
}
|
||||
|
||||
$this->maxValuesCount = $maxValuesCount;
|
||||
}
|
||||
|
||||
protected function isValidAttribute($attribute): bool
|
||||
{
|
||||
// Search for attribute in schema
|
||||
if (!isset($this->schema[$attribute])) {
|
||||
$this->message = 'Attribute not found in schema: ' . $attribute;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function isValidAttributeAndValues(string $attribute, array $values): bool
|
||||
{
|
||||
if (!$this->isValidAttribute($attribute)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$attributeSchema = $this->schema[$attribute];
|
||||
|
||||
if (count($values) > $this->maxValuesCount) {
|
||||
$this->message = 'Query on attribute has greater than ' . $this->maxValuesCount . ' values: ' . $attribute;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract the type of desired attribute from collection $schema
|
||||
$attributeType = $attributeSchema['type'];
|
||||
|
||||
foreach ($values as $value) {
|
||||
$condition = match ($attributeType) {
|
||||
Database::VAR_DATETIME => gettype($value) === Database::VAR_STRING,
|
||||
default => gettype($value) === $attributeType
|
||||
};
|
||||
|
||||
if (!$condition) {
|
||||
$this->message = 'Query type does not match expected: ' . $attributeType;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is valid.
|
||||
*
|
||||
* Returns true if method is a filter method, attribute exists, and value matches attribute type
|
||||
*
|
||||
* Otherwise, returns false
|
||||
*
|
||||
* @param Query $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($query): bool
|
||||
{
|
||||
// Validate method
|
||||
$method = $query->getMethod();
|
||||
$attribute = $query->getAttribute();
|
||||
|
||||
switch ($method) {
|
||||
case Query::TYPE_EQUAL:
|
||||
case Query::TYPE_NOTEQUAL:
|
||||
case Query::TYPE_LESSER:
|
||||
case Query::TYPE_LESSEREQUAL:
|
||||
case Query::TYPE_GREATER:
|
||||
case Query::TYPE_GREATEREQUAL:
|
||||
case Query::TYPE_SEARCH:
|
||||
$values = $query->getValues();
|
||||
return $this->isValidAttributeAndValues($attribute, $values);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getMethodType(): string
|
||||
{
|
||||
return self::METHOD_TYPE_FILTER;
|
||||
}
|
||||
}
|
61
src/Appwrite/Utopia/Database/Validator/Query/Limit.php
Normal file
61
src/Appwrite/Utopia/Database/Validator/Query/Limit.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator\Query;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Query\Base;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Validator\Range;
|
||||
|
||||
class Limit extends Base
|
||||
{
|
||||
protected int $maxLimit;
|
||||
|
||||
/**
|
||||
* Query constructor
|
||||
*
|
||||
* @param int $maxLimit
|
||||
*/
|
||||
public function __construct(int $maxLimit = 100)
|
||||
{
|
||||
$this->maxLimit = $maxLimit;
|
||||
}
|
||||
|
||||
protected function isValidLimit($limit): bool
|
||||
{
|
||||
$validator = new Range(0, $this->maxLimit);
|
||||
if ($validator->isValid($limit)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->message = 'Invalid limit: ' . $validator->getDescription();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is valid.
|
||||
*
|
||||
* Returns true if method is limit values are within range.
|
||||
*
|
||||
* @param Query $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($query): bool
|
||||
{
|
||||
// Validate method
|
||||
$method = $query->getMethod();
|
||||
|
||||
if ($method !== Query::TYPE_LIMIT) {
|
||||
$this->message = 'Query method invalid: ' . $method;
|
||||
return false;
|
||||
}
|
||||
|
||||
$limit = $query->getValue();
|
||||
return $this->isValidLimit($limit);
|
||||
}
|
||||
|
||||
public function getMethodType(): string
|
||||
{
|
||||
return self::METHOD_TYPE_LIMIT;
|
||||
}
|
||||
}
|
61
src/Appwrite/Utopia/Database/Validator/Query/Offset.php
Normal file
61
src/Appwrite/Utopia/Database/Validator/Query/Offset.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator\Query;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Query\Base;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Validator\Range;
|
||||
|
||||
class Offset extends Base
|
||||
{
|
||||
protected int $maxOffset;
|
||||
|
||||
/**
|
||||
* Query constructor
|
||||
*
|
||||
* @param int $maxOffset
|
||||
*/
|
||||
public function __construct(int $maxOffset = 5000)
|
||||
{
|
||||
$this->maxOffset = $maxOffset;
|
||||
}
|
||||
|
||||
protected function isValidOffset($offset): bool
|
||||
{
|
||||
$validator = new Range(0, $this->maxOffset);
|
||||
if ($validator->isValid($offset)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->message = 'Invalid offset: ' . $validator->getDescription();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is valid.
|
||||
*
|
||||
* Returns true if method is offset and values are within range.
|
||||
*
|
||||
* @param Query $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($query): bool
|
||||
{
|
||||
// Validate method
|
||||
$method = $query->getMethod();
|
||||
|
||||
if ($method !== Query::TYPE_OFFSET) {
|
||||
$this->message = 'Query method invalid: ' . $method;
|
||||
return false;
|
||||
}
|
||||
|
||||
$offset = $query->getValue();
|
||||
return $this->isValidOffset($offset);
|
||||
}
|
||||
|
||||
public function getMethodType(): string
|
||||
{
|
||||
return self::METHOD_TYPE_OFFSET;
|
||||
}
|
||||
}
|
68
src/Appwrite/Utopia/Database/Validator/Query/Order.php
Normal file
68
src/Appwrite/Utopia/Database/Validator/Query/Order.php
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator\Query;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Query\Base;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Validator;
|
||||
|
||||
class Order extends Base
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $schema = [];
|
||||
|
||||
/**
|
||||
* Query constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
foreach ($attributes as $attribute) {
|
||||
$this->schema[$attribute->getAttribute('key')] = $attribute->getArrayCopy();
|
||||
}
|
||||
}
|
||||
|
||||
protected function isValidAttribute($attribute): bool
|
||||
{
|
||||
// Search for attribute in schema
|
||||
if (!isset($this->schema[$attribute])) {
|
||||
$this->message = 'Attribute not found in schema: ' . $attribute;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is valid.
|
||||
*
|
||||
* Returns true if method is ORDER_ASC or ORDER_DESC and attributes are valid
|
||||
*
|
||||
* Otherwise, returns false
|
||||
*
|
||||
* @param Query $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($query): bool
|
||||
{
|
||||
$method = $query->getMethod();
|
||||
$attribute = $query->getAttribute();
|
||||
|
||||
if ($method === Query::TYPE_ORDERASC || $method === Query::TYPE_ORDERDESC) {
|
||||
if ($attribute === '') {
|
||||
return true;
|
||||
}
|
||||
return $this->isValidAttribute($attribute);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getMethodType(): string
|
||||
{
|
||||
return self::METHOD_TYPE_ORDER;
|
||||
}
|
||||
}
|
|
@ -2,12 +2,15 @@
|
|||
|
||||
namespace Tests\E2E\Scopes;
|
||||
|
||||
use Appwrite\Tests\Retryable;
|
||||
use Tests\E2E\Client;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
abstract class Scope extends TestCase
|
||||
{
|
||||
use Retryable;
|
||||
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Tests\E2E\Services\Account;
|
||||
|
||||
use Appwrite\Tests\Retry;
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\DateTime;
|
||||
|
@ -390,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);
|
||||
|
@ -407,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);
|
||||
|
@ -424,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);
|
||||
|
@ -519,6 +519,7 @@ trait AccountBase
|
|||
/**
|
||||
* @depends testUpdateAccountName
|
||||
*/
|
||||
#[Retry(count: 1)]
|
||||
public function testUpdateAccountPassword($data): array
|
||||
{
|
||||
$email = $data['email'] ?? '';
|
||||
|
|
|
@ -614,7 +614,7 @@ class AccountCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'search' => $newName
|
||||
'search' => $newName,
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -628,7 +628,7 @@ class AccountCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'search' => $id
|
||||
'search' => $id,
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -654,7 +654,8 @@ class AccountCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'search' => '"' . $email . '"'
|
||||
'search' => '"' . $email . '"',
|
||||
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -668,7 +669,7 @@ class AccountCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'search' => $id
|
||||
'search' => $id,
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -31,7 +31,7 @@ class DatabasesPermissionsGuestTest extends Scope
|
|||
$this->assertEquals('InvalidDocumentDatabase', $database['body']['name']);
|
||||
|
||||
$databaseId = $database['body']['$id'];
|
||||
$movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [
|
||||
$publicMovies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Movies',
|
||||
'permissions' => [
|
||||
|
@ -40,12 +40,23 @@ class DatabasesPermissionsGuestTest extends Scope
|
|||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
]);
|
||||
$privateMovies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', $this->getServerHeader(), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'Movies',
|
||||
'permissions' => [],
|
||||
'documentSecurity' => true,
|
||||
]);
|
||||
|
||||
$collection = ['id' => $movies['body']['$id']];
|
||||
$publicCollection = ['id' => $publicMovies['body']['$id']];
|
||||
$privateCollection = ['id' => $privateMovies['body']['$id']];
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection['id'] . '/attributes/string', $this->getServerHeader(), [
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $publicCollection['id'] . '/attributes/string', $this->getServerHeader(), [
|
||||
'key' => 'title',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
]);
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $privateCollection['id'] . '/attributes/string', $this->getServerHeader(), [
|
||||
'key' => 'title',
|
||||
'size' => 256,
|
||||
'required' => true,
|
||||
|
@ -53,13 +64,14 @@ class DatabasesPermissionsGuestTest extends Scope
|
|||
|
||||
sleep(2);
|
||||
|
||||
return ['collectionId' => $collection['id'], 'databaseId' => $databaseId];
|
||||
return [
|
||||
'databaseId' => $databaseId,
|
||||
'publicCollectionId' => $publicCollection['id'],
|
||||
'privateCollectionId' => $privateCollection['id'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* [string[] $permissions]
|
||||
*/
|
||||
public function readDocumentsProvider()
|
||||
public function permissionsProvider(): array
|
||||
{
|
||||
return [
|
||||
[[Permission::read(Role::any())]],
|
||||
|
@ -72,14 +84,23 @@ class DatabasesPermissionsGuestTest extends Scope
|
|||
}
|
||||
|
||||
/**
|
||||
* @dataProvider readDocumentsProvider
|
||||
* @dataProvider permissionsProvider
|
||||
*/
|
||||
public function testReadDocuments($permissions)
|
||||
{
|
||||
$data = $this->createCollection();
|
||||
$collectionId = $data['collectionId'];
|
||||
$publicCollectionId = $data['publicCollectionId'];
|
||||
$privateCollectionId = $data['privateCollectionId'];
|
||||
$databaseId = $data['databaseId'];
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', $this->getServerHeader(), [
|
||||
|
||||
$publicResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents', $this->getServerHeader(), [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Lorem',
|
||||
],
|
||||
'permissions' => $permissions,
|
||||
]);
|
||||
$privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $privateCollectionId . '/documents', $this->getServerHeader(), [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Lorem',
|
||||
|
@ -87,18 +108,118 @@ class DatabasesPermissionsGuestTest extends Scope
|
|||
'permissions' => $permissions,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
$this->assertEquals(201, $publicResponse['headers']['status-code']);
|
||||
$this->assertEquals(201, $privateResponse['headers']['status-code']);
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
Authorization::cleanRoles();
|
||||
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [
|
||||
$publicDocuments = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]);
|
||||
$privateDocuments = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $privateCollectionId . '/documents', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(1, $documents['body']['total']);
|
||||
$this->assertEquals($permissions, $documents['body']['documents'][0]['$permissions']);
|
||||
$this->assertEquals(1, $publicDocuments['body']['total']);
|
||||
$this->assertEquals($permissions, $publicDocuments['body']['documents'][0]['$permissions']);
|
||||
|
||||
if (\in_array(Permission::read(Role::any()), $permissions)) {
|
||||
$this->assertEquals(1, $privateDocuments['body']['total']);
|
||||
$this->assertEquals($permissions, $privateDocuments['body']['documents'][0]['$permissions']);
|
||||
} else {
|
||||
$this->assertEquals(0, $privateDocuments['body']['total']);
|
||||
}
|
||||
|
||||
foreach ($roles as $role) {
|
||||
Authorization::setRole($role);
|
||||
}
|
||||
}
|
||||
|
||||
public function testWriteDocument()
|
||||
{
|
||||
$data = $this->createCollection();
|
||||
$publicCollectionId = $data['publicCollectionId'];
|
||||
$privateCollectionId = $data['privateCollectionId'];
|
||||
$databaseId = $data['databaseId'];
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
Authorization::cleanRoles();
|
||||
|
||||
$publicResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Lorem',
|
||||
]
|
||||
]);
|
||||
|
||||
$publicDocumentId = $publicResponse['body']['$id'];
|
||||
$this->assertEquals(201, $publicResponse['headers']['status-code']);
|
||||
|
||||
$privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $privateCollectionId . '/documents', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Lorem',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(401, $privateResponse['headers']['status-code']);
|
||||
|
||||
// Create a document in private collection with API key so we can test that update and delete are also not allowed
|
||||
$privateResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $privateCollectionId . '/documents', $this->getServerHeader(), [
|
||||
'documentId' => ID::unique(),
|
||||
'data' => [
|
||||
'title' => 'Lorem',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $privateResponse['headers']['status-code']);
|
||||
$privateDocumentId = $privateResponse['body']['$id'];
|
||||
|
||||
$publicDocument = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents/' . $publicDocumentId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'data' => [
|
||||
'title' => 'Thor: Ragnarok',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $publicDocument['headers']['status-code']);
|
||||
$this->assertEquals('Thor: Ragnarok', $publicDocument['body']['title']);
|
||||
|
||||
$privateDocument = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $privateCollectionId . '/documents/' . $privateDocumentId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'data' => [
|
||||
'title' => 'Thor: Ragnarok',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(401, $privateDocument['headers']['status-code']);
|
||||
|
||||
$publicDocument = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents/' . $publicDocumentId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(204, $publicDocument['headers']['status-code']);
|
||||
|
||||
$privateDocument = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $privateCollectionId . '/documents/' . $privateDocumentId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(401, $privateDocument['headers']['status-code']);
|
||||
|
||||
foreach ($roles as $role) {
|
||||
Authorization::setRole($role);
|
||||
|
|
|
@ -231,7 +231,75 @@ class FunctionsCustomClientTest extends Scope
|
|||
];
|
||||
}
|
||||
|
||||
public function testCreateExecutionUnauthorized(): array
|
||||
public function testCreateCustomExecutionGuest()
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$apikey = $this->getProject()['apiKey'];
|
||||
|
||||
$function = $this->client->call(Client::METHOD_POST, '/functions', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
'x-appwrite-key' => $apikey,
|
||||
], [
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test',
|
||||
'execute' => [Role::any()->toString()],
|
||||
'runtime' => 'php-8.0',
|
||||
'vars' => [
|
||||
'funcKey1' => 'funcValue1',
|
||||
'funcKey2' => 'funcValue2',
|
||||
'funcKey3' => 'funcValue3',
|
||||
],
|
||||
'timeout' => 10,
|
||||
]);
|
||||
|
||||
$functionId = $function['body']['$id'] ?? '';
|
||||
|
||||
$this->assertEquals(201, $function['headers']['status-code']);
|
||||
|
||||
$folder = 'php-fn';
|
||||
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
|
||||
$this->packageCode($folder);
|
||||
|
||||
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', [
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $projectId,
|
||||
'x-appwrite-key' => $apikey,
|
||||
], [
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), //different tarball names intentional
|
||||
]);
|
||||
|
||||
$deploymentId = $deployment['body']['$id'] ?? '';
|
||||
|
||||
// Wait for deployment to be built.
|
||||
sleep(10);
|
||||
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
|
||||
// Why do we have to do this?
|
||||
$function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
'x-appwrite-key' => $apikey,
|
||||
], []);
|
||||
|
||||
$this->assertEquals(200, $function['headers']['status-code']);
|
||||
|
||||
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], [
|
||||
'data' => 'foobar',
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $execution['headers']['status-code']);
|
||||
}
|
||||
|
||||
public function testCreateExecutionNoDeployment(): array
|
||||
{
|
||||
$function = $this->client->call(Client::METHOD_POST, '/functions', [
|
||||
'content-type' => 'application/json',
|
||||
|
@ -252,7 +320,7 @@ class FunctionsCustomClientTest extends Scope
|
|||
'async' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(401, $execution['headers']['status-code']);
|
||||
$this->assertEquals(404, $execution['headers']['status-code']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
@ -293,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']);
|
||||
|
@ -304,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
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Tests\E2E\Services\Functions;
|
||||
|
||||
use Appwrite\Tests\Retry;
|
||||
use CURLFile;
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
|
@ -96,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'],
|
||||
|
@ -158,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);
|
||||
|
@ -169,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);
|
||||
|
@ -184,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);
|
||||
|
@ -412,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'],
|
||||
|
@ -548,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
|
||||
*/
|
||||
|
@ -1209,6 +1319,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
}
|
||||
|
||||
#[Retry(count: 1)]
|
||||
public function testCreateCustomRubyExecution()
|
||||
{
|
||||
$name = 'ruby-3.1';
|
||||
|
|
|
@ -259,6 +259,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
|
||||
*/
|
||||
|
|
|
@ -49,13 +49,10 @@ class StorageCustomClientTest extends Scope
|
|||
$this->assertEquals(201, $bucket['headers']['status-code']);
|
||||
$this->assertNotEmpty($bucketId);
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
Authorization::cleanRoles();
|
||||
|
||||
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', array_merge([
|
||||
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', [
|
||||
'content-type' => 'multipart/form-data',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
], [
|
||||
'fileId' => ID::unique(),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'),
|
||||
]);
|
||||
|
@ -68,38 +65,47 @@ class StorageCustomClientTest extends Scope
|
|||
$this->assertEquals('image/png', $file['body']['mimeType']);
|
||||
$this->assertEquals(47218, $file['body']['sizeOriginal']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $file['headers']['status-code']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/preview', array_merge([
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/preview', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $file['headers']['status-code']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/download', array_merge([
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/download', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $file['headers']['status-code']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/view', array_merge([
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/view', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $file['headers']['status-code']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
$file = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $bucketId . '/files/' . $fileId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
], [
|
||||
'name' => 'permissions.png',
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $file['headers']['status-code']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $bucketId . '/files/' . $fileId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(204, $file['headers']['status-code']);
|
||||
$this->assertEmpty($file['body']);
|
||||
|
@ -173,6 +179,15 @@ class StorageCustomClientTest extends Scope
|
|||
|
||||
$this->assertEquals(200, $file['headers']['status-code']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'permissions.png',
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $file['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
@ -289,6 +304,15 @@ class StorageCustomClientTest extends Scope
|
|||
|
||||
$this->assertEquals(200, $file['headers']['status-code']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'permissions.png',
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $file['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
@ -297,7 +321,7 @@ class StorageCustomClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]);
|
||||
|
||||
$this->assertEquals($file['headers']['status-code'], 401);
|
||||
$this->assertEquals(401, $file['headers']['status-code']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', [
|
||||
'content-type' => 'multipart/form-data',
|
||||
|
@ -307,6 +331,15 @@ class StorageCustomClientTest extends Scope
|
|||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'),
|
||||
]);
|
||||
|
||||
$this->client->call(CLient::METHOD_PUT, '/storage/buckets/' . $bucketId . '/files/' . $fileId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], [
|
||||
'name' => 'permissions.png',
|
||||
]);
|
||||
|
||||
$this->assertEquals(401, $file['headers']['status-code']);
|
||||
|
||||
$this->assertEquals($file['headers']['status-code'], 401);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $bucketId . '/files/' . $fileId, [
|
||||
|
@ -583,35 +616,31 @@ class StorageCustomClientTest extends Scope
|
|||
$this->assertEquals('image/png', $file1['body']['mimeType']);
|
||||
$this->assertEquals(47218, $file1['body']['sizeOriginal']);
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
Authorization::cleanRoles();
|
||||
Authorization::setRole(Role::any()->toString());
|
||||
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $file['headers']['status-code']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/preview', array_merge([
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/preview', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $file['headers']['status-code']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/download', array_merge([
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/download', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $file['headers']['status-code']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/view', array_merge([
|
||||
$file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/view', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $file['headers']['status-code']);
|
||||
|
||||
|
@ -628,16 +657,12 @@ class StorageCustomClientTest extends Scope
|
|||
|
||||
$this->assertEquals(401, $file['headers']['status-code']);
|
||||
|
||||
$file = $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
$file = $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $bucketId . '/files/' . $fileId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
]);
|
||||
|
||||
$this->assertEquals(401, $file['headers']['status-code']);
|
||||
|
||||
foreach ($roles as $role) {
|
||||
Authorization::setRole($role);
|
||||
}
|
||||
}
|
||||
|
||||
public function testFileUsersPermissions(): void
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -19,7 +19,8 @@ trait TeamsBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Arsenal'
|
||||
'name' => 'Arsenal',
|
||||
'roles' => ['player'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response1['headers']['status-code']);
|
||||
|
@ -128,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',
|
||||
|
@ -191,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']);
|
||||
|
@ -203,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']);
|
||||
|
@ -217,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']);
|
||||
|
@ -235,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']);
|
||||
|
|
|
@ -30,10 +30,51 @@ trait TeamsBaseClient
|
|||
$this->assertEquals($this->getUser()['name'], $response['body']['memberships'][0]['userName']);
|
||||
$this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['userEmail']);
|
||||
$this->assertEquals($teamName, $response['body']['memberships'][0]['teamName']);
|
||||
$this->assertEquals('owner', $response['body']['memberships'][0]['roles'][0]);
|
||||
$this->assertContains('owner', $response['body']['memberships'][0]['roles']);
|
||||
$this->assertContains('player', $response['body']['memberships'][0]['roles']);
|
||||
|
||||
$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'],
|
||||
|
@ -47,7 +88,8 @@ trait TeamsBaseClient
|
|||
$this->assertEquals($this->getUser()['name'], $response['body']['memberships'][0]['userName']);
|
||||
$this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['userEmail']);
|
||||
$this->assertEquals($teamName, $response['body']['memberships'][0]['teamName']);
|
||||
$this->assertEquals('owner', $response['body']['memberships'][0]['roles'][0]);
|
||||
$this->assertContains('owner', $response['body']['memberships'][0]['roles']);
|
||||
$this->assertContains('player', $response['body']['memberships'][0]['roles']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
@ -62,7 +104,8 @@ trait TeamsBaseClient
|
|||
$this->assertEquals($this->getUser()['name'], $response['body']['memberships'][0]['userName']);
|
||||
$this->assertEquals($this->getUser()['email'], $response['body']['memberships'][0]['userEmail']);
|
||||
$this->assertEquals($teamName, $response['body']['memberships'][0]['teamName']);
|
||||
$this->assertEquals('owner', $response['body']['memberships'][0]['roles'][0]);
|
||||
$this->assertContains('owner', $response['body']['memberships'][0]['roles']);
|
||||
$this->assertContains('player', $response['body']['memberships'][0]['roles']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
@ -209,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']);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
namespace Tests\E2E\Services\Users;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\ID;
|
||||
|
||||
trait UsersBase
|
||||
|
@ -379,11 +378,145 @@ trait UsersBase
|
|||
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
|
||||
$this->assertEquals($response['body']['users'][1]['$id'], 'user1');
|
||||
|
||||
$user1 = $response['body']['users'][1];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'cursor' => $response['body']['users'][0]['$id']
|
||||
'queries' => ['equal("name", "' . $user1['name'] . '")']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['users']);
|
||||
$this->assertCount(1, $response['body']['users']);
|
||||
$this->assertEquals($response['body']['users'][0]['name'], $user1['name']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['equal("email", "' . $user1['email'] . '")']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['users']);
|
||||
$this->assertCount(1, $response['body']['users']);
|
||||
$this->assertEquals($response['body']['users'][0]['email'], $user1['email']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['equal("status", true)']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['users']);
|
||||
$this->assertCount($totalUsers, $response['body']['users']);
|
||||
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
|
||||
$this->assertEquals($response['body']['users'][0]['status'], $user1['status']);
|
||||
$this->assertEquals($response['body']['users'][1]['$id'], $user1['$id']);
|
||||
$this->assertEquals($response['body']['users'][1]['status'], $user1['status']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['equal("status", false)']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertEmpty($response['body']['users']);
|
||||
$this->assertCount(0, $response['body']['users']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['equal("passwordUpdate", "' . $user1['passwordUpdate'] . '")']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['users']);
|
||||
$this->assertCount(1, $response['body']['users']);
|
||||
$this->assertEquals($response['body']['users'][0]['passwordUpdate'], $user1['passwordUpdate']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['equal("registration", "' . $user1['registration'] . '")']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['users']);
|
||||
$this->assertCount(1, $response['body']['users']);
|
||||
$this->assertEquals($response['body']['users'][0]['registration'], $user1['registration']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['equal("emailVerification", false)']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['users']);
|
||||
$this->assertCount($totalUsers, $response['body']['users']);
|
||||
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
|
||||
$this->assertEquals($response['body']['users'][0]['status'], $user1['status']);
|
||||
$this->assertEquals($response['body']['users'][1]['$id'], $user1['$id']);
|
||||
$this->assertEquals($response['body']['users'][1]['status'], $user1['status']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['equal("emailVerification", true)']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertEmpty($response['body']['users']);
|
||||
$this->assertCount(0, $response['body']['users']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['equal("phoneVerification", false)']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertIsArray($response['body']['users']);
|
||||
$this->assertCount($totalUsers, $response['body']['users']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['equal("phoneVerification", true)']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertEmpty($response['body']['users']);
|
||||
$this->assertCount(0, $response['body']['users']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['cursorAfter("' . $data['userId'] . '")']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -396,8 +529,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'cursor' => 'user1',
|
||||
'cursorDirection' => Database::CURSOR_BEFORE
|
||||
'queries' => ['cursorBefore("user1")']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -413,7 +545,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'Ronaldo'
|
||||
'search' => "Ronaldo",
|
||||
]);
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
@ -425,7 +557,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'cristiano.ronaldo@manchester-united.co.uk'
|
||||
'search' => "cristiano.ronaldo@manchester-united.co.uk",
|
||||
]);
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
@ -437,7 +569,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'cristiano.ronaldo'
|
||||
'search' => "cristiano.ronaldo",
|
||||
]);
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
@ -449,7 +581,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'manchester'
|
||||
'search' => "manchester",
|
||||
]);
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
|
@ -461,7 +593,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'united.co.uk'
|
||||
'search' => "united.co.uk",
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -475,7 +607,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'man'
|
||||
'search' => "man",
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -489,7 +621,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => $data['userId']
|
||||
'search' => $data['userId'],
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -505,7 +637,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'cursor' => 'unknown'
|
||||
'queries' => ['cursorAfter("unknown")']
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
@ -596,7 +728,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => $newName
|
||||
'search' => $newName,
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -609,7 +741,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => $id
|
||||
'search' => $id,
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -663,7 +795,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => $newEmail
|
||||
'search' => $newEmail,
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -676,7 +808,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => $id
|
||||
'search' => $id,
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
|
@ -852,7 +984,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'limit' => 1
|
||||
'queries' => [ 'limit(1)' ],
|
||||
]);
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
|
@ -864,7 +996,7 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'offset' => 1
|
||||
'queries' => [ 'offset(1)' ],
|
||||
]);
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
|
@ -875,14 +1007,79 @@ trait UsersBase
|
|||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'offset' => 1,
|
||||
'limit' => 1
|
||||
'queries' => [ 'limit(1)', 'offset(1)' ],
|
||||
]);
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
$this->assertIsArray($logs['body']['logs']);
|
||||
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
||||
$this->assertIsNumeric($logs['body']['total']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['limit(-1)']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['limit(101)']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['offset(-1)']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['offset(5001)']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['equal("$id", "asdf")']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['orderAsc("$id")']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['cursorAsc("$id")']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Tests\E2E\Services\Webhooks;
|
||||
|
||||
use Appwrite\Tests\Retry;
|
||||
use CURLFile;
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\Database\DateTime;
|
||||
|
@ -304,6 +305,7 @@ trait WebhooksBase
|
|||
/**
|
||||
* @depends testCreateCollection
|
||||
*/
|
||||
#[Retry(count: 1)]
|
||||
public function testDeleteDocument(array $data): array
|
||||
{
|
||||
$actorsId = $data['actorsId'];
|
||||
|
|
16
tests/extensions/Retry.php
Normal file
16
tests/extensions/Retry.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Tests;
|
||||
|
||||
/**
|
||||
* Allows test methods to be retried if they fail.
|
||||
*
|
||||
* Requires that the test class extends {@see TestCase} and has trait {@see Retryable}.
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_METHOD)]
|
||||
class Retry
|
||||
{
|
||||
public function __construct(protected int $count = 1)
|
||||
{
|
||||
}
|
||||
}
|
70
tests/extensions/Retryable.php
Normal file
70
tests/extensions/Retryable.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Allows test methods annotated with {@see Retry} to be retried.
|
||||
*/
|
||||
trait Retryable
|
||||
{
|
||||
/**
|
||||
* Custom runBare, hides and defers to PHPUnit {@see TestCase} runBare function,
|
||||
* accounting for any retries configured by the {@see Retry} annotation.
|
||||
*
|
||||
* @return void
|
||||
* @throws \ReflectionException
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function runBare(): void
|
||||
{
|
||||
$retries = $this->getNumberOfRetries();
|
||||
$ex = null;
|
||||
for ($i = 0; $i <= $retries; ++$i) {
|
||||
try {
|
||||
parent::runBare();
|
||||
return;
|
||||
} catch (\Throwable | \Exception $ex) {
|
||||
// Swallow the exception until we have exhausted our retries.
|
||||
if ($i !== $retries) {
|
||||
echo 'Flaky test failed, retrying...' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($ex) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
private function getNumberOfRetries(): int
|
||||
{
|
||||
$root = new \ReflectionClass($this);
|
||||
$case = $this->getTestCaseRoot($root);
|
||||
$name = $case->getProperty('name');
|
||||
$name->setAccessible(true);
|
||||
$name = $name->getValue($this);
|
||||
$method = $root->getMethod($name);
|
||||
$attributes = $method->getAttributes(Retry::class);
|
||||
$attribute = $attributes[0] ?? null;
|
||||
$args = $attribute?->getArguments();
|
||||
$retries = $args['count'] ?? 0;
|
||||
return \max(0, $retries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionClass $reflection
|
||||
* @return \ReflectionClass
|
||||
*/
|
||||
private function getTestCaseRoot(\ReflectionClass $reflection): \ReflectionClass
|
||||
{
|
||||
if ($reflection->getName() === TestCase::class) {
|
||||
return $reflection;
|
||||
}
|
||||
return $this->getTestCaseRoot($reflection->getParentClass());
|
||||
}
|
||||
}
|
121
tests/unit/Utopia/Database/Validator/IndexedQueriesTest.php
Normal file
121
tests/unit/Utopia/Database/Validator/IndexedQueriesTest.php
Normal file
|
@ -0,0 +1,121 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Utopia\Database\Validator;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\IndexedQueries;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Cursor;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Filter;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Limit;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Offset;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Order;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class IndexedQueriesTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function testEmptyQueries(): void
|
||||
{
|
||||
$validator = new IndexedQueries();
|
||||
|
||||
$this->assertEquals(true, $validator->isValid([]));
|
||||
}
|
||||
|
||||
public function testInvalidQuery(): void
|
||||
{
|
||||
$validator = new IndexedQueries();
|
||||
|
||||
$this->assertEquals(false, $validator->isValid(["this.is.invalid"]));
|
||||
}
|
||||
|
||||
public function testInvalidMethod(): void
|
||||
{
|
||||
$validator = new IndexedQueries();
|
||||
$this->assertEquals(false, $validator->isValid(['equal("attr", "value")']));
|
||||
|
||||
$validator = new IndexedQueries([], [], new Limit());
|
||||
$this->assertEquals(false, $validator->isValid(['equal("attr", "value")']));
|
||||
}
|
||||
|
||||
public function testInvalidValue(): void
|
||||
{
|
||||
$validator = new IndexedQueries([], [], new Limit());
|
||||
$this->assertEquals(false, $validator->isValid(['limit(-1)']));
|
||||
}
|
||||
|
||||
public function testValid(): void
|
||||
{
|
||||
$attributes = [
|
||||
new Document([
|
||||
'key' => 'name',
|
||||
'type' => Database::VAR_STRING,
|
||||
'array' => false,
|
||||
]),
|
||||
];
|
||||
$indexes = [
|
||||
new Document([
|
||||
'status' => 'available',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['name'],
|
||||
]),
|
||||
new Document([
|
||||
'status' => 'available',
|
||||
'type' => Database::INDEX_FULLTEXT,
|
||||
'attributes' => ['name'],
|
||||
]),
|
||||
];
|
||||
$validator = new IndexedQueries(
|
||||
$attributes,
|
||||
$indexes,
|
||||
new Cursor(),
|
||||
new Filter($attributes),
|
||||
new Limit(),
|
||||
new Offset(),
|
||||
new Order($attributes),
|
||||
);
|
||||
$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("name", "value")']), $validator->getDescription());
|
||||
}
|
||||
|
||||
public function testMissingIndex(): void
|
||||
{
|
||||
$attributes = [
|
||||
new Document([
|
||||
'key' => 'name',
|
||||
'type' => Database::VAR_STRING,
|
||||
'array' => false,
|
||||
]),
|
||||
];
|
||||
$indexes = [
|
||||
new Document([
|
||||
'status' => 'available',
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['name'],
|
||||
]),
|
||||
];
|
||||
$validator = new IndexedQueries(
|
||||
$attributes,
|
||||
$indexes,
|
||||
new Cursor(),
|
||||
new Filter($attributes),
|
||||
new Limit(),
|
||||
new Offset(),
|
||||
new Order($attributes),
|
||||
);
|
||||
$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("name", "value")']), $validator->getDescription());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Utopia\Database\Validator\Queries;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Base;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class CollectionTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function testEmptyQueries(): void
|
||||
{
|
||||
$validator = new Base('users', []);
|
||||
|
||||
$this->assertEquals($validator->isValid([]), true);
|
||||
}
|
||||
|
||||
public function testValid(): void
|
||||
{
|
||||
$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());
|
||||
}
|
||||
|
||||
public function testMissingIndex(): void
|
||||
{
|
||||
$validator = new Base('users', ['name']);
|
||||
$this->assertEquals(false, $validator->isValid(['equal("dne", "value")']), $validator->getDescription());
|
||||
$this->assertEquals(false, $validator->isValid(['orderAsc("dne")']), $validator->getDescription());
|
||||
}
|
||||
}
|
39
tests/unit/Utopia/Database/Validator/Queries/UsersTest.php
Normal file
39
tests/unit/Utopia/Database/Validator/Queries/UsersTest.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Utopia\Database\Validator\Queries;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Users;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class UsersTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function testIsValid(): void
|
||||
{
|
||||
$validator = new Users();
|
||||
|
||||
/**
|
||||
* Test for Success
|
||||
*/
|
||||
$this->assertEquals(true, $validator->isValid([]), $validator->getDescription());
|
||||
$this->assertEquals(true, $validator->isValid(['equal("name", "value")']), $validator->getDescription());
|
||||
$this->assertEquals(true, $validator->isValid(['equal("email", "value")']), $validator->getDescription());
|
||||
$this->assertEquals(true, $validator->isValid(['equal("phone", "value")']), $validator->getDescription());
|
||||
$this->assertEquals(true, $validator->isValid(['greaterThan("passwordUpdate", "2020-10-15 06:38")']), $validator->getDescription());
|
||||
$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());
|
||||
|
||||
/**
|
||||
* Test for Failure
|
||||
*/
|
||||
$this->assertEquals(false, $validator->isValid(['equal("password", "value")']), $validator->getDescription());
|
||||
}
|
||||
}
|
76
tests/unit/Utopia/Database/Validator/QueriesTest.php
Normal file
76
tests/unit/Utopia/Database/Validator/QueriesTest.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Utopia\Database\Validator;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Queries;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Cursor;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Filter;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Limit;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Offset;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Order;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class QueriesTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function testEmptyQueries(): void
|
||||
{
|
||||
$validator = new Queries();
|
||||
|
||||
$this->assertEquals(true, $validator->isValid([]));
|
||||
}
|
||||
|
||||
public function testInvalidQuery(): void
|
||||
{
|
||||
$validator = new Queries();
|
||||
|
||||
$this->assertEquals(false, $validator->isValid(["this.is.invalid"]));
|
||||
}
|
||||
|
||||
public function testInvalidMethod(): void
|
||||
{
|
||||
$validator = new Queries();
|
||||
$this->assertEquals(false, $validator->isValid(['equal("attr", "value")']));
|
||||
|
||||
$validator = new Queries(new Limit());
|
||||
$this->assertEquals(false, $validator->isValid(['equal("attr", "value")']));
|
||||
}
|
||||
|
||||
public function testInvalidValue(): void
|
||||
{
|
||||
$validator = new Queries(new Limit());
|
||||
$this->assertEquals(false, $validator->isValid(['limit(-1)']));
|
||||
}
|
||||
|
||||
public function testValid(): void
|
||||
{
|
||||
$attributes = [
|
||||
new Document([
|
||||
'key' => 'name',
|
||||
'type' => Database::VAR_STRING,
|
||||
'array' => false,
|
||||
])
|
||||
];
|
||||
$validator = new Queries(
|
||||
new Cursor(),
|
||||
new Filter($attributes),
|
||||
new Limit(),
|
||||
new Offset(),
|
||||
new Order($attributes),
|
||||
);
|
||||
$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());
|
||||
}
|
||||
}
|
41
tests/unit/Utopia/Database/Validator/Query/CursorTest.php
Normal file
41
tests/unit/Utopia/Database/Validator/Query/CursorTest.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Utopia\Database\Validator\Query;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Query\Base;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Query;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class CursorTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var Base
|
||||
*/
|
||||
protected $validator = null;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->validator = new Cursor();
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function testValue(): void
|
||||
{
|
||||
// Test for Success
|
||||
$this->assertEquals($this->validator->isValid(new Query(Query::TYPE_CURSORAFTER, values: ['asdf'])), true, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(new Query(Query::TYPE_CURSORBEFORE, values: ['asdf'])), true, $this->validator->getDescription());
|
||||
|
||||
// Test for Failure
|
||||
$this->assertEquals($this->validator->isValid(Query::limit(-1)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::limit(101)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::offset(-1)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::offset(5001)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::equal('attr', ['v'])), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::orderAsc('attr')), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::orderDesc('attr')), false, $this->validator->getDescription());
|
||||
}
|
||||
}
|
59
tests/unit/Utopia/Database/Validator/Query/FilterTest.php
Normal file
59
tests/unit/Utopia/Database/Validator/Query/FilterTest.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Utopia\Database\Validator\Query;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Query\Base;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Filter;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class FilterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var Base
|
||||
*/
|
||||
protected $validator = null;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->validator = new Filter(
|
||||
attributes: [
|
||||
new Document([
|
||||
'key' => 'attr',
|
||||
'type' => Database::VAR_STRING,
|
||||
'array' => false,
|
||||
]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function testValue(): void
|
||||
{
|
||||
// Test for Success
|
||||
$this->assertEquals($this->validator->isValid(Query::equal('attr', ['v'])), true, $this->validator->getDescription());
|
||||
|
||||
// Test for Failure
|
||||
$this->assertEquals($this->validator->isValid(Query::limit(1)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::limit(0)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::limit(100)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::limit(-1)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::limit(101)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::offset(1)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::offset(0)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::offset(5000)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::offset(-1)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::offset(5001)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::equal('dne', ['v'])), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::equal('', ['v'])), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::orderAsc('attr')), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::orderDesc('attr')), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(new Query(Query::TYPE_CURSORAFTER, values: ['asdf'])), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(new Query(Query::TYPE_CURSORBEFORE, values: ['asdf'])), false, $this->validator->getDescription());
|
||||
}
|
||||
}
|
37
tests/unit/Utopia/Database/Validator/Query/LimitTest.php
Normal file
37
tests/unit/Utopia/Database/Validator/Query/LimitTest.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Utopia\Database\Validator\Query;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Query\Base;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Query;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class LimitTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var Base
|
||||
*/
|
||||
protected $validator = null;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->validator = new Limit(100);
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function testValue(): void
|
||||
{
|
||||
// Test for Success
|
||||
$this->assertEquals($this->validator->isValid(Query::limit(1)), true, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::limit(0)), true, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::limit(100)), true, $this->validator->getDescription());
|
||||
|
||||
// Test for Failure
|
||||
$this->assertEquals($this->validator->isValid(Query::limit(-1)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::limit(101)), false, $this->validator->getDescription());
|
||||
}
|
||||
}
|
41
tests/unit/Utopia/Database/Validator/Query/OffsetTest.php
Normal file
41
tests/unit/Utopia/Database/Validator/Query/OffsetTest.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Utopia\Database\Validator\Query;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Query\Base;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Query;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class OffsetTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var Base
|
||||
*/
|
||||
protected $validator = null;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->validator = new Offset(5000);
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function testValue(): void
|
||||
{
|
||||
// Test for Success
|
||||
$this->assertEquals($this->validator->isValid(Query::offset(1)), true, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::offset(0)), true, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::offset(5000)), true, $this->validator->getDescription());
|
||||
|
||||
// Test for Failure
|
||||
$this->assertEquals($this->validator->isValid(Query::offset(-1)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::offset(5001)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::equal('attr', ['v'])), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::orderAsc('attr')), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::orderDesc('attr')), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::limit(100)), false, $this->validator->getDescription());
|
||||
}
|
||||
}
|
55
tests/unit/Utopia/Database/Validator/Query/OrderTest.php
Normal file
55
tests/unit/Utopia/Database/Validator/Query/OrderTest.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Utopia\Database\Validator\Query;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Query\Base;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Order;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class OrderTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var Base
|
||||
*/
|
||||
protected $validator = null;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->validator = new Order(
|
||||
attributes: [
|
||||
new Document([
|
||||
'key' => 'attr',
|
||||
'type' => Database::VAR_STRING,
|
||||
'array' => false,
|
||||
]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function testValue(): void
|
||||
{
|
||||
// Test for Success
|
||||
$this->assertEquals($this->validator->isValid(Query::orderAsc('attr')), true, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::orderAsc('')), true, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::orderDesc('attr')), true, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::orderDesc('')), true, $this->validator->getDescription());
|
||||
|
||||
// Test for Failure
|
||||
$this->assertEquals($this->validator->isValid(Query::limit(-1)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::limit(101)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::offset(-1)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::offset(5001)), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::equal('attr', ['v'])), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::equal('dne', ['v'])), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::equal('', ['v'])), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::orderDesc('dne')), false, $this->validator->getDescription());
|
||||
$this->assertEquals($this->validator->isValid(Query::orderAsc('dne')), false, $this->validator->getDescription());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue