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

Merge branch 'feat-database-indexing' of https://github.com/appwrite/appwrite into feat-database-disable-collections

This commit is contained in:
Torsten Dittmann 2021-12-17 11:53:57 +01:00
commit 2690cab771
37 changed files with 835 additions and 800 deletions

View file

@ -31,10 +31,10 @@ ENV DEBUG=$DEBUG
ENV PHP_REDIS_VERSION=5.3.4 \
PHP_MONGODB_VERSION=1.9.1 \
PHP_SWOOLE_VERSION=v4.8.0 \
PHP_SWOOLE_VERSION=v4.8.3 \
PHP_IMAGICK_VERSION=3.5.1 \
PHP_YAML_VERSION=2.2.1 \
PHP_MAXMINDDB_VERSION=v1.10.1
PHP_YAML_VERSION=2.2.2 \
PHP_MAXMINDDB_VERSION=v1.11.0
RUN \
apk add --no-cache --virtual .deps \

View file

@ -317,7 +317,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('scope', 'public')
->label('docs', false)
->param('projectId', '', new Text(1024), 'Project unique ID.')
->param('projectId', '', new Text(1024), 'Project ID.')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
->param('code', '', new Text(1024), 'OAuth2 code.')
->param('state', '', new Text(2048), 'Login state params.', true)
@ -344,7 +344,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->label('scope', 'public')
->label('origin', '*')
->label('docs', false)
->param('projectId', '', new Text(1024), 'Project unique ID.')
->param('projectId', '', new Text(1024), 'Project ID.')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.')
->param('code', '', new Text(1024), 'OAuth2 code.')
->param('state', '', new Text(2048), 'Login state params.', true)
@ -669,26 +669,24 @@ App::post('/v1/account/sessions/magic-url')
$userId = $userId == 'unique()' ? $dbForInternal->getId() : $userId;
$user = Authorization::skip(function () use ($dbForInternal, $userId, $email) {
return $dbForInternal->createDocument('users', new Document([
'$id' => $userId,
'$read' => ['role:all'],
'$write' => ['user:' . $userId],
'email' => $email,
'emailVerification' => false,
'status' => true,
'password' => null,
'passwordUpdate' => \time(),
'registration' => \time(),
'reset' => false,
'prefs' => [],
'sessions' => [],
'tokens' => [],
'memberships' => [],
'search' => implode(' ', [$userId, $email]),
'deleted' => false
]));
});
$user = Authorization::skip(fn () => $dbForInternal->createDocument('users', new Document([
'$id' => $userId,
'$read' => ['role:all'],
'$write' => ['user:' . $userId],
'email' => $email,
'emailVerification' => false,
'status' => true,
'password' => null,
'passwordUpdate' => \time(),
'registration' => \time(),
'reset' => false,
'prefs' => [],
'sessions' => [],
'tokens' => [],
'memberships' => [],
'search' => implode(' ', [$userId, $email]),
'deleted' => false
])));
$mails->setParam('event', 'users.create');
$audits->setParam('event', 'users.create');
@ -772,7 +770,7 @@ App::put('/v1/account/sessions/magic-url')
->label('sdk.response.model', Response::MODEL_SESSION)
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},userId:{param-userId}')
->param('userId', '', new CustomId(), 'User unique ID.')
->param('userId', '', new CustomId(), 'User ID.')
->param('secret', '', new Text(256), 'Valid verification token.')
->inject('request')
->inject('response')
@ -1186,8 +1184,8 @@ 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. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->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)
->inject('response')
->inject('user')
->inject('locale')
@ -1272,7 +1270,7 @@ App::get('/v1/account/sessions/:sessionId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_SESSION)
->param('sessionId', null, new UID(), 'Session unique ID. Use the string \'current\' to get the current device session.')
->param('sessionId', null, new UID(), 'Session ID. Use the string \'current\' to get the current device session.')
->inject('response')
->inject('user')
->inject('locale')
@ -1368,8 +1366,8 @@ App::patch('/v1/account/password')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('password', '', new Password(), 'User password. Must be at least 8 chars.')
->param('oldPassword', '', new Password(), 'Old user password. Must be at least 8 chars.', true)
->param('password', '', new Password(), 'New user password. Must be at least 8 chars.')
->param('oldPassword', '', new Password(), 'Current user password. Must be at least 8 chars.', true)
->inject('response')
->inject('user')
->inject('dbForInternal')
@ -1586,7 +1584,7 @@ App::delete('/v1/account/sessions/:sessionId')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->label('abuse-limit', 100)
->param('sessionId', null, new UID(), 'Session unique ID. Use the string \'current\' to delete the current device session.')
->param('sessionId', null, new UID(), 'Session ID. Use the string \'current\' to delete the current device session.')
->inject('request')
->inject('response')
->inject('user')
@ -1869,10 +1867,10 @@ App::put('/v1/account/recovery')
->label('sdk.response.model', Response::MODEL_TOKEN)
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},userId:{param-userId}')
->param('userId', '', new UID(), 'User account UID address.')
->param('userId', '', new UID(), 'User ID.')
->param('secret', '', new Text(256), 'Valid reset token.')
->param('password', '', new Password(), 'User password. Must be at least 8 chars.')
->param('passwordAgain', '', new Password(), 'New password again. Must be at least 8 chars.')
->param('password', '', new Password(), 'New user password. Must be at least 8 chars.')
->param('passwordAgain', '', new Password(), 'Repeat new user password. Must be at least 8 chars.')
->inject('response')
->inject('dbForInternal')
->inject('audits')
@ -2052,7 +2050,7 @@ App::put('/v1/account/verification')
->label('sdk.response.model', Response::MODEL_TOKEN)
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},userId:{param-userId}')
->param('userId', '', new UID(), 'User unique ID.')
->param('userId', '', new UID(), 'User ID.')
->param('secret', '', new Text(256), 'Valid verification token.')
->inject('response')
->inject('user')

View file

@ -95,9 +95,7 @@ App::get('/v1/avatars/credit-cards/:code')
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->inject('response')
->action(function ($code, $width, $height, $quality, $response) use ($avatarCallback) {
return $avatarCallback('credit-cards', $code, $width, $height, $quality, $response);
});
->action(fn($code, $width, $height, $quality, $response) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response));
App::get('/v1/avatars/browsers/:code')
->desc('Get Browser Icon')
@ -115,9 +113,7 @@ App::get('/v1/avatars/browsers/:code')
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->inject('response')
->action(function ($code, $width, $height, $quality, $response) use ($avatarCallback) {
return $avatarCallback('browsers', $code, $width, $height, $quality, $response);
});
->action(fn($code, $width, $height, $quality, $response) => $avatarCallback('browsers', $code, $width, $height, $quality, $response));
App::get('/v1/avatars/flags/:code')
->desc('Get Country Flag')
@ -135,9 +131,7 @@ App::get('/v1/avatars/flags/:code')
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->inject('response')
->action(function ($code, $width, $height, $quality, $response) use ($avatarCallback) {
return $avatarCallback('flags', $code, $width, $height, $quality, $response);
});
->action(fn($code, $width, $height, $quality, $response) => $avatarCallback('flags', $code, $width, $height, $quality, $response));
App::get('/v1/avatars/image')
->desc('Get Image from URL')

View file

@ -32,7 +32,7 @@ use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\IP;
use Appwrite\Network\Validator\URL;
use Appwrite\Utopia\Response;
use DeviceDetector\DeviceDetector;
use Appwrite\Detector\Detector;
/**
* Create attribute of varying type
@ -50,7 +50,7 @@ use DeviceDetector\DeviceDetector;
*/
function createAttribute($collectionId, $attribute, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage): Document
{
$attributeId = $attribute->getId();
$key = $attribute->getAttribute('key');
$type = $attribute->getAttribute('type', '');
$size = $attribute->getAttribute('size', 0);
$required = $attribute->getAttribute('required', true);
@ -59,7 +59,7 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $
$format = $attribute->getAttribute('format', '');
$formatOptions = $attribute->getAttribute('formatOptions', []);
$filters = $attribute->getAttribute('filters', []); // filters are hidden from the endpoint
$default = $attribute->getAttribute('default', null);
$default = $attribute->getAttribute('default');
$collection = $dbForInternal->getDocument('collections', $collectionId);
@ -84,8 +84,8 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $
try {
$attribute = new Document([
'$id' => $collectionId.'_'.$attributeId,
'key' => $attributeId,
'$id' => $collectionId.'_'.$key,
'key' => $key,
'collectionId' => $collectionId,
'type' => $type,
'status' => 'processing', // processing, available, failed, deleting, stuck
@ -149,9 +149,9 @@ App::post('/v1/database/collections')
->label('sdk.response.model', Response::MODEL_COLLECTION)
->param('collectionId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.')
->param('permission', null, new WhiteList(['document', 'collection']), 'Permissions type model to use for reading documents in this collection. You can use collection-level permission set once on the collection using the `read` and `write` params, or you can set document-level permission where each document read and write params will decide who has access to read and write to each document individually. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('permission', null, new WhiteList(['document', 'collection']), 'Permissions type model to use for reading documents in this collection. You can use collection-level permission set once on the collection using the `read` and `write` params, or you can set document-level permission where each document read and write params will decide who has access to read and write to each document individually. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')
@ -207,8 +207,8 @@ App::get('/v1/database/collections')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_COLLECTION_LIST)
->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('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.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
@ -252,7 +252,7 @@ App::get('/v1/database/collections/:collectionId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_COLLECTION)
->param('collectionId', '', new UID(), 'Collection unique ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->inject('response')
->inject('dbForInternal')
->inject('usage')
@ -392,7 +392,7 @@ App::get('/v1/database/:collectionId/usage')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USAGE_COLLECTION)
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
->param('collectionId', '', new UID(), 'Collection unique ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')
@ -499,9 +499,9 @@ App::get('/v1/database/collections/:collectionId/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('collectionId', '', new UID(), 'Collection unique ID.')
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->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)
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')
@ -530,24 +530,12 @@ App::get('/v1/database/collections/:collectionId/logs')
foreach ($logs as $i => &$log) {
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
$dd = new DeviceDetector($log['userAgent']);
$detector = new Detector($log['userAgent']);
$detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
$dd->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
$dd->parse();
$os = $dd->getOs();
$osCode = (isset($os['short_name'])) ? $os['short_name'] : '';
$osName = (isset($os['name'])) ? $os['name'] : '';
$osVersion = (isset($os['version'])) ? $os['version'] : '';
$client = $dd->getClient();
$clientType = (isset($client['type'])) ? $client['type'] : '';
$clientCode = (isset($client['short_name'])) ? $client['short_name'] : '';
$clientName = (isset($client['name'])) ? $client['name'] : '';
$clientVersion = (isset($client['version'])) ? $client['version'] : '';
$clientEngine = (isset($client['engine'])) ? $client['engine'] : '';
$clientEngineVersion = (isset($client['engine_version'])) ? $client['engine_version'] : '';
$os = $detector->getOS();
$client = $detector->getClient();
$device = $detector->getDevice();
$output[$i] = new Document([
'event' => $log['event'],
@ -557,19 +545,18 @@ App::get('/v1/database/collections/:collectionId/logs')
'mode' => $log['data']['mode'] ?? null,
'ip' => $log['ip'],
'time' => $log['time'],
'osCode' => $osCode,
'osName' => $osName,
'osVersion' => $osVersion,
'clientType' => $clientType,
'clientCode' => $clientCode,
'clientName' => $clientName,
'clientVersion' => $clientVersion,
'clientEngine' => $clientEngine,
'clientEngineVersion' => $clientEngineVersion,
'deviceName' => $dd->getDeviceName(),
'deviceBrand' => $dd->getBrandName(),
'deviceModel' => $dd->getModel(),
'osCode' => $os['osCode'],
'osName' => $os['osName'],
'osVersion' => $os['osVersion'],
'clientType' => $client['clientType'],
'clientCode' => $client['clientCode'],
'clientName' => $client['clientName'],
'clientVersion' => $client['clientVersion'],
'clientEngine' => $client['clientEngine'],
'clientEngineVersion' => $client['clientEngineVersion'],
'deviceName' => $device['deviceName'],
'deviceBrand' => $device['deviceBrand'],
'deviceModel' => $device['deviceModel']
]);
$record = $geodb->get($log['ip']);
@ -601,11 +588,11 @@ App::put('/v1/database/collections/:collectionId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_COLLECTION)
->param('collectionId', '', new UID(), 'Collection unique ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('name', null, new Text(128), 'Collection name. Max length: 128 chars.')
->param('permission', null, new WhiteList(['document', 'collection']), 'Permissions type model to use for reading documents in this collection. You can use collection-level permission set once on the collection using the `read` and `write` params, or you can set document-level permission where each document read and write params will decide who has access to read and write to each document individually. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
->param('permission', null, new WhiteList(['document', 'collection']), 'Permissions type model to use for reading documents in this collection. You can use collection-level permission set once on the collection using the `read` and `write` params, or you can set document-level permission where each document read and write params will decide who has access to read and write to each document individually. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
->param('enabled', true, new Boolean(), 'Is collection enabled?', true)
->inject('response')
->inject('dbForInternal')
@ -667,7 +654,7 @@ App::delete('/v1/database/collections/:collectionId')
->label('sdk.description', '/docs/references/database/delete-collection.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('collectionId', '', new UID(), 'Collection unique ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')
@ -727,8 +714,8 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_STRING)
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Range::TYPE_INTEGER), 'Attribute size for text attributes, in number of characters.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Text(0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
@ -739,7 +726,7 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $size, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $size, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal*/
/** @var Utopia\Database\Database $dbForExternal*/
@ -754,7 +741,7 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
}
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_STRING,
'size' => $size,
'required' => $required,
@ -777,8 +764,8 @@ App::post('/v1/database/collections/:collectionId/attributes/email')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_EMAIL)
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Email(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
@ -788,7 +775,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal*/
/** @var Utopia\Database\Database $dbForExternal*/
@ -797,7 +784,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email')
/** @var Appwrite\Stats\Stats $usage */
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_STRING,
'size' => 254,
'required' => $required,
@ -821,8 +808,8 @@ App::post('/v1/database/collections/:collectionId/attributes/enum')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_ENUM)
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('elements', [], new ArrayList(new Text(0)), 'Array of elements in enumerated type. Uses length of longest element to determine size.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Text(0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
@ -833,7 +820,7 @@ App::post('/v1/database/collections/:collectionId/attributes/enum')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $elements, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $elements, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal*/
/** @var Utopia\Database\Database $dbForExternal*/
@ -853,7 +840,7 @@ App::post('/v1/database/collections/:collectionId/attributes/enum')
}
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_STRING,
'size' => $size,
'required' => $required,
@ -878,8 +865,8 @@ App::post('/v1/database/collections/:collectionId/attributes/ip')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_IP)
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new IP(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
@ -889,7 +876,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal*/
/** @var Utopia\Database\Database $dbForExternal*/
@ -898,7 +885,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip')
/** @var Appwrite\Stats\Stats $usage */
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_STRING,
'size' => 39,
'required' => $required,
@ -922,8 +909,8 @@ App::post('/v1/database/collections/:collectionId/attributes/url')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_URL)
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new URL(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
@ -933,7 +920,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForExternal*/
/** @var Appwrite\Event\Event $database */
@ -941,7 +928,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url')
/** @var Appwrite\Stats\Stats $usage */
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_STRING,
'size' => 2000,
'required' => $required,
@ -965,8 +952,8 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_INTEGER)
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new Integer(), 'Minimum value to enforce on new documents', true)
->param('max', null, new Integer(), 'Maximum value to enforce on new documents', true)
@ -978,7 +965,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $required, $min, $max, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal*/
/** @var Utopia\Database\Database $dbForExternal*/
@ -1001,7 +988,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
}
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_INTEGER,
'size' => 0,
'required' => $required,
@ -1036,8 +1023,8 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_FLOAT)
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new FloatValidator(), 'Minimum value to enforce on new documents', true)
->param('max', null, new FloatValidator(), 'Maximum value to enforce on new documents', true)
@ -1049,7 +1036,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForInternal, $dbForExternal,$database, $audits, $usage) {
->action(function ($collectionId, $key, $required, $min, $max, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal*/
/** @var Utopia\Database\Database $dbForExternal*/
@ -1065,6 +1052,11 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
throw new Exception('Minimum value must be lesser than maximum value', 400);
}
// Ensure default value is a float
if (!is_null($default)) {
$default = \floatval($default);
}
$validator = new Range($min, $max, Database::VAR_FLOAT);
if (!is_null($default) && !$validator->isValid($default)) {
@ -1072,7 +1064,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
}
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_FLOAT,
'required' => $required,
'size' => 0,
@ -1107,8 +1099,8 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_BOOLEAN)
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Boolean(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
@ -1118,7 +1110,7 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal*/
/** @var Appwrite\Event\Event $database */
@ -1126,7 +1118,7 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean')
/** @var Appwrite\Stats\Stats $usage */
$attribute = createAttribute($collectionId, new Document([
'$id' => $attributeId,
'key' => $key,
'type' => Database::VAR_BOOLEAN,
'size' => 0,
'required' => $required,
@ -1148,7 +1140,7 @@ App::get('/v1/database/collections/:collectionId/attributes')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ATTRIBUTE_LIST)
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->inject('response')
->inject('dbForInternal')
->inject('usage')
@ -1172,7 +1164,7 @@ App::get('/v1/database/collections/:collectionId/attributes')
]), Response::MODEL_ATTRIBUTE_LIST);
});
App::get('/v1/database/collections/:collectionId/attributes/:attributeId')
App::get('/v1/database/collections/:collectionId/attributes/:key')
->desc('Get Attribute')
->groups(['api', 'database'])
->label('scope', 'collections.read')
@ -1191,12 +1183,12 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId')
Response::MODEL_ATTRIBUTE_URL,
Response::MODEL_ATTRIBUTE_IP,
Response::MODEL_ATTRIBUTE_STRING,])// needs to be last, since its condition would dominate any other string attribute
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->inject('response')
->inject('dbForInternal')
->inject('usage')
->action(function ($collectionId, $attributeId, $response, $dbForInternal, $usage) {
->action(function ($collectionId, $key, $response, $dbForInternal, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
@ -1206,7 +1198,7 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId')
throw new Exception('Collection not found', 404);
}
$attribute = $collection->find('$id', $attributeId, 'attributes');
$attribute = $collection->find('$id', $key, 'attributes');
if (!$attribute) {
throw new Exception('Attribute not found', 404);
@ -1235,7 +1227,7 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId')
$response->dynamic($attribute, $model);
});
App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
App::delete('/v1/database/collections/:collectionId/attributes/:key')
->desc('Delete Attribute')
->groups(['api', 'database'])
->label('scope', 'collections.write')
@ -1246,8 +1238,8 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
->label('sdk.description', '/docs/references/database/delete-attribute.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('attributeId', '', new Key(), 'Attribute ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')
@ -1255,7 +1247,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
->inject('events')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $attributeId, $response, $dbForInternal, $dbForExternal, $database, $events, $audits, $usage) {
->action(function ($collectionId, $key, $response, $dbForInternal, $dbForExternal, $database, $events, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Utopia\Database\Database $dbForExternal */
@ -1270,7 +1262,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
throw new Exception('Collection not found', 404);
}
$attribute = $dbForInternal->getDocument('attributes', $collectionId.'_'.$attributeId);
$attribute = $dbForInternal->getDocument('attributes', $collectionId.'_'.$key);
if (empty($attribute->getId())) {
throw new Exception('Attribute not found', 404);
@ -1335,8 +1327,8 @@ App::post('/v1/database/collections/:collectionId/indexes')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_INDEX)
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('indexId', null, new Key(), 'Index ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('key', null, new Key(), 'Index Key.')
->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE, Database::INDEX_SPATIAL, Database::INDEX_ARRAY]), 'Index type.')
->param('attributes', null, new ArrayList(new Key()), 'Array of attributes to index.')
->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING)), 'Array of index orders.', true)
@ -1345,7 +1337,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $indexId, $type, $attributes, $orders, $response, $dbForInternal, $database, $audits, $usage) {
->action(function ($collectionId, $key, $type, $attributes, $orders, $response, $dbForInternal, $database, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Appwrite\Event\Event $database */
@ -1376,7 +1368,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
// lengths hidden by default
$lengths = [];
foreach ($attributes as $key => $attribute) {
foreach ($attributes as $i => $attribute) {
// find attribute metadata in collection document
$attributeIndex = \array_search($attribute, array_column($oldAttributes, 'key'));
@ -1394,13 +1386,13 @@ App::post('/v1/database/collections/:collectionId/indexes')
}
// set attribute size as index length only for strings
$lengths[$key] = ($attributeType === Database::VAR_STRING) ? $attributeSize : null;
$lengths[$i] = ($attributeType === Database::VAR_STRING) ? $attributeSize : null;
}
try {
$index = $dbForInternal->createDocument('indexes', new Document([
'$id' => $collectionId.'_'.$indexId,
'key' => $indexId,
'$id' => $collectionId.'_'.$key,
'key' => $key,
'status' => 'processing', // processing, available, failed, deleting, stuck
'collectionId' => $collectionId,
'type' => $type,
@ -1443,7 +1435,7 @@ App::get('/v1/database/collections/:collectionId/indexes')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_INDEX_LIST)
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->inject('response')
->inject('dbForInternal')
->inject('usage')
@ -1459,21 +1451,15 @@ App::get('/v1/database/collections/:collectionId/indexes')
$indexes = $collection->getAttribute('indexes');
$indexes = array_map(function ($index) use ($collection) {
return new Document([\array_merge($index, [
'collectionId' => $collection->getId(),
])]);
}, $indexes);
$usage->setParam('database.collections.read', 1);
$response->dynamic(new Document([
'sum' => \count($indexes),
'attributes' => $indexes,
'indexes' => $indexes,
]), Response::MODEL_INDEX_LIST);
});
App::get('/v1/database/collections/:collectionId/indexes/:indexId')
App::get('/v1/database/collections/:collectionId/indexes/:key')
->desc('Get Index')
->groups(['api', 'database'])
->label('scope', 'collections.read')
@ -1484,12 +1470,12 @@ App::get('/v1/database/collections/:collectionId/indexes/:indexId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_INDEX)
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('indexId', null, new Key(), 'Index ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('key', null, new Key(), 'Index Key.')
->inject('response')
->inject('dbForInternal')
->inject('usage')
->action(function ($collectionId, $indexId, $response, $dbForInternal, $usage) {
->action(function ($collectionId, $key, $response, $dbForInternal, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
@ -1502,7 +1488,7 @@ App::get('/v1/database/collections/:collectionId/indexes/:indexId')
$indexes = $collection->getAttribute('indexes');
// Search for index
$indexIndex = array_search($indexId, array_column($indexes, '$id'));
$indexIndex = array_search($key, array_column($indexes, 'key'));
if ($indexIndex === false) {
throw new Exception('Index not found', 404);
@ -1513,11 +1499,11 @@ App::get('/v1/database/collections/:collectionId/indexes/:indexId')
])]);
$usage->setParam('database.collections.read', 1);
$response->dynamic($index, Response::MODEL_INDEX);
});
App::delete('/v1/database/collections/:collectionId/indexes/:indexId')
App::delete('/v1/database/collections/:collectionId/indexes/:key')
->desc('Delete Index')
->groups(['api', 'database'])
->label('scope', 'collections.write')
@ -1528,15 +1514,15 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId')
->label('sdk.description', '/docs/references/database/delete-index.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('collectionId', null, new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('indexId', '', new Key(), 'Index ID.')
->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('key', '', new Key(), 'Index Key.')
->inject('response')
->inject('dbForInternal')
->inject('database')
->inject('events')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $indexId, $response, $dbForInternal, $database, $events, $audits, $usage) {
->action(function ($collectionId, $key, $response, $dbForInternal, $database, $events, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Appwrite\Event\Event $database */
@ -1550,7 +1536,7 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId')
throw new Exception('Collection not found', 404);
}
$index = $dbForInternal->getDocument('indexes', $collectionId.'_'.$indexId);
$index = $dbForInternal->getDocument('indexes', $collectionId.'_'.$key);
if (empty($index->getId())) {
throw new Exception('Index not found', 404);
@ -1596,25 +1582,27 @@ App::post('/v1/database/collections/:collectionId/documents')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DOCUMENT)
->param('documentId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('collectionId', null, new UID(), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).')
->param('documentId', '', new CustomId(), 'Document ID. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection with validation rules using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('data', [], new JSON(), 'Document data as JSON object.')
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')
->inject('user')
->inject('audits')
->inject('usage')
->inject('events')
->inject('mode')
->action(function ($documentId, $collectionId, $data, $read, $write, $response, $dbForInternal, $dbForExternal, $user, $audits, $usage, $mode) {
->action(function ($documentId, $collectionId, $data, $read, $write, $response, $dbForInternal, $dbForExternal, $user, $audits, $usage, $events, $mode) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Utopia\Database\Database $dbForExternal */
/** @var Utopia\Database\Document $user */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
/** @var string $mode */
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
@ -1627,7 +1615,10 @@ App::post('/v1/database/collections/:collectionId/documents')
throw new Exception('$id is not allowed for creating new documents, try update instead', 400);
}
$collection = $dbForInternal->getDocument('collections', $collectionId);
/**
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
*/
$collection = Authorization::skip(fn() => $dbForInternal->getDocument('collections', $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled') && $mode !== APP_MODE_ADMIN )) {
throw new Exception('Collection not found', 404);
@ -1665,9 +1656,7 @@ App::post('/v1/database/collections/:collectionId/documents')
try {
if ($collection->getAttribute('permission') === 'collection') {
/** @var Document $document */
$document = Authorization::skip(function() use ($dbForExternal, $collectionId, $data) {
return $dbForExternal->createDocument($collectionId, new Document($data));
});
$document = Authorization::skip(fn() => $dbForExternal->createDocument($collectionId, new Document($data)));
} else {
$document = $dbForExternal->createDocument($collectionId, new Document($data));
}
@ -1679,6 +1668,8 @@ App::post('/v1/database/collections/:collectionId/documents')
throw new Exception('Document already exists', 409);
}
$events->setParam('collection', $collection->getArrayCopy());
$usage
->setParam('database.documents.create', 1)
->setParam('collectionId', $collectionId)
@ -1705,11 +1696,11 @@ App::get('/v1/database/collections/:collectionId/documents')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DOCUMENT_LIST)
->param('collectionId', '', new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('queries', [], new ArrayList(new Text(128)), 'Array of query strings.', true)
->param('limit', 25, new Range(0, 100), 'Maximum number of documents to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage 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.', 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 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 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.', true)
->param('orderAttributes', [], new ArrayList(new Text(128)), 'Array of attributes used to sort results.', true)
->param('orderTypes', [], new ArrayList(new WhiteList(['DESC', 'ASC'], true)), 'Array of order directions for sorting attribtues. Possible values are DESC for descending order, or ASC for ascending order.', true)
@ -1725,7 +1716,10 @@ App::get('/v1/database/collections/:collectionId/documents')
/** @var Appwrite\Stats\Stats $usage */
/** @var string $mode */
$collection = $dbForInternal->getDocument('collections', $collectionId);
/**
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
*/
$collection = Authorization::skip(fn() => $dbForInternal->getDocument('collections', $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled') && $mode !== APP_MODE_ADMIN )) {
throw new Exception('Collection not found', 404);
@ -1761,11 +1755,11 @@ App::get('/v1/database/collections/:collectionId/documents')
if ($collection->getAttribute('permission') === 'collection') {
/** @var Document[] $documents */
$documents = Authorization::skip(function() use ($dbForExternal, $collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument, $cursorDirection) {
return $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection);
});
$documents = Authorization::skip(fn() => $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection));
$sum = Authorization::skip(fn() => $dbForExternal->count($collectionId, $queries, APP_LIMIT_COUNT));
} else {
$documents = $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection);
$sum = $dbForExternal->count($collectionId, $queries, APP_LIMIT_COUNT);
}
$usage
@ -1774,7 +1768,7 @@ App::get('/v1/database/collections/:collectionId/documents')
;
$response->dynamic(new Document([
'sum' => $dbForExternal->count($collectionId, $queries, APP_LIMIT_COUNT),
'sum' => $sum,
'documents' => $documents,
]), Response::MODEL_DOCUMENT_LIST);
});
@ -1790,8 +1784,8 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DOCUMENT)
->param('collectionId', null, new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('documentId', null, new UID(), 'Document unique ID.')
->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('documentId', null, new UID(), 'Document ID.')
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')
@ -1803,7 +1797,10 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
/** @var Utopia\Database\Database $dbForExternal */
/** @var string $mode */
$collection = $dbForInternal->getDocument('collections', $collectionId);
/**
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
*/
$collection = Authorization::skip(fn() => $dbForInternal->getDocument('collections', $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled') && $mode !== APP_MODE_ADMIN )) {
throw new Exception('Collection not found', 404);
@ -1819,9 +1816,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
if ($collection->getAttribute('permission') === 'collection') {
/** @var Document $document */
$document = Authorization::skip(function() use ($dbForExternal, $collectionId, $documentId) {
return $dbForExternal->getDocument($collectionId, $documentId);
});
$document = Authorization::skip(fn() => $dbForExternal->getDocument($collectionId, $documentId));
} else {
$document = $dbForExternal->getDocument($collectionId, $documentId);
}
@ -1849,10 +1844,10 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId/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('collectionId', '', new UID(), 'Collection unique ID.')
->param('documentId', null, new UID(), 'Document unique ID.')
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->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)
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')
@ -1887,24 +1882,12 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId/logs')
foreach ($logs as $i => &$log) {
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
$dd = new DeviceDetector($log['userAgent']);
$detector = new Detector($log['userAgent']);
$detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
$dd->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
$dd->parse();
$os = $dd->getOs();
$osCode = (isset($os['short_name'])) ? $os['short_name'] : '';
$osName = (isset($os['name'])) ? $os['name'] : '';
$osVersion = (isset($os['version'])) ? $os['version'] : '';
$client = $dd->getClient();
$clientType = (isset($client['type'])) ? $client['type'] : '';
$clientCode = (isset($client['short_name'])) ? $client['short_name'] : '';
$clientName = (isset($client['name'])) ? $client['name'] : '';
$clientVersion = (isset($client['version'])) ? $client['version'] : '';
$clientEngine = (isset($client['engine'])) ? $client['engine'] : '';
$clientEngineVersion = (isset($client['engine_version'])) ? $client['engine_version'] : '';
$os = $detector->getOS();
$client = $detector->getClient();
$device = $detector->getDevice();
$output[$i] = new Document([
'event' => $log['event'],
@ -1914,19 +1897,18 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId/logs')
'mode' => $log['data']['mode'] ?? null,
'ip' => $log['ip'],
'time' => $log['time'],
'osCode' => $osCode,
'osName' => $osName,
'osVersion' => $osVersion,
'clientType' => $clientType,
'clientCode' => $clientCode,
'clientName' => $clientName,
'clientVersion' => $clientVersion,
'clientEngine' => $clientEngine,
'clientEngineVersion' => $clientEngineVersion,
'deviceName' => $dd->getDeviceName(),
'deviceBrand' => $dd->getBrandName(),
'deviceModel' => $dd->getModel(),
'osCode' => $os['osCode'],
'osName' => $os['osName'],
'osVersion' => $os['osVersion'],
'clientType' => $client['clientType'],
'clientCode' => $client['clientCode'],
'clientName' => $client['clientName'],
'clientVersion' => $client['clientVersion'],
'clientEngine' => $client['clientEngine'],
'clientEngineVersion' => $client['clientEngineVersion'],
'deviceName' => $device['deviceName'],
'deviceBrand' => $device['deviceBrand'],
'deviceModel' => $device['deviceModel']
]);
$record = $geodb->get($log['ip']);
@ -1957,26 +1939,31 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DOCUMENT)
->param('collectionId', null, new UID(), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).')
->param('documentId', null, new UID(), 'Document unique ID.')
->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection with validation rules using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('documentId', null, new UID(), 'Document ID.')
->param('data', [], new JSON(), 'Document data as JSON object.')
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default inherits the existing read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')
->inject('audits')
->inject('usage')
->inject('events')
->inject('mode')
->action(function ($collectionId, $documentId, $data, $read, $write, $response, $dbForInternal, $dbForExternal, $audits, $usage, $mode) {
->action(function ($collectionId, $documentId, $data, $read, $write, $response, $dbForInternal, $dbForExternal, $audits, $usage, $events, $mode) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Utopia\Database\Database $dbForExternal */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
/** @var string $mode */
$collection = $dbForInternal->getDocument('collections', $collectionId);
/**
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
*/
$collection = Authorization::skip(fn() => $dbForInternal->getDocument('collections', $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled') && $mode !== APP_MODE_ADMIN )) {
throw new Exception('Collection not found', 404);
@ -1988,9 +1975,12 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
if (!$validator->isValid($collection->getWrite())) {
throw new Exception('Unauthorized permissions', 401);
}
$document = Authorization::skip(fn() => $dbForExternal->getDocument($collectionId, $documentId));
} else {
$document = $dbForExternal->getDocument($collectionId, $documentId);
}
$document = $dbForExternal->getDocument($collectionId, $documentId);
if ($document->isEmpty()) {
throw new Exception('Document not found', 404);
@ -2032,9 +2022,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
try {
if ($collection->getAttribute('permission') === 'collection') {
/** @var Document $document */
$document = Authorization::skip(function() use ($dbForExternal, $collection, $document, $data) {
return $dbForExternal->updateDocument($collection->getId(), $document->getId(), new Document($data));
});
$document = Authorization::skip(fn() => $dbForExternal->updateDocument($collection->getId(), $document->getId(), new Document($data)));
} else {
$document = $dbForExternal->updateDocument($collection->getId(), $document->getId(), new Document($data));
}
@ -2048,7 +2036,9 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
catch (StructureException $exception) {
throw new Exception($exception->getMessage(), 400);
}
$events->setParam('collection', $collection->getArrayCopy());
$usage
->setParam('database.documents.update', 1)
->setParam('collectionId', $collectionId)
@ -2074,8 +2064,8 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
->label('sdk.description', '/docs/references/database/delete-document.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('collectionId', null, new UID(), 'Collection unique ID. You can create a new collection using the Database service [server integration](/docs/server/database#createCollection).')
->param('documentId', null, new UID(), 'Document unique ID.')
->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
->param('documentId', null, new UID(), 'Document ID.')
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')
@ -2091,7 +2081,10 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
/** @var Appwrite\Stats\Stats $usage */
/** @var string $mode */
$collection = $dbForInternal->getDocument('collections', $collectionId);
/**
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
*/
$collection = Authorization::skip(fn() => $dbForInternal->getDocument('collections', $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled') && $mode !== APP_MODE_ADMIN )) {
throw new Exception('Collection not found', 404);
@ -2107,9 +2100,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
if ($collection->getAttribute('permission') === 'collection') {
/** @var Document $document */
$document = Authorization::skip(function() use ($dbForExternal, $collectionId, $documentId) {
return $dbForExternal->getDocument($collectionId, $documentId);
});
$document = Authorization::skip(fn() => $dbForExternal->getDocument($collectionId, $documentId));
} else {
$document = $dbForExternal->getDocument($collectionId, $documentId);
}
@ -2118,7 +2109,12 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
throw new Exception('No document found', 404);
}
$dbForExternal->deleteDocument($collectionId, $documentId);
if ($collection->getAttribute('permission') === 'collection') {
Authorization::skip(fn() => $dbForExternal->deleteDocument($collectionId, $documentId));
} else {
$dbForExternal->deleteDocument($collectionId, $documentId);
}
$dbForExternal->deleteCachedDocument($collectionId, $documentId);
$usage
@ -2128,6 +2124,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
$events
->setParam('eventData', $response->output($document, Response::MODEL_DOCUMENT))
->setParam('collection', $collection->getArrayCopy());
;
$audits

View file

@ -39,11 +39,11 @@ App::post('/v1/functions')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION)
->param('functionId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('functionId', '', new CustomId(), 'Function ID. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('runtime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution runtime.')
->param('vars', [], new Assoc(), 'Key-value JSON object.', true)
->param('vars', [], new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
->param('events', [], new ArrayList(new WhiteList(array_keys(Config::getParam('events')), true)), 'Events list.', true)
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
->param('timeout', 15, new Range(1, 900), 'Function maximum execution time in seconds.', true)
@ -88,9 +88,9 @@ App::get('/v1/functions')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION_LIST)
->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 function used as the starting point for the query, excluding the function itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('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.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
@ -158,7 +158,7 @@ App::get('/v1/functions/:functionId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('functionId', '', new UID(), 'Function ID.')
->inject('response')
->inject('dbForInternal')
->action(function ($functionId, $response, $dbForInternal) {
@ -184,7 +184,7 @@ App::get('/v1/functions/:functionId/usage')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USAGE_FUNCTIONS)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('functionId', '', new UID(), 'Function ID.')
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d']), 'Date range.', true)
->inject('response')
->inject('project')
@ -290,13 +290,13 @@ App::put('/v1/functions/:functionId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('functionId', '', new UID(), 'Function ID.')
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('vars', [], new Assoc(), 'Key-value JSON object.', true)
->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('vars', [], new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
->param('events', [], new ArrayList(new WhiteList(array_keys(Config::getParam('events')), true)), 'Events list.', true)
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
->param('timeout', 15, new Range(1, 900), 'Function maximum execution time in seconds.', true)
->param('timeout', 15, new Range(1, 900), 'Maximum execution time in seconds.', true)
->inject('response')
->inject('dbForInternal')
->inject('project')
@ -352,8 +352,8 @@ App::patch('/v1/functions/:functionId/tag')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('tag', '', new UID(), 'Tag unique ID.')
->param('functionId', '', new UID(), 'Function ID.')
->param('tag', '', new UID(), 'Tag ID.')
->inject('response')
->inject('dbForInternal')
->inject('project')
@ -406,7 +406,7 @@ App::delete('/v1/functions/:functionId')
->label('sdk.description', '/docs/references/functions/delete-function.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('functionId', '', new UID(), 'Function ID.')
->inject('response')
->inject('dbForInternal')
->inject('deletes')
@ -447,7 +447,7 @@ App::post('/v1/functions/:functionId/tags')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TAG)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('functionId', '', new UID(), 'Function ID.')
->param('command', '', new Text('1028'), 'Code execution command.')
->param('code', [], new File(), 'Gzip file with your code package. When used with the Appwrite CLI, pass the path to your code directory, and the CLI will automatically package your code. Use a path that is within the current directory.', false)
->inject('request')
@ -533,11 +533,11 @@ App::get('/v1/functions/:functionId/tags')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TAG_LIST)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('functionId', '', new UID(), 'Function ID.')
->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 tag used as the starting point for the query, excluding the tag itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('limit', 25, new Range(0, 100), 'Maximum number of tags 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 tag used as the starting point for the query, excluding the tag itself. Should be used for efficient pagination when working with large sets of data. [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.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
@ -588,8 +588,8 @@ App::get('/v1/functions/:functionId/tags/:tagId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TAG)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('tagId', '', new UID(), 'Tag unique ID.')
->param('functionId', '', new UID(), 'Function ID.')
->param('tagId', '', new UID(), 'Tag ID.')
->inject('response')
->inject('dbForInternal')
->action(function ($functionId, $tagId, $response, $dbForInternal) {
@ -626,8 +626,8 @@ App::delete('/v1/functions/:functionId/tags/:tagId')
->label('sdk.description', '/docs/references/functions/delete-tag.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('tagId', '', new UID(), 'Tag unique ID.')
->param('functionId', '', new UID(), 'Function ID.')
->param('tagId', '', new UID(), 'Tag ID.')
->inject('response')
->inject('dbForInternal')
->inject('usage')
@ -687,7 +687,7 @@ App::post('/v1/functions/:functionId/executions')
->label('sdk.response.model', Response::MODEL_EXECUTION)
->label('abuse-limit', 60)
->label('abuse-time', 60)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('functionId', '', new UID(), 'Function ID.')
->param('data', '', new Text(8192), 'String of custom data to send to function.', true)
// ->param('async', 1, new Range(0, 1), 'Execute code asynchronously. Pass 1 for true, 0 for false. Default value is 1.', true)
->inject('response')
@ -795,11 +795,11 @@ App::get('/v1/functions/:functionId/executions')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_EXECUTION_LIST)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->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('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.', 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.', true)
->inject('response')
->inject('dbForInternal')
@ -807,9 +807,7 @@ App::get('/v1/functions/:functionId/executions')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
$function = Authorization::skip(function() use ($dbForInternal, $functionId) {
return $dbForInternal->getDocument('functions', $functionId);
});
$function = Authorization::skip(fn() => $dbForInternal->getDocument('functions', $functionId));
if ($function->isEmpty()) {
throw new Exception('Function not found', 404);
@ -851,8 +849,8 @@ App::get('/v1/functions/:functionId/executions/:executionId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_EXECUTION)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('executionId', '', new UID(), 'Execution unique ID.')
->param('functionId', '', new UID(), 'Function ID.')
->param('executionId', '', new UID(), 'Execution ID.')
->inject('response')
->inject('dbForInternal')
->action(function ($functionId, $executionId, $response, $dbForInternal) {

View file

@ -162,8 +162,8 @@ App::get('/v1/projects')
->label('sdk.response.model', Response::MODEL_PROJECT_LIST)
->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 project used as the starting point for the query, excluding the project itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Results offset. The default value is 0. Use this param to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursor', '', new UID(), 'ID of the project used as the starting point for the query, excluding the project itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')

View file

@ -41,10 +41,10 @@ App::post('/v1/storage/files')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE)
->param('fileId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('fileId', '', new CustomId(), 'File ID. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('file', [], new File(), 'Binary file.', false)
->param('read', null, new ArrayList(new Text(64)), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
->param('write', null, new ArrayList(new Text(64)), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
->param('read', null, new ArrayList(new Text(64)), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
->param('write', null, new ArrayList(new Text(64)), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true)
->inject('request')
->inject('response')
->inject('dbForInternal')
@ -137,6 +137,7 @@ App::post('/v1/storage/files')
$data = $compressor->compress($data);
$key = App::getEnv('_APP_OPENSSL_KEY_V1');
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$tag = null;
$data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag);
if (!$device->write($path, $data, $mimeType)) {
@ -162,7 +163,7 @@ App::post('/v1/storage/files')
'comment' => '',
'openSSLVersion' => '1',
'openSSLCipher' => OpenSSL::CIPHER_AES_128_GCM,
'openSSLTag' => \bin2hex($tag),
'openSSLTag' => \bin2hex($tag ?? ''),
'openSSLIV' => \bin2hex($iv),
'search' => implode(' ', [$fileId, $file['name'] ?? '',]),
]));
@ -195,9 +196,9 @@ App::get('/v1/storage/files')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE_LIST)
->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 file used as the starting point for the query, excluding the file itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('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.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
@ -244,7 +245,7 @@ App::get('/v1/storage/files/:fileId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE)
->param('fileId', '', new UID(), 'File unique ID.')
->param('fileId', '', new UID(), 'File ID.')
->inject('response')
->inject('dbForInternal')
->inject('usage')
@ -276,7 +277,7 @@ App::get('/v1/storage/files/:fileId/preview')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE)
->label('sdk.methodType', 'location')
->param('fileId', '', new UID(), 'File unique ID')
->param('fileId', '', new UID(), 'File ID.')
->param('width', 0, new Range(0, 4000), 'Resize preview image width, Pass an integer between 0 to 4000.', true)
->param('height', 0, new Range(0, 4000), 'Resize preview image height, Pass an integer between 0 to 4000.', true)
->param('gravity', Image::GRAVITY_CENTER, new WhiteList(Image::getGravityTypes()), 'Image crop gravity. Can be one of ' . implode(",", Image::getGravityTypes()), true)
@ -437,7 +438,7 @@ App::get('/v1/storage/files/:fileId/download')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', '*/*')
->label('sdk.methodType', 'location')
->param('fileId', '', new UID(), 'File unique ID.')
->param('fileId', '', new UID(), 'File ID.')
->inject('response')
->inject('dbForInternal')
->inject('usage')
@ -502,7 +503,7 @@ App::get('/v1/storage/files/:fileId/view')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', '*/*')
->label('sdk.methodType', 'location')
->param('fileId', '', new UID(), 'File unique ID.')
->param('fileId', '', new UID(), 'File ID.')
->inject('response')
->inject('dbForInternal')
->inject('usage')
@ -578,9 +579,9 @@ App::put('/v1/storage/files/:fileId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE)
->param('fileId', '', new UID(), 'File unique ID.')
->param('read', [], new ArrayList(new Text(64)), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('write', [], new ArrayList(new Text(64)), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('fileId', '', new UID(), 'File ID.')
->param('read', [], new ArrayList(new Text(64)), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('write', [], new ArrayList(new Text(64)), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->inject('response')
->inject('dbForInternal')
->inject('user')
@ -647,7 +648,7 @@ App::delete('/v1/storage/files/:fileId')
->label('sdk.description', '/docs/references/storage/delete-file.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('fileId', '', new UID(), 'File unique ID.')
->param('fileId', '', new UID(), 'File ID.')
->inject('response')
->inject('dbForInternal')
->inject('events')
@ -793,7 +794,7 @@ App::get('/v1/storage/:bucketId/usage')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USAGE_BUCKETS)
->param('bucketId', '', new UID(), 'Bucket unique ID.')
->param('bucketId', '', new UID(), 'Bucket ID.')
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
->inject('response')
->inject('dbForInternal')

View file

@ -34,7 +34,7 @@ App::post('/v1/teams')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEAM)
->param('teamId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('teamId', '', new CustomId(), 'Team ID. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('name', null, new Text(128), 'Team name. Max length: 128 chars.')
->param('roles', ['owner'], new ArrayList(new Key()), 'Array of strings. Use this param to set the roles in the team for the user who created it. The default role is **owner**. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Max length for each role is 32 chars.', true)
->inject('response')
@ -105,9 +105,9 @@ App::get('/v1/teams')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEAM_LIST)
->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 team used as the starting point for the query, excluding the team itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('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.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
@ -150,7 +150,7 @@ App::get('/v1/teams/:teamId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEAM)
->param('teamId', '', new UID(), 'Team unique ID.')
->param('teamId', '', new UID(), 'Team ID.')
->inject('response')
->inject('dbForInternal')
->action(function ($teamId, $response, $dbForInternal) {
@ -178,7 +178,7 @@ App::put('/v1/teams/:teamId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEAM)
->param('teamId', '', new UID(), 'Team unique ID.')
->param('teamId', '', new UID(), 'Team ID.')
->param('name', null, new Text(128), 'Team name. Max length: 128 chars.')
->inject('response')
->inject('dbForInternal')
@ -211,7 +211,7 @@ App::delete('/v1/teams/:teamId')
->label('sdk.description', '/docs/references/teams/delete-team.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('teamId', '', new UID(), 'Team unique ID.')
->param('teamId', '', new UID(), 'Team ID.')
->inject('response')
->inject('dbForInternal')
->inject('events')
@ -269,11 +269,11 @@ App::post('/v1/teams/:teamId/memberships')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
->label('abuse-limit', 10)
->param('teamId', '', new UID(), 'Team unique ID.')
->param('email', '', new Email(), 'New team member email.')
->param('teamId', '', new UID(), 'Team ID.')
->param('email', '', new Email(), 'Team member email.')
->param('roles', [], new ArrayList(new Key()), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Max length for each role is 32 chars.')
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add our own built-in confirm page
->param('name', '', new Text(128), 'New team member name. Max length: 128 chars.', true)
->param('name', '', new Text(128), 'Team member name. Max length: 128 chars.', true)
->inject('response')
->inject('project')
->inject('user')
@ -441,11 +441,11 @@ App::get('/v1/teams/:teamId/memberships')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MEMBERSHIP_LIST)
->param('teamId', '', new UID(), 'Team unique ID.')
->param('teamId', '', new UID(), 'Team ID.')
->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 membership used as the starting point for the query, excluding the membership itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('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.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
@ -499,8 +499,8 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MEMBERSHIP_LIST)
->param('teamId', '', new UID(), 'Team unique ID.')
->param('membershipId', '', new UID(), 'membership unique ID.')
->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.')
->inject('response')
->inject('dbForInternal')
->action(function ($teamId, $membershipId, $response, $dbForInternal) {
@ -536,9 +536,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
->param('teamId', '', new UID(), 'Team unique ID.')
->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.')
->param('roles', [], new ArrayList(new Key()), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Max length for each role is 32 chars.')
->param('roles', [], new ArrayList(new Key()), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Max length for each role is 32 chars.')
->inject('request')
->inject('response')
->inject('user')
@ -601,9 +601,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
->param('teamId', '', new UID(), 'Team unique ID.')
->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.')
->param('userId', '', new UID(), 'User unique ID.')
->param('userId', '', new UID(), 'User ID.')
->param('secret', '', new Text(256), 'Secret key.')
->inject('request')
->inject('response')
@ -739,7 +739,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
->label('sdk.description', '/docs/references/teams/delete-team-membership.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('teamId', '', new UID(), 'Team unique ID.')
->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.')
->inject('response')
->inject('dbForInternal')

View file

@ -15,7 +15,7 @@ use Utopia\Audit\Audit;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Validator\UID;
use DeviceDetector\DeviceDetector;
use Appwrite\Detector\Detector;
use Appwrite\Database\Validator\CustomId;
use Utopia\Config\Config;
use Utopia\Database\Database;
@ -34,7 +34,7 @@ App::post('/v1/users')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('userId', '', new CustomId(), 'User ID. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('email', '', new Email(), 'User email.')
->param('password', '', new Password(), 'User password. Must be at least 8 chars.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
@ -93,9 +93,9 @@ App::get('/v1/users')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER_LIST)
->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 user used as the starting point for the query, excluding the user itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('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.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
@ -143,7 +143,7 @@ App::get('/v1/users/:userId')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new UID(), 'User unique ID.')
->param('userId', '', new UID(), 'User ID.')
->inject('response')
->inject('dbForInternal')
->inject('usage')
@ -175,7 +175,7 @@ App::get('/v1/users/:userId/prefs')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PREFERENCES)
->param('userId', '', new UID(), 'User unique ID.')
->param('userId', '', new UID(), 'User ID.')
->inject('response')
->inject('dbForInternal')
->inject('usage')
@ -209,7 +209,7 @@ App::get('/v1/users/:userId/sessions')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_SESSION_LIST)
->param('userId', '', new UID(), 'User unique ID.')
->param('userId', '', new UID(), 'User ID.')
->inject('response')
->inject('dbForInternal')
->inject('locale')
@ -258,9 +258,9 @@ App::get('/v1/users/:userId/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('userId', '', new UID(), 'User unique ID.')
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->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)
->inject('response')
->inject('dbForInternal')
->inject('locale')
@ -306,42 +306,29 @@ App::get('/v1/users/:userId/logs')
foreach ($logs as $i => &$log) {
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
$dd = new DeviceDetector($log['userAgent']);
$detector = new Detector($log['userAgent']);
$detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
$dd->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
$dd->parse();
$os = $dd->getOs();
$osCode = (isset($os['short_name'])) ? $os['short_name'] : '';
$osName = (isset($os['name'])) ? $os['name'] : '';
$osVersion = (isset($os['version'])) ? $os['version'] : '';
$client = $dd->getClient();
$clientType = (isset($client['type'])) ? $client['type'] : '';
$clientCode = (isset($client['short_name'])) ? $client['short_name'] : '';
$clientName = (isset($client['name'])) ? $client['name'] : '';
$clientVersion = (isset($client['version'])) ? $client['version'] : '';
$clientEngine = (isset($client['engine'])) ? $client['engine'] : '';
$clientEngineVersion = (isset($client['engine_version'])) ? $client['engine_version'] : '';
$os = $detector->getOS();
$client = $detector->getClient();
$device = $detector->getDevice();
$output[$i] = new Document([
'event' => $log['event'],
'ip' => $log['ip'],
'time' => $log['time'],
'osCode' => $osCode,
'osName' => $osName,
'osVersion' => $osVersion,
'clientType' => $clientType,
'clientCode' => $clientCode,
'clientName' => $clientName,
'clientVersion' => $clientVersion,
'clientEngine' => $clientEngine,
'clientEngineVersion' => $clientEngineVersion,
'deviceName' => $dd->getDeviceName(),
'deviceBrand' => $dd->getBrandName(),
'deviceModel' => $dd->getModel(),
'osCode' => $os['osCode'],
'osName' => $os['osName'],
'osVersion' => $os['osVersion'],
'clientType' => $client['clientType'],
'clientCode' => $client['clientCode'],
'clientName' => $client['clientName'],
'clientVersion' => $client['clientVersion'],
'clientEngine' => $client['clientEngine'],
'clientEngineVersion' => $client['clientEngineVersion'],
'deviceName' => $device['deviceName'],
'deviceBrand' => $device['deviceBrand'],
'deviceModel' => $device['deviceModel']
]);
$record = $geodb->get($log['ip']);
@ -377,8 +364,8 @@ App::patch('/v1/users/:userId/status')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new UID(), 'User unique ID.')
->param('status', null, new Boolean(true), 'User Status. To activate the user pass `true` and to block the user pass `false`')
->param('userId', '', new UID(), 'User ID.')
->param('status', null, new Boolean(true), 'User Status. To activate the user pass `true` and to block the user pass `false`.')
->inject('response')
->inject('dbForInternal')
->inject('usage')
@ -413,8 +400,8 @@ App::patch('/v1/users/:userId/verification')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new UID(), 'User unique ID.')
->param('emailVerification', false, new Boolean(), 'User Email Verification Status.')
->param('userId', '', new UID(), 'User ID.')
->param('emailVerification', false, new Boolean(), 'User email verification status.')
->inject('response')
->inject('dbForInternal')
->inject('usage')
@ -449,7 +436,7 @@ App::patch('/v1/users/:userId/name')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new UID(), 'User unique ID.')
->param('userId', '', new UID(), 'User ID.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.')
->inject('response')
->inject('dbForInternal')
@ -488,8 +475,8 @@ App::patch('/v1/users/:userId/password')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new UID(), 'User unique ID.')
->param('password', '', new Password(), 'New user password. Must be between 6 to 32 chars.')
->param('userId', '', new UID(), 'User ID.')
->param('password', '', new Password(), 'New user password. Must be at least 8 chars.')
->inject('response')
->inject('dbForInternal')
->inject('audits')
@ -531,7 +518,7 @@ App::patch('/v1/users/:userId/email')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new UID(), 'User unique ID.')
->param('userId', '', new UID(), 'User ID.')
->param('email', '', new Email(), 'User email.')
->inject('response')
->inject('dbForInternal')
@ -581,7 +568,7 @@ App::patch('/v1/users/:userId/prefs')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PREFERENCES)
->param('userId', '', new UID(), 'User unique ID.')
->param('userId', '', new UID(), 'User ID.')
->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.')
->inject('response')
->inject('dbForInternal')
@ -616,8 +603,8 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
->label('sdk.description', '/docs/references/users/delete-user-session.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('userId', '', new UID(), 'User unique ID.')
->param('sessionId', null, new UID(), 'User unique session ID.')
->param('userId', '', new UID(), 'User ID.')
->param('sessionId', null, new UID(), 'Session ID.')
->inject('response')
->inject('dbForInternal')
->inject('events')
@ -672,7 +659,7 @@ App::delete('/v1/users/:userId/sessions')
->label('sdk.description', '/docs/references/users/delete-user-sessions.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('userId', '', new UID(), 'User unique ID.')
->param('userId', '', new UID(), 'User ID.')
->inject('response')
->inject('dbForInternal')
->inject('events')
@ -719,7 +706,7 @@ App::delete('/v1/users/:userId')
->label('sdk.description', '/docs/references/users/delete.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('userId', '', function () {return new UID();}, 'User unique ID.')
->param('userId', '', function () {return new UID();}, 'User ID.')
->inject('response')
->inject('dbForInternal')
->inject('events')

View file

@ -207,7 +207,14 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
if ($project->getId() !== 'console') {
$payload = new Document($response->getPayload());
$target = Realtime::fromPayload($events->getParam('event'), $payload, $project);
$collection = new Document($events->getParam('collection') ?? []);
$target = Realtime::fromPayload(
event: $events->getParam('event'),
payload: $payload,
project: $project,
collection: $collection
);
Realtime::send(
$target['projectId'] ?? $project->getId(),

View file

@ -73,17 +73,8 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
}
} while ($attempts < $max);
App::setResource('db', function () use (&$db) {
return $db;
});
App::setResource('cache', function () use (&$redis) {
return $redis;
});
App::setResource('app', function() use (&$app) {
return $app;
});
App::setResource('db', fn() => $db);
App::setResource('cache', fn() => $redis);
$dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */
@ -170,14 +161,9 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
$db = $register->get('dbPool')->get();
$redis = $register->get('redisPool')->get();
App::setResource('db', function () use (&$db) {
return $db;
});
App::setResource('db', fn() => $db);
App::setResource('cache', fn() => $redis);
App::setResource('cache', function () use (&$redis) {
return $redis;
});
try {
Authorization::cleanRoles();
Authorization::setRole('role:all');

View file

@ -286,8 +286,8 @@ Database::addFilter('encrypt',
return json_encode([
'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
'method' => OpenSSL::CIPHER_AES_128_GCM,
'iv' => bin2hex($iv),
'tag' => bin2hex($tag),
'iv' => \bin2hex($iv),
'tag' => \bin2hex($tag ?? ''),
'version' => '1',
]);
},
@ -543,9 +543,7 @@ Locale::setLanguageFromJSON('zh-tw', __DIR__.'/config/locale/translations/zh-tw.
// Runtime Execution
App::setResource('register', function() use ($register) {
return $register;
});
App::setResource('register', fn() => $register);
App::setResource('layout', function($locale) {
$layout = new View(__DIR__.'/views/layouts/default.phtml');

View file

@ -87,9 +87,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
'timestamp' => time(),
'value' => '{}'
]);
$statsDocument = Authorization::skip(function () use ($database, $document) {
return $database->createDocument('realtime', $document);
});
$statsDocument = Authorization::skip(fn() => $database->createDocument('realtime', $document));
} catch (\Throwable $th) {
Console::error('[Error] Type: ' . get_class($th));
Console::error('[Error] Message: ' . $th->getMessage());
@ -106,12 +104,8 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
Timer::tick(5000, function () use ($register, $stats, $containerId, &$statsDocument) {
/** @var Document $statsDocument */
foreach ($stats as $projectId => $value) {
if (empty($value['connections']) && empty($value['messages'])) {
continue;
}
$connections = $stats->get($projectId, 'connections');
$messages = $stats->get($projectId, 'messages');
$connections = $stats->get($projectId, 'connections') ?? 0;
$messages = $stats->get($projectId, 'messages' ?? 0);
$usage = new Event('v1-usage', 'UsageV1');
$usage
@ -132,9 +126,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
}
$payload = [];
foreach ($stats as $projectId => $value) {
if (!empty($value['connectionsTotal'])) {
$payload[$projectId] = $stats->get($projectId, 'connectionsTotal');
}
$payload[$projectId] = $stats->get($projectId, 'connectionsTotal');
}
if (empty($payload) || empty($statsDocument)) {
return;
@ -147,9 +139,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
->setAttribute('timestamp', time())
->setAttribute('value', json_encode($payload));
Authorization::skip(function () use ($database, $statsDocument) {
$database->updateDocument('realtime', $statsDocument->getId(), $statsDocument);
});
Authorization::skip(fn() => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument));
} catch (\Throwable $th) {
Console::error('[Error] Type: ' . get_class($th));
Console::error('[Error] Message: ' . $th->getMessage());
@ -177,11 +167,9 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
$payload = [];
$list = Authorization::skip(function () use ($database) {
return $database->find('realtime', [
$list = Authorization::skip(fn() => $database->find('realtime', [
new Query('timestamp', Query::TYPE_GREATER, [(time() - 15)])
]);
});
]));
/**
* Aggregate stats across containers.
@ -335,21 +323,10 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
Console::info("Connection open (user: {$connection})");
App::setResource('db', function () use (&$db) {
return $db;
});
App::setResource('cache', function () use (&$redis) {
return $redis;
});
App::setResource('request', function () use ($request) {
return $request;
});
App::setResource('response', function () use ($response) {
return $response;
});
App::setResource('db', fn() => $db);
App::setResource('cache', fn() => $redis);
App::setResource('request', fn() => $request);
App::setResource('response', fn() => $response);
try {
/** @var \Utopia\Database\Document $user */
@ -493,7 +470,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
}
switch ($message['type']) {
/**
/**
* This type is used to authenticate.
*/
case 'authentication':

View file

@ -347,7 +347,7 @@ $logs = $this->getParam('logs', null);
<button class="new-index">Add Index</button>
</li>
<li data-state="/console/database/collection/activity?id={{router.params.id}}&project={{router.params.project}}">
<h2>Activity <span class="badge" data-ls-bind="{{logs.sum}}"></span></h2>
<h2>Activity</h2>
<?php echo $logs->render(); ?>
</li>
@ -470,8 +470,8 @@ $logs = $this->getParam('logs', null);
<div class="col span-1"><input name="permission" value="document" type="radio" class="margin-top-no" data-ls-bind="{{project-collection.permission}}" /></div>
<div class="col span-11">
<b>Document Level</b>
<p class="text-fade margin-top-tiny">With Document Level permissions, you have granular access control over every file. Users will only be able to access documents for which they have explicit permissions.</p>
<p class="text-fade margin-top-tiny">In this permission level, document permissions take precedence and bucket permissions are ignored.</p>
<p class="text-fade margin-top-tiny">With Document Level permissions, you have granular access control over every document. Users will only be able to access documents for which they have explicit permissions.</p>
<p class="text-fade margin-top-tiny">In this permission level, document permissions take precedence and collection permissions are ignored.</p>
</div>
</div>
@ -480,7 +480,7 @@ $logs = $this->getParam('logs', null);
<div class="col span-11">
<b>Collection Level</b>
<p class="text-fade margin-top-tiny">With Collection Level permissions, you assign permissions only once in the collection.</p>
<p class="text-fade margin-top-tiny">In this permission level, permissions assigned to collection takes the precedence and documents permissions are ignored</p>
<p class="text-fade margin-top-tiny">In this permission level, permissions assigned to collection takes the precedence and documents permissions are ignored.</p>
<div data-ls-if="{{project-collection.permission}} === 'collection'">
<label for="collection-read">Read Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</span></label>
<input type="hidden" id="collection-read" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{project-collection.$read}}" placeholder="User ID, Team ID or Role" />
@ -592,14 +592,6 @@ $logs = $this->getParam('logs', null);
(() => {
const form = document.getElementById("add-string-attribute");
const fields = {
required: {
array: ["disable", "uncheck"],
required: ["enable"]
},
array: {
required: ["disable", "uncheck"],
array: ["enable"]
},
xdefault: {
oneOf: {
if: ["array", "required"],
@ -683,14 +675,6 @@ $logs = $this->getParam('logs', null);
(() => {
const form = document.getElementById("add-integer-attribute");
const fields = {
required: {
array: ["disable", "uncheck"],
required: ["enable"]
},
array: {
required: ["disable", "uncheck"],
array: ["enable"]
},
xdefault: {
oneOf: {
if: ["array", "required"],
@ -772,14 +756,6 @@ $logs = $this->getParam('logs', null);
(() => {
const form = document.getElementById("add-float-attribute");
const fields = {
required: {
array: ["disable", "uncheck"],
required: ["enable"]
},
array: {
required: ["disable", "uncheck"],
array: ["enable"]
},
xdefault: {
oneOf: {
if: ["array", "required"],
@ -850,14 +826,6 @@ $logs = $this->getParam('logs', null);
(() => {
const form = document.getElementById("add-email-attribute");
const fields = {
required: {
array: ["disable", "uncheck"],
required: ["enable"]
},
array: {
required: ["disable", "uncheck"],
array: ["enable"]
},
xdefault: {
oneOf: {
if: ["array", "required"],
@ -930,14 +898,6 @@ $logs = $this->getParam('logs', null);
(() => {
const form = document.getElementById("add-boolean-attribute");
const fields = {
required: {
array: ["disable", "uncheck"],
required: ["enable"]
},
array: {
required: ["disable", "uncheck"],
array: ["enable"]
},
xdefault: {
oneOf: {
if: ["array", "required"],
@ -1009,14 +969,6 @@ $logs = $this->getParam('logs', null);
(() => {
const form = document.getElementById("add-ip-attribute");
const fields = {
required: {
array: ["disable", "uncheck"],
required: ["enable"]
},
array: {
required: ["disable", "uncheck"],
array: ["enable"]
},
xdefault: {
oneOf: {
if: ["array", "required"],
@ -1077,7 +1029,7 @@ $logs = $this->getParam('logs', null);
</div>
<label for="string-default">Default Value</label>
<input id="string-default" name="xdefault" type="url" pattern="https://.*" title="Valid URL address" class="margin-bottom-large" autocomplete="off">
<input id="string-default" name="xdefault" type="url" title="Valid URL address" class="margin-bottom-large" autocomplete="off">
<footer>
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
@ -1087,14 +1039,6 @@ $logs = $this->getParam('logs', null);
(() => {
const form = document.getElementById("add-url-attribute");
const fields = {
required: {
array: ["disable", "uncheck"],
required: ["enable"]
},
array: {
required: ["disable", "uncheck"],
array: ["enable"]
},
xdefault: {
oneOf: {
if: ["array", "required"],
@ -1182,14 +1126,6 @@ $logs = $this->getParam('logs', null);
(() => {
const form = document.getElementById("add-enum-attribute");
const fields = {
required: {
array: ["disable", "uncheck"],
required: ["enable"]
},
array: {
required: ["disable", "uncheck"],
array: ["enable"]
},
xdefault: {
oneOf: {
if: ["array", "required"],

View file

@ -280,15 +280,6 @@ $logs = $this->getParam('logs', null);
<button type="button" class="margin-end margin-bottom-small reverse" @click="doc = addAttribute(doc, attr.key)"> Add Attribute </button>
</div>
</template>
<script type="text/javascript">
function addAttribute(doc, key) {
if (!Array.isArray(doc[key])) {
doc[key] = [];
}
doc[key].push(null);
return doc;
}
</script>
</li>
</template>
</ul>

View file

@ -444,7 +444,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
<div class="zone xl"
data-service="teams.getMemberships"
data-scope="console"
data-event="load,teams.createMembership,teams.deleteMembership"
data-event="load,teams.createMembership,teams.deleteMembership,teams.createMembership.resent"
data-name="members"
data-param-team-id="{{console-project.teamId}}"
data-success="trigger"

View file

@ -323,7 +323,9 @@
<button class="danger">Logout</button>
</form>
<div class="pull-start margin-end avatar-container">
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.clientCode|lowercase}}?width=120&height=120&project={{env.PROJECT}},title={{session.clientName}},alt={{session.clientName}}" class="avatar" loading="lazy" width="60" height="60" />
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{session.clientCode|lowercase}} !== 'cli'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{session.clientCode|lowercase}}?width=120&height=120&project={{env.PROJECT}},title={{session.clientName}},alt={{session.clientName}}" class="avatar" loading="lazy" width="60" height="60" />
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{session.clientCode|lowercase}} === 'cli'" data-ls-attrs="src=/images/clients/terminal.png?buster=<?php echo APP_CACHE_BUSTER; ?>,title={{session.clientName}},alt={{session.clientName}}" class="avatar" loading="lazy" width="60" height="60" />
<div data-ls-if="{{session.provider}} !== 'email'" class="corner">
<img data-ls-attrs="src=/images/users/{{session.provider}}.png?buster=<?php echo APP_CACHE_BUSTER; ?>,title={{session.provider}},alt={{session.provider}}" class="avatar xs" loading="lazy" width="30" height="30" />
@ -390,7 +392,10 @@
<td data-title="Date: "><span data-ls-bind="{{log.time|dateTime}}"></span></td>
<td data-title="Event: "><span data-ls-bind="{{log.event}}"></span></td>
<td data-title="Client: ">
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="({{log.clientCode}})" data-ls-attrs="src={{env.API}}/avatars/browsers/{{log.clientCode|lowercase}}?width=80&height=80&project={{env.PROJECT}},title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{log.clientCode|lowercase}} !== 'cli'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{log.clientCode|lowercase}}?width=80&height=80&project={{env.PROJECT}},title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{log.clientCode|lowercase}} === 'cli'" data-ls-attrs="src=/images/clients/terminal.png?buster=<?php echo APP_CACHE_BUSTER; ?>,,title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
<span data-ls-if="({{log.clientName}})" data-ls-bind="{{log.clientName}} {{log.clientVersion}} on {{log.model}} {{log.osName}} {{log.osVersion}}"></span>
<div data-ls-if="(!{{log.clientName}})" class="text-align-center text-fade">Unknown</div>
</td>

View file

@ -103,6 +103,8 @@ class DeletesV1 extends Worker
$dbForInternal = $this->getInternalDB($projectId);
$dbForExternal = $this->getExternalDB($projectId);
$dbForExternal->deleteCollection($collectionId);
$this->deleteByGroup('attributes', [
new Query('collectionId', Query::TYPE_EQUAL, [$collectionId])
], $dbForInternal);
@ -110,8 +112,6 @@ class DeletesV1 extends Worker
$this->deleteByGroup('indexes', [
new Query('collectionId', Query::TYPE_EQUAL, [$collectionId])
], $dbForInternal);
$dbForExternal->deleteCollection($collectionId);
}
/**

View file

@ -481,16 +481,14 @@ class FunctionsV1 extends Worker
Console::info('Function executed in ' . ($executionEnd - $executionStart) . ' seconds, status: ' . $functionStatus);
$execution = Authorization::skip(function() use ($database, $execution, $tag, $functionStatus, $exitCode, $stdout, $stderr, $executionTime) {
return $database->updateDocument('executions', $execution->getId(), new Document(array_merge($execution->getArrayCopy(), [
$execution = Authorization::skip(fn() => $database->updateDocument('executions', $execution->getId(), new Document(array_merge($execution->getArrayCopy(), [
'tagId' => $tag->getId(),
'status' => $functionStatus,
'exitCode' => $exitCode,
'stdout' => \utf8_encode(\mb_substr($stdout, -8000)), // log last 8000 chars output
'stderr' => \utf8_encode(\mb_substr($stderr, -8000)), // log last 8000 chars output
'time' => (float)$executionTime,
])));
});
]))));
$executionModel = new Execution();
$executionUpdate = new Event('v1-webhooks', 'WebhooksV1');

View file

@ -57,21 +57,21 @@
"utopia-php/image": "0.5.*",
"resque/php-resque": "1.3.6",
"matomo/device-detector": "4.3.1",
"matomo/device-detector": "5.0.1",
"dragonmantank/cron-expression": "3.1.0",
"influxdb/influxdb-php": "1.15.2",
"phpmailer/phpmailer": "6.5.1",
"chillerlan/php-qrcode": "4.3.1",
"phpmailer/phpmailer": "6.5.3",
"chillerlan/php-qrcode": "4.3.2",
"adhocore/jwt": "1.1.2",
"slickdeals/statsd": "3.1.0"
},
"repositories": [],
"require-dev": {
"appwrite/sdk-generator": "0.16.2",
"phpunit/phpunit": "9.5.6",
"swoole/ide-helper": "4.6.7",
"textalk/websocket": "1.5.2",
"vimeo/psalm": "4.7.2"
"phpunit/phpunit": "9.5.10",
"swoole/ide-helper": "4.8.3",
"textalk/websocket": "1.5.5",
"vimeo/psalm": "4.13.1"
},
"provide": {
"ext-phpiredis": "*"

244
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "9dcf48d4173daea87c60b464b104cd22",
"content-hash": "d4017e5555cb2462f367b6881d5b6a5c",
"packages": [
{
"name": "adhocore/jwt",
@ -170,16 +170,16 @@
},
{
"name": "chillerlan/php-qrcode",
"version": "4.3.1",
"version": "4.3.2",
"source": {
"type": "git",
"url": "https://github.com/chillerlan/php-qrcode.git",
"reference": "be3beb936c21fe53a4e7e8f7f3582e9f02443666"
"reference": "b625396e0752d79747a55205ae7e191eeb459dcd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/be3beb936c21fe53a4e7e8f7f3582e9f02443666",
"reference": "be3beb936c21fe53a4e7e8f7f3582e9f02443666",
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/b625396e0752d79747a55205ae7e191eeb459dcd",
"reference": "b625396e0752d79747a55205ae7e191eeb459dcd",
"shasum": ""
},
"require": {
@ -188,7 +188,7 @@
"php": "^7.4 || ^8.0"
},
"require-dev": {
"phan/phan": "^3.2.2",
"phan/phan": "^5.3",
"phpunit/phpunit": "^9.5",
"setasign/fpdf": "^1.8.2"
},
@ -232,7 +232,7 @@
],
"support": {
"issues": "https://github.com/chillerlan/php-qrcode/issues",
"source": "https://github.com/chillerlan/php-qrcode/tree/4.3.1"
"source": "https://github.com/chillerlan/php-qrcode/tree/4.3.2"
},
"funding": [
{
@ -244,7 +244,7 @@
"type": "ko_fi"
}
],
"time": "2021-01-05T21:21:28+00:00"
"time": "2021-11-18T08:46:03+00:00"
},
{
"name": "chillerlan/php-settings-container",
@ -933,21 +933,21 @@
},
{
"name": "matomo/device-detector",
"version": "4.3.1",
"version": "5.0.1",
"source": {
"type": "git",
"url": "https://github.com/matomo-org/device-detector.git",
"reference": "88e5419ee1448ccb9537e287dd09836ff9d2de3b"
"reference": "ebd8a07e4b69088c0e34f29ec72dc162c34c9264"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/88e5419ee1448ccb9537e287dd09836ff9d2de3b",
"reference": "88e5419ee1448ccb9537e287dd09836ff9d2de3b",
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/ebd8a07e4b69088c0e34f29ec72dc162c34c9264",
"reference": "ebd8a07e4b69088c0e34f29ec72dc162c34c9264",
"shasum": ""
},
"require": {
"mustangostang/spyc": "*",
"php": ">=7.2"
"php": "^7.2|^8.0"
},
"replace": {
"piwik/device-detector": "self.version"
@ -998,7 +998,7 @@
"source": "https://github.com/matomo-org/matomo",
"wiki": "https://dev.matomo.org/"
},
"time": "2021-09-20T12:34:12+00:00"
"time": "2021-12-07T11:40:16+00:00"
},
{
"name": "mongodb/mongodb",
@ -1120,16 +1120,16 @@
},
{
"name": "phpmailer/phpmailer",
"version": "v6.5.1",
"version": "v6.5.3",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "dd803df5ad7492e1b40637f7ebd258fee5ca7355"
"reference": "baeb7cde6b60b1286912690ab0693c7789a31e71"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/dd803df5ad7492e1b40637f7ebd258fee5ca7355",
"reference": "dd803df5ad7492e1b40637f7ebd258fee5ca7355",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/baeb7cde6b60b1286912690ab0693c7789a31e71",
"reference": "baeb7cde6b60b1286912690ab0693c7789a31e71",
"shasum": ""
},
"require": {
@ -1186,7 +1186,7 @@
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"support": {
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.5.1"
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.5.3"
},
"funding": [
{
@ -1194,7 +1194,7 @@
"type": "github"
}
],
"time": "2021-08-18T09:14:16+00:00"
"time": "2021-11-25T16:34:11+00:00"
},
{
"name": "psr/http-client",
@ -4493,16 +4493,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.5.6",
"version": "9.5.10",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "fb9b8333f14e3dce976a60ef6a7e05c7c7ed8bfb"
"reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fb9b8333f14e3dce976a60ef6a7e05c7c7ed8bfb",
"reference": "fb9b8333f14e3dce976a60ef6a7e05c7c7ed8bfb",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
"reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a",
"shasum": ""
},
"require": {
@ -4514,11 +4514,11 @@
"ext-xml": "*",
"ext-xmlwriter": "*",
"myclabs/deep-copy": "^1.10.1",
"phar-io/manifest": "^2.0.1",
"phar-io/manifest": "^2.0.3",
"phar-io/version": "^3.0.2",
"php": ">=7.3",
"phpspec/prophecy": "^1.12.1",
"phpunit/php-code-coverage": "^9.2.3",
"phpunit/php-code-coverage": "^9.2.7",
"phpunit/php-file-iterator": "^3.0.5",
"phpunit/php-invoker": "^3.1.1",
"phpunit/php-text-template": "^2.0.3",
@ -4580,7 +4580,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.6"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10"
},
"funding": [
{
@ -4592,7 +4592,7 @@
"type": "github"
}
],
"time": "2021-06-23T05:14:38+00:00"
"time": "2021-09-25T07:38:51+00:00"
},
{
"name": "psr/container",
@ -5613,24 +5613,18 @@
},
{
"name": "swoole/ide-helper",
"version": "4.6.7",
"version": "4.8.3",
"source": {
"type": "git",
"url": "https://github.com/swoole/ide-helper.git",
"reference": "0d1409b8274117addfe64d3ea412812a69807411"
"reference": "3ac4971814273889933b871e03b2a6b340e58f79"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/swoole/ide-helper/zipball/0d1409b8274117addfe64d3ea412812a69807411",
"reference": "0d1409b8274117addfe64d3ea412812a69807411",
"url": "https://api.github.com/repos/swoole/ide-helper/zipball/3ac4971814273889933b871e03b2a6b340e58f79",
"reference": "3ac4971814273889933b871e03b2a6b340e58f79",
"shasum": ""
},
"require-dev": {
"guzzlehttp/guzzle": "~6.5.0",
"laminas/laminas-code": "~3.4.0",
"squizlabs/php_codesniffer": "~3.5.0",
"symfony/filesystem": "~4.0"
},
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -5645,7 +5639,7 @@
"description": "IDE help files for Swoole.",
"support": {
"issues": "https://github.com/swoole/ide-helper/issues",
"source": "https://github.com/swoole/ide-helper/tree/4.6.7"
"source": "https://github.com/swoole/ide-helper/tree/4.8.3"
},
"funding": [
{
@ -5655,56 +5649,48 @@
{
"url": "https://github.com/swoole",
"type": "github"
},
{
"url": "https://opencollective.com/swoole-src",
"type": "open_collective"
}
],
"time": "2021-05-14T16:05:16+00:00"
"time": "2021-12-01T08:11:40+00:00"
},
{
"name": "symfony/console",
"version": "v5.4.1",
"version": "v6.0.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "9130e1a0fc93cb0faadca4ee917171bd2ca9e5f4"
"reference": "fafd9802d386bf1c267e0249ddb7ceb14dcfdad4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/9130e1a0fc93cb0faadca4ee917171bd2ca9e5f4",
"reference": "9130e1a0fc93cb0faadca4ee917171bd2ca9e5f4",
"url": "https://api.github.com/repos/symfony/console/zipball/fafd9802d386bf1c267e0249ddb7ceb14dcfdad4",
"reference": "fafd9802d386bf1c267e0249ddb7ceb14dcfdad4",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/deprecation-contracts": "^2.1|^3",
"php": ">=8.0.2",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php73": "^1.9",
"symfony/polyfill-php80": "^1.16",
"symfony/service-contracts": "^1.1|^2|^3",
"symfony/string": "^5.1|^6.0"
"symfony/string": "^5.4|^6.0"
},
"conflict": {
"psr/log": ">=3",
"symfony/dependency-injection": "<4.4",
"symfony/dotenv": "<5.1",
"symfony/event-dispatcher": "<4.4",
"symfony/lock": "<4.4",
"symfony/process": "<4.4"
"symfony/dependency-injection": "<5.4",
"symfony/dotenv": "<5.4",
"symfony/event-dispatcher": "<5.4",
"symfony/lock": "<5.4",
"symfony/process": "<5.4"
},
"provide": {
"psr/log-implementation": "1.0|2.0"
"psr/log-implementation": "1.0|2.0|3.0"
},
"require-dev": {
"psr/log": "^1|^2",
"symfony/config": "^4.4|^5.0|^6.0",
"symfony/dependency-injection": "^4.4|^5.0|^6.0",
"symfony/event-dispatcher": "^4.4|^5.0|^6.0",
"symfony/lock": "^4.4|^5.0|^6.0",
"symfony/process": "^4.4|^5.0|^6.0",
"symfony/var-dumper": "^4.4|^5.0|^6.0"
"psr/log": "^1|^2|^3",
"symfony/config": "^5.4|^6.0",
"symfony/dependency-injection": "^5.4|^6.0",
"symfony/event-dispatcher": "^5.4|^6.0",
"symfony/lock": "^5.4|^6.0",
"symfony/process": "^5.4|^6.0",
"symfony/var-dumper": "^5.4|^6.0"
},
"suggest": {
"psr/log": "For using the console logger",
@ -5744,7 +5730,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v5.4.1"
"source": "https://github.com/symfony/console/tree/v6.0.1"
},
"funding": [
{
@ -5760,7 +5746,7 @@
"type": "tidelift"
}
],
"time": "2021-12-09T11:22:43+00:00"
"time": "2021-12-09T12:47:37+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
@ -6007,85 +5993,6 @@
],
"time": "2021-05-27T12:26:48+00:00"
},
{
"name": "symfony/polyfill-php73",
"version": "v1.23.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php73.git",
"reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010",
"reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php73\\": ""
},
"files": [
"bootstrap.php"
],
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2021-02-19T12:13:01+00:00"
},
{
"name": "symfony/service-contracts",
"version": "v3.0.0",
@ -6255,21 +6162,21 @@
},
{
"name": "textalk/websocket",
"version": "1.5.2",
"version": "1.5.5",
"source": {
"type": "git",
"url": "https://github.com/Textalk/websocket-php.git",
"reference": "b93249453806a2dd46495de46d76fcbcb0d8dee8"
"reference": "846542f82658132cd36acb7a7e8ce0f03960c295"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Textalk/websocket-php/zipball/b93249453806a2dd46495de46d76fcbcb0d8dee8",
"reference": "b93249453806a2dd46495de46d76fcbcb0d8dee8",
"url": "https://api.github.com/repos/Textalk/websocket-php/zipball/846542f82658132cd36acb7a7e8ce0f03960c295",
"reference": "846542f82658132cd36acb7a7e8ce0f03960c295",
"shasum": ""
},
"require": {
"php": "^7.2 | ^8.0",
"psr/log": "^1.0"
"psr/log": "^1 | ^2 | ^3"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.0",
@ -6298,9 +6205,9 @@
"description": "WebSocket client and server",
"support": {
"issues": "https://github.com/Textalk/websocket-php/issues",
"source": "https://github.com/Textalk/websocket-php/tree/1.5.2"
"source": "https://github.com/Textalk/websocket-php/tree/1.5.5"
},
"time": "2021-02-12T15:39:23+00:00"
"time": "2021-08-07T10:21:40+00:00"
},
{
"name": "theseer/tokenizer",
@ -6433,16 +6340,16 @@
},
{
"name": "vimeo/psalm",
"version": "4.7.2",
"version": "4.13.1",
"source": {
"type": "git",
"url": "https://github.com/vimeo/psalm.git",
"reference": "83a0325c0a95c0ab531d6b90c877068b464377b5"
"reference": "5cf660f63b548ccd4a56f62d916ee4d6028e01a3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/83a0325c0a95c0ab531d6b90c877068b464377b5",
"reference": "83a0325c0a95c0ab531d6b90c877068b464377b5",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/5cf660f63b548ccd4a56f62d916ee4d6028e01a3",
"reference": "5cf660f63b548ccd4a56f62d916ee4d6028e01a3",
"shasum": ""
},
"require": {
@ -6452,6 +6359,7 @@
"composer/semver": "^1.4 || ^2.0 || ^3.0",
"composer/xdebug-handler": "^1.1 || ^2.0",
"dnoegel/php-xdg-base-dir": "^0.1.1",
"ext-ctype": "*",
"ext-dom": "*",
"ext-json": "*",
"ext-libxml": "*",
@ -6461,11 +6369,11 @@
"felixfbecker/advanced-json-rpc": "^3.0.3",
"felixfbecker/language-server-protocol": "^1.5",
"netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0",
"nikic/php-parser": "^4.10.1",
"nikic/php-parser": "^4.13",
"openlss/lib-array2xml": "^1.0",
"php": "^7.1|^8",
"sebastian/diff": "^3.0 || ^4.0",
"symfony/console": "^3.4.17 || ^4.1.6 || ^5.0",
"symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0",
"webmozart/path-util": "^2.3"
},
"provide": {
@ -6480,15 +6388,15 @@
"phpmyadmin/sql-parser": "5.1.0||dev-master",
"phpspec/prophecy": ">=1.9.0",
"phpunit/phpunit": "^9.0",
"psalm/plugin-phpunit": "^0.13",
"slevomat/coding-standard": "^6.3.11",
"psalm/plugin-phpunit": "^0.16",
"slevomat/coding-standard": "^7.0",
"squizlabs/php_codesniffer": "^3.5",
"symfony/process": "^4.3",
"weirdan/phpunit-appveyor-reporter": "^1.0.0",
"symfony/process": "^4.3 || ^5.0 || ^6.0",
"weirdan/prophecy-shim": "^1.0 || ^2.0"
},
"suggest": {
"ext-igbinary": "^2.0.5"
"ext-curl": "In order to send data to shepherd",
"ext-igbinary": "^2.0.5 is required, used to serialize caching data"
},
"bin": [
"psalm",
@ -6532,9 +6440,9 @@
],
"support": {
"issues": "https://github.com/vimeo/psalm/issues",
"source": "https://github.com/vimeo/psalm/tree/4.7.2"
"source": "https://github.com/vimeo/psalm/tree/4.13.1"
},
"time": "2021-05-01T20:56:25+00:00"
"time": "2021-11-23T23:52:49+00:00"
},
{
"name": "webmozart/path-util",

View file

@ -207,5 +207,20 @@ window.formValidation = (form, fields) => {
element.dispatchEvent(new Event("change"));
}
}
});
});
};
/**
* Method to add attribute for the UI on array attributes.
*
* Needs to be global - since client side routing will break it.
* @param {*} doc
* @param {*} key
* @returns
*/
function addAttribute(doc, key) {
if (!Array.isArray(doc[key])) {
doc[key] = [];
}
doc[key].push(null);
return doc;
}

View file

@ -38,6 +38,9 @@
datasets: []
},
options: {
animation: {
duration: 0
},
responsive: true,
hover: {
mode: "nearest",

View file

@ -47,7 +47,18 @@ class Detector
*/
public function getClient(): array
{
$client = $this->getDetector()->getClient();
if (strpos($this->userAgent, 'Appwrite CLI') !== false) {
$version = explode(' ', $this->userAgent)[0];
$version = explode('/', $version)[1];
$client = [
'type' => 'desktop',
'short_name' => 'cli',
'name' => 'Appwrite CLI',
'version' => $version
];
} else {
$client = $this->getDetector()->getClient();
}
return [
'clientType' => (isset($client['type'])) ? $client['type'] : '',
@ -86,4 +97,16 @@ class Detector
return $this->detctor;
}
/**
* Sets whether to skip bot detection.
* It is needed if we want bots to be processed as a simple clients. So we can detect if it is mobile client,
* or desktop, or enything else. By default all this information is not retrieved for the bots.
*
* @param bool $skip
*/
public function skipBotDetection(bool $skip = true): void
{
$this->getDetector()->skipBotDetection($skip);
}
}

View file

@ -240,7 +240,7 @@ class Realtime extends Adapter
* @param Document|null $project
* @return array
*/
public static function fromPayload(string $event, Document $payload, Document $project = null): array
public static function fromPayload(string $event, Document $payload, Document $project = null, Document $collection = null): array
{
$channels = [];
$roles = [];
@ -275,12 +275,6 @@ class Realtime extends Adapter
$channels[] = 'teams.' . $payload->getId();
$roles = ['team:' . $payload->getId()];
break;
case strpos($event, 'database.collections.') === 0:
$channels[] = 'collections';
$channels[] = 'collections.' . $payload->getId();
$roles = $payload->getRead();
break;
case strpos($event, 'database.attributes.') === 0:
case strpos($event, 'database.indexes.') === 0:
@ -290,10 +284,15 @@ class Realtime extends Adapter
break;
case strpos($event, 'database.documents.') === 0:
if ($collection->isEmpty()) {
throw new \Exception('Collection need to be passed to to Realtime for Document events in the Database.');
}
$channels[] = 'documents';
$channels[] = 'collections.' . $payload->getAttribute('$collection') . '.documents';
$channels[] = 'documents.' . $payload->getId();
$roles = $payload->getRead();
$roles = ($collection->getAttribute('permission') === 'collection') ? $collection->getRead() : $payload->getRead();
break;
case strpos($event, 'storage.') === 0:

View file

@ -46,8 +46,8 @@ class V06 extends Migration
$document->setAttribute('secret', json_encode([
'data' => OpenSSL::encrypt($document->getAttribute('secret'), OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
'method' => OpenSSL::CIPHER_AES_128_GCM,
'iv' => bin2hex($iv),
'tag' => bin2hex($tag),
'iv' => \bin2hex($iv),
'tag' => \bin2hex($tag ?? ''),
'version' => '1',
]));
}

View file

@ -392,10 +392,7 @@ class Swagger2 extends Format
foreach ($this->models as $model) {
foreach ($model->getRules() as $rule) {
if (
in_array($model->getType(), $usedModels)
&& !in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])
) {
if (!in_array($rule['type'], ['string', 'integer', 'boolean', 'json', 'float'])) {
$usedModels[] = $rule['type'];
}
}

View file

@ -19,8 +19,8 @@ trait DatabaseBase
]), [
'collectionId' => 'unique()',
'name' => 'Movies',
'read' => ['role:all'],
'write' => ['role:all'],
'read' => [],
'write' => [],
'permission' => 'document',
]);
@ -104,7 +104,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'title',
'key' => 'title',
'size' => 256,
'required' => true,
]);
@ -114,7 +114,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'releaseYear',
'key' => 'releaseYear',
'required' => true,
]);
@ -123,7 +123,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'actors',
'key' => 'actors',
'size' => 256,
'required' => false,
'array' => true,
@ -177,8 +177,8 @@ trait DatabaseBase
]), [
'collectionId' => 'unique()',
'name' => 'Response Models',
'read' => ['role:all'],
'write' => ['role:all'],
'read' => [],
'write' => [],
'permission' => 'document',
]);
@ -192,7 +192,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'string',
'key' => 'string',
'size' => 16,
'required' => false,
'default' => 'default',
@ -203,7 +203,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'email',
'key' => 'email',
'required' => false,
'default' => 'default@example.com',
]);
@ -213,7 +213,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'enum',
'key' => 'enum',
'elements' => ['yes', 'no', 'maybe'],
'required' => false,
'default' => 'maybe',
@ -224,7 +224,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'ip',
'key' => 'ip',
'required' => false,
'default' => '192.0.2.0',
]);
@ -234,7 +234,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'url',
'key' => 'url',
'required' => false,
'default' => 'http://example.com',
]);
@ -244,7 +244,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'integer',
'key' => 'integer',
'required' => false,
'min' => 1,
'max' => 5,
@ -256,7 +256,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'float',
'key' => 'float',
'required' => false,
'min' => 1.5,
'max' => 5.5,
@ -268,7 +268,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'boolean',
'key' => 'boolean',
'required' => false,
'default' => true,
]);
@ -632,7 +632,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'enum',
'key' => 'enum',
'elements' => ['yes', 'no', ''],
'required' => false,
'default' => 'maybe',
@ -654,7 +654,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'titleIndex',
'key' => 'titleIndex',
'type' => 'fulltext',
'attributes' => ['title'],
]);
@ -670,7 +670,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'releaseYear',
'key' => 'releaseYear',
'type' => 'key',
'attributes' => ['releaseYear'],
]);
@ -1293,8 +1293,8 @@ trait DatabaseBase
]), [
'collectionId' => 'unique()',
'name' => 'invalidDocumentStructure',
'read' => ['role:all'],
'write' => ['role:all'],
'read' => [],
'write' => [],
'permission' => 'document',
]);
@ -1308,7 +1308,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'email',
'key' => 'email',
'required' => false,
]);
@ -1317,7 +1317,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'enum',
'key' => 'enum',
'elements' => ['yes', 'no', 'maybe'],
'required' => false,
]);
@ -1327,7 +1327,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'ip',
'key' => 'ip',
'required' => false,
]);
@ -1336,7 +1336,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'url',
'key' => 'url',
'size' => 256,
'required' => false,
]);
@ -1346,7 +1346,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'range',
'key' => 'range',
'required' => false,
'min' => 1,
'max' => 10,
@ -1358,7 +1358,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'floatRange',
'key' => 'floatRange',
'required' => false,
'min' => 1.1,
'max' => 1.4,
@ -1369,7 +1369,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'probability',
'key' => 'probability',
'required' => false,
'min' => 0,
'max' => 1,
@ -1380,7 +1380,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'upperBound',
'key' => 'upperBound',
'required' => false,
'max' => 10,
]);
@ -1390,7 +1390,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'lowerBound',
'key' => 'lowerBound',
'required' => false,
'min' => 5,
]);
@ -1403,7 +1403,7 @@ trait DatabaseBase
'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'invalidRange',
'key' => 'invalidRange',
'required' => false,
'min' => 4,
'max' => 3,
@ -1413,7 +1413,7 @@ trait DatabaseBase
'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'defaultArray',
'key' => 'defaultArray',
'required' => false,
'default' => 42,
'array' => true,
@ -1852,7 +1852,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'attribute',
'key' => 'attribute',
'size' => 64,
'required' => true,
]);
@ -1868,7 +1868,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'key_attribute',
'key' => 'key_attribute',
'type' => 'key',
'attributes' => [$attribute['body']['key']],
]);
@ -1893,13 +1893,41 @@ trait DatabaseBase
$this->assertEquals(201, $document1['headers']['status-code']);
$document2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => 'unique()',
'data' => [
'attribute' => 'one',
],
'read' => [],
'write' => [$user],
]);
$this->assertEquals(201, $document2['headers']['status-code']);
$document3 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => 'unique()',
'data' => [
'attribute' => 'one',
],
'read' => [],
'write' => [],
]);
$this->assertEquals(201, $document3['headers']['status-code']);
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(1, $documents['body']['sum']);
$this->assertCount(1, $documents['body']['documents']);
$this->assertEquals(3, $documents['body']['sum']);
$this->assertCount(3, $documents['body']['documents']);
/*
* Test for Failure
@ -1958,7 +1986,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
]));
$this->assertEquals(404, $documents['headers']['status-code']);
$this->assertEquals(401, $documents['headers']['status-code']);
}
/**
@ -1971,7 +1999,7 @@ trait DatabaseBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'unique_title',
'key' => 'unique_title',
'type' => 'unique',
'attributes' => ['title'],
]);

View file

@ -165,7 +165,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'firstName',
'key' => 'firstName',
'size' => 256,
'required' => true,
]);
@ -175,7 +175,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'lastName',
'key' => 'lastName',
'size' => 256,
'required' => true,
]);
@ -185,7 +185,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'unneeded',
'key' => 'unneeded',
'size' => 256,
'required' => true,
]);
@ -214,7 +214,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'key_lastName',
'key' => 'key_lastName',
'type' => 'key',
'attributes' => [
'lastName',
@ -275,7 +275,7 @@ class DatabaseCustomServerTest extends Scope
return [
'collectionId' => $actors['body']['$id'],
'indexId' => $index['body']['key'],
'key' => $index['body']['key'],
];
}
@ -284,7 +284,7 @@ class DatabaseCustomServerTest extends Scope
*/
public function testDeleteIndex($data): array
{
$index = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/indexes/'. $data['indexId'], array_merge([
$index = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/indexes/'. $data['key'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
@ -316,7 +316,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'attribute1',
'key' => 'attribute1',
'size' => 16,
'required' => true,
]);
@ -326,7 +326,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'attribute2',
'key' => 'attribute2',
'size' => 16,
'required' => true,
]);
@ -343,7 +343,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'index1',
'key' => 'index1',
'type' => 'key',
'attributes' => ['attribute1', 'attribute2'],
'orders' => ['ASC', 'ASC'],
@ -354,7 +354,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'index2',
'key' => 'index2',
'type' => 'key',
'attributes' => ['attribute2'],
]);
@ -428,7 +428,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'attribute1',
'key' => 'attribute1',
'size' => 16,
'required' => true,
]);
@ -438,7 +438,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'attribute2',
'key' => 'attribute2',
'size' => 16,
'required' => true,
]);
@ -455,7 +455,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'index1',
'key' => 'index1',
'type' => 'key',
'attributes' => ['attribute1', 'attribute2'],
'orders' => ['ASC', 'ASC'],
@ -466,7 +466,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'index2',
'key' => 'index2',
'type' => 'key',
'attributes' => ['attribute2'],
]);
@ -615,7 +615,7 @@ class DatabaseCustomServerTest extends Scope
// 'x-appwrite-project' => $this->getProject()['$id'],
// 'x-appwrite-key' => $this->getProject()['apiKey']
// ]), [
// 'attributeId' => "attribute{$i}",
// 'key' => "attribute{$i}",
// 'required' => false,
// ]);
@ -629,7 +629,7 @@ class DatabaseCustomServerTest extends Scope
// 'x-appwrite-project' => $this->getProject()['$id'],
// 'x-appwrite-key' => $this->getProject()['apiKey']
// ]), [
// 'attributeId' => "tooMany",
// 'key' => "tooMany",
// 'required' => false,
// ]);
@ -663,7 +663,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => "attribute{$i}",
'key' => "attribute{$i}",
'size' => 1024,
'required' => true,
]);
@ -678,7 +678,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'tooWide',
'key' => 'tooWide',
'size' => 1024,
'required' => true,
]);
@ -714,7 +714,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => "attribute{$i}",
'key' => "attribute{$i}",
'size' => 64,
'required' => true,
]);
@ -751,7 +751,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => "key_attribute{$i}",
'key' => "key_attribute{$i}",
'type' => 'key',
'attributes' => ["attribute{$i}"],
]);
@ -780,7 +780,7 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'tooMany',
'key' => 'tooMany',
'type' => 'key',
'attributes' => ['attribute61'],
]);

View file

@ -26,7 +26,7 @@ class DatabasePermissionsGuestTest extends Scope
$collection = ['id' => $movies['body']['$id']];
$this->client->call(Client::METHOD_POST, '/database/collections/' . $collection['id'] . '/attributes/string', $this->getServerHeader(), [
'attributeId' => 'title',
'key' => 'title',
'size' => 256,
'required' => true,
]);

View file

@ -65,7 +65,7 @@ class DatabasePermissionsMemberTest extends Scope
$this->collections = ['public' => $public['body']['$id']];
$response = $this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['public'] . '/attributes/string', $this->getServerHeader(), [
'attributeId' => 'title',
'key' => 'title',
'size' => 256,
'required' => true,
]);
@ -83,7 +83,7 @@ class DatabasePermissionsMemberTest extends Scope
$this->collections['private'] = $private['body']['$id'];
$this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['private'] . '/attributes/string', $this->getServerHeader(), [
'attributeId' => 'title',
'key' => 'title',
'size' => 256,
'required' => true,
]);

View file

@ -45,7 +45,7 @@ class DatabasePermissionsTeamTest extends Scope
$this->collections['collection1'] = $collection1['body']['$id'];
$this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['collection1'] . '/attributes/string', $this->getServerHeader(), [
'attributeId' => 'title',
'key' => 'title',
'size' => 256,
'required' => true,
]);
@ -61,7 +61,7 @@ class DatabasePermissionsTeamTest extends Scope
$this->collections['collection2'] = $collection2['body']['$id'];
$this->client->call(Client::METHOD_POST, '/database/collections/' . $this->collections['collection2'] . '/attributes/string', $this->getServerHeader(), [
'attributeId' => 'title',
'key' => 'title',
'size' => 256,
'required' => true,
]);
@ -160,7 +160,7 @@ class DatabasePermissionsTeamTest extends Scope
if ($success) {
$this->assertCount(1, $documents['body']['documents']);
} else {
$this->assertEquals(404, $documents['headers']['status-code']);
$this->assertEquals(401, $documents['headers']['status-code']);
}
}

View file

@ -61,7 +61,7 @@ class RealtimeConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'attributeId' => 'name',
'key' => 'name',
'size' => 256,
'required' => true,
]);
@ -134,7 +134,7 @@ class RealtimeConsoleClientTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'indexId' => 'key_name',
'key' => 'key_name',
'type' => 'key',
'attributes' => [
'name',

View file

@ -77,9 +77,7 @@ class RealtimeCustomClientTest extends Scope
'files',
'files.1',
'collections',
'collections.1',
'collections.1.documents',
'collections.2',
'collections.2.documents',
'documents',
'documents.1',
@ -93,15 +91,13 @@ class RealtimeCustomClientTest extends Scope
$this->assertEquals('connected', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertNotEmpty($response['data']['user']);
$this->assertCount(12, $response['data']['channels']);
$this->assertCount(10, $response['data']['channels']);
$this->assertContains('account', $response['data']['channels']);
$this->assertContains('account.' . $userId, $response['data']['channels']);
$this->assertContains('files', $response['data']['channels']);
$this->assertContains('files.1', $response['data']['channels']);
$this->assertContains('collections', $response['data']['channels']);
$this->assertContains('collections.1', $response['data']['channels']);
$this->assertContains('collections.1.documents', $response['data']['channels']);
$this->assertContains('collections.2', $response['data']['channels']);
$this->assertContains('collections.2.documents', $response['data']['channels']);
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('documents.1', $response['data']['channels']);
@ -561,24 +557,11 @@ class RealtimeCustomClientTest extends Scope
]), [
'collectionId' => 'unique()',
'name' => 'Actors',
'read' => ['role:all'],
'write' => ['role:all'],
'permission' => 'collection'
'read' => [],
'write' => [],
'permission' => 'document'
]);
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('collections', $response['data']['channels']);
$this->assertContains('collections.' . $actors['body']['$id'], $response['data']['channels']);
$this->assertEquals('database.collections.create', $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$data = ['actorsId' => $actors['body']['$id']];
$name = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/attributes/string', array_merge([
@ -586,7 +569,7 @@ class RealtimeCustomClientTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'name',
'key' => 'name',
'size' => 256,
'required' => true,
]);
@ -662,7 +645,6 @@ class RealtimeCustomClientTest extends Scope
$this->assertEquals($response['data']['payload']['name'], 'Chris Evans 2');
/**
* Test Document Delete
*/
@ -703,6 +685,166 @@ class RealtimeCustomClientTest extends Scope
$client->close();
}
public function testChannelDatabaseCollectionPermissions()
{
$user = $this->getUser();
$session = $user['session'] ?? '';
$projectId = $this->getProject()['$id'];
$client = $this->getWebsocket(['documents', 'collections'], [
'origin' => 'http://localhost',
'cookie' => 'a_session_'.$projectId.'=' . $session
]);
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('connected', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('collections', $response['data']['channels']);
$this->assertNotEmpty($response['data']['user']);
$this->assertEquals($user['$id'], $response['data']['user']['$id']);
/**
* Test Collection Create
*/
$actors = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'collectionId' => 'unique()',
'name' => 'Actors',
'read' => ['role:all'],
'write' => ['role:all'],
'permission' => 'collection'
]);
$data = ['actorsId' => $actors['body']['$id']];
$name = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/attributes/string', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'key' => 'name',
'size' => 256,
'required' => true,
]);
$this->assertEquals($name['headers']['status-code'], 201);
$this->assertEquals($name['body']['key'], 'name');
$this->assertEquals($name['body']['type'], 'string');
$this->assertEquals($name['body']['size'], 256);
$this->assertEquals($name['body']['required'], true);
sleep(2);
/**
* Test Document Create
*/
$document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => 'unique()',
'data' => [
'name' => 'Chris Evans'
],
'read' => [],
'write' => [],
]);
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(3, $response['data']['channels']);
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('documents.' . $document['body']['$id'], $response['data']['channels']);
$this->assertContains('collections.' . $actors['body']['$id'] . '.documents', $response['data']['channels']);
$this->assertEquals('database.documents.create', $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($response['data']['payload']['name'], 'Chris Evans');
$data['documentId'] = $document['body']['$id'];
/**
* Test Document Update
*/
$document = $this->client->call(Client::METHOD_PATCH, '/database/collections/' . $data['actorsId'] . '/documents/' . $data['documentId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'name' => 'Chris Evans 2'
],
'read' => [],
'write' => [],
]);
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(3, $response['data']['channels']);
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('documents.' . $data['documentId'], $response['data']['channels']);
$this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
$this->assertEquals('database.documents.update', $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($response['data']['payload']['name'], 'Chris Evans 2');
/**
* Test Document Delete
*/
$document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['actorsId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'documentId' => 'unique()',
'data' => [
'name' => 'Bradley Cooper'
],
'read' => [],
'write' => [],
]);
$client->receive();
$this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['actorsId'] . '/documents/' . $document['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertArrayHasKey('data', $response);
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(3, $response['data']['channels']);
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('documents.' . $document['body']['$id'], $response['data']['channels']);
$this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
$this->assertEquals('database.documents.delete', $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($response['data']['payload']['name'], 'Bradley Cooper');
$client->close();
}
public function testChannelFiles()
{
$user = $this->getUser();

View file

@ -57,7 +57,7 @@ trait WebhooksBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'firstName',
'key' => 'firstName',
'size' => 256,
'required' => true,
]);
@ -67,7 +67,7 @@ trait WebhooksBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'lastName',
'key' => 'lastName',
'size' => 256,
'required' => true,
]);
@ -77,7 +77,7 @@ trait WebhooksBase
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'attributeId' => 'extra',
'key' => 'extra',
'size' => 64,
'required' => false,
]);

View file

@ -64,7 +64,7 @@ class WebhooksCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'indexId' => 'fullname',
'key' => 'fullname',
'type' => 'key',
'attributes' => ['lastName', 'firstName'],
'orders' => ['ASC', 'ASC'],
@ -125,7 +125,7 @@ class WebhooksCustomServerTest extends Scope
'write' => ['role:all'],
'permission' => 'document'
]);
$this->assertEquals($actors['headers']['status-code'], 201);
$this->assertNotEmpty($actors['body']['$id']);
@ -134,7 +134,7 @@ class WebhooksCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), []);
$this->assertEquals($actors['headers']['status-code'], 204);
$webhook = $this->getLastRequest();

View file

@ -2,7 +2,7 @@
namespace Appwrite\Tests;
use Appwrite\Database\Document;
use Utopia\Database\Document;
use Appwrite\Messaging\Adapter\Realtime;
use PHPUnit\Framework\TestCase;
@ -195,4 +195,51 @@ class MessagingTest extends TestCase
$this->assertArrayHasKey('account', $channels);
$this->assertArrayNotHasKey('account.456', $channels);
}
public function testFromPayloadCollectionLevelPermissions(): void
{
/**
* Test Collection Level Permissions
*/
$result = Realtime::fromPayload(
event: 'database.documents.create',
payload: new Document([
'$id' => 'test',
'$collection' => 'collection',
'$read' => ['role:admin'],
'$write' => ['role:admin']
]),
collection: new Document([
'$id' => 'collection',
'$read' => ['role:all'],
'$write' => ['role:all'],
'permission' => 'collection'
])
);
$this->assertContains('role:all', $result['roles']);
$this->assertNotContains('role:admin', $result['roles']);
/**
* Test Document Level Permissions
*/
$result = Realtime::fromPayload(
event: 'database.documents.create',
payload: new Document([
'$id' => 'test',
'$collection' => 'collection',
'$read' => ['role:all'],
'$write' => ['role:all']
]),
collection: new Document([
'$id' => 'collection',
'$read' => ['role:admin'],
'$write' => ['role:admin'],
'permission' => 'document'
])
);
$this->assertContains('role:all', $result['roles']);
$this->assertNotContains('role:admin', $result['roles']);
}
}