1
0
Fork 0
mirror of synced 2024-06-26 18:20:43 +12:00

Merge branch '0.7.x' of github.com:appwrite/appwrite into feat-465-add-arm-support

This commit is contained in:
Eldad Fux 2020-12-12 20:32:49 +02:00
commit b55bbdf79c
111 changed files with 4676 additions and 1074 deletions

3
.gitignore vendored
View file

@ -4,4 +4,5 @@
/tests/resources/storage/
/.idea/
.DS_Store
.php_cs.cache
.php_cs.cache
debug/

View file

@ -3,14 +3,30 @@
## Features
- New route in Locale API to fetch a list of languages (@TorstenDittmann)
- Improved Webhooks and New System Events - [Learn more]()
- Added Google Fonts to Appwrite for offline availability
- Added response to /locale/languages API with a list of languages (@TorstenDittmann ,[#351](https://github.com/appwrite/appwrite/issues/351))
- Added API response payload structure info and examples to the docs site ([#381](https://github.com/appwrite/appwrite/issues/381))
- Added Google Fonts to Appwrite for offline availability
- Added a new route in the Avatars API to get user initials avatar ([#386](https://github.com/appwrite/appwrite/issues/386))
- Added option to delete team from the console ([#380](https://github.com/appwrite/appwrite/issues/380))
- Added option to view team members from the console ([#378](https://github.com/appwrite/appwrite/issues/378))
- Add option to assign new team members to a team from the console and the API ([#379](https://github.com/appwrite/appwrite/issues/379))
- Added support for Brotli compression (@PedroCisnerosSantana, @Rohitub222, [#310](https://github.com/appwrite/appwrite/issues/310))
- Added a new route in the Avatars API to get user initials avatar
- Added option to delete team from the console
- Added Select All Checkbox for on Console API key Scopes Screen ([#477](https://github.com/appwrite/appwrite/issues/477))
- Added pagination and search for team memberships route ([#387](https://github.com/appwrite/appwrite/issues/387))
- UI performance & accessibility improvements ([#406](https://github.com/appwrite/appwrite/pull/406))
- Added option to view team members from the console
- Added option to join a user to any team from the console
- Added option to delete user from the API (@TorstenDittmann - #378)
- Added option to delete user from the console (@PineappleIOnic - #538)
- Added support for Brotli compression (@PedroCisnerosSantana, @Rohitub222)
- Created lazy deletion of data worker ([#521](https://github.com/appwrite/appwrite/issues/521))
- All emails are now sent asynchronously for improved performance (@TorstenDittmann ,[#402](https://github.com/appwrite/appwrite/pull/402))
- Updated grid for OAuth2 providers list in the console ([#413](https://github.com/appwrite/appwrite/issues/413))
- Upgraded Redis Resque queue library to version 1.3.6 ([#319](https://github.com/appwrite/appwrite/issues/319))
- Moved all Appwrite container logs to STDOUT & STDERR ([#389](https://github.com/appwrite/appwrite/issues/389))
- New UI micro-interactions and CSS fixes (@AnatoleLucet)
- UI performance & accessibility improvements (#406)
- New Doctor CLI to debug the Appwrite server ([#415](https://github.com/appwrite/appwrite/issues/415))
@ -40,6 +56,8 @@
- Upgraded Traefik image to version 2.3
- Upgraded Redis Docker image to version 6.0 (alpine)
- Upgraded Influxdb Docker image to version 1.8 (alpine)
- Added option to disable mail sending by setting empty SMTP host
- Upgraded installation script ([#490](https://github.com/appwrite/appwrite/issues/490))
## Breaking Changes (Read before upgrading!)
- **Deprecated** `first` and `last` query params for documents list route in the database API
@ -51,6 +69,17 @@
## Bug Fixes
- Fixed an issue where Special characters in _APP_OPENSSL_KEY_V1_ env caused an error ([#732](https://github.com/appwrite/appwrite/issues/732))
- Fixed an issue where Account webhook doesn't trigger through the console ([#493](https://github.com/appwrite/appwrite/issues/493))
- Fixed case sensitive country flag code ([#526](https://github.com/appwrite/appwrite/issues/526))
- Fixed redirect to Appwrite login page when deep link is provided ([#427](https://github.com/appwrite/appwrite/issues/427))
- Fixed an issue where Creating documents fails for parent documents would result in an error ([#514](https://github.com/appwrite/appwrite/issues/514))
- Fixed an issue with Email Sending Problem using external smtp ([#707](https://github.com/appwrite/appwrite/issues/707))
- Fixed an issue where you could not remove a key from User Prefs ([#316](https://github.com/appwrite/appwrite/issues/316))
- Fixed an issue where events are not fully visible in the console ([#492](https://github.com/appwrite/appwrite/issues/492))
- Fixed an issue where UI would wrongly validate integers ([#394](https://github.com/appwrite/appwrite/issues/394))
- Fixed an issue where graphs were cut in mobile view ([#376](https://github.com/appwrite/appwrite/issues/376))
- Fixed URL issue where console/ would not display list of projects ([#372](https://github.com/appwrite/appwrite/issues/372))
- Fixed output of /v1/health/queue/certificates returning wrong data
- Fixed bug where team members count was wrong in some cases
- Fixed network calculation for uploaded files

View file

@ -15,7 +15,7 @@ RUN composer update --ignore-platform-reqs --optimize-autoloader \
FROM php:7.4-cli-alpine as step1
ENV PHP_REDIS_VERSION=5.3.0 \
PHP_SWOOLE_VERSION=v4.5.6 \
PHP_SWOOLE_VERSION=v4.5.8 \
PHP_MAXMINDDB_VERSION=v1.8.0 \
PHP_XDEBUG_VERSION=sdebug_2_9-beta

View file

@ -11,4 +11,4 @@
## Reporting a Vulnerability
For security issues, kindly email us at security@appwrite.io instead of posting a public issue in GitHub.
For security issues, kindly email us at security@appwrite.io instead of posting a public issue in GitHub.

View file

@ -172,6 +172,14 @@ $collections = [
'required' => false,
'array' => true,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Filter',
'key' => 'filter',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'required' => false,
'array' => true,
],
],
],
Database::SYSTEM_COLLECTION_USERS => [
@ -292,6 +300,15 @@ $collections = [
'name' => 'Token',
'structure' => true,
'rules' => [
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'User ID',
'key' => 'userId',
'type' => Database::SYSTEM_VAR_TYPE_TEXT,
'default' => null,
'required' => false,
'array' => false,
],
[
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'label' => 'Type',

View file

@ -3,68 +3,163 @@
/**
* List of publicly accessiable system events
*/
use Appwrite\Utopia\Response;
return [
'account.create' => [
'description' => 'This event triggers when the account is created.',
'model' => Response::MODEL_USER,
'note' => '',
],
'account.update.email' => [
'description' => 'This event triggers when the account email address is updated.',
'model' => Response::MODEL_USER,
'note' => '',
],
'account.update.name' => [
'description' => 'This event triggers when the account name is updated.',
'model' => Response::MODEL_USER,
'note' => '',
],
'account.update.password' => [
'description' => 'This event triggers when the account password is updated.',
'model' => Response::MODEL_USER,
'note' => '',
],
'account.update.prefs' => [
'description' => 'This event triggers when the account preferences are updated.',
'model' => Response::MODEL_USER,
'note' => '',
],
'account.recovery.create' => [
'description' => 'This event triggers when the account recovery token is created.',
'model' => Response::MODEL_TOKEN,
'note' => 'version >= 0.7',
],
'account.recovery.update' => [
'description' => 'This event triggers when the account recovery token is validated.',
'model' => Response::MODEL_TOKEN,
'note' => 'version >= 0.7',
],
'account.verification.create' => [
'description' => 'This event triggers when the account verification token is created.',
'model' => Response::MODEL_TOKEN,
'note' => 'version >= 0.7',
],
'account.verification.update' => [
'description' => 'This event triggers when the account verification token is validated.',
'model' => Response::MODEL_TOKEN,
'note' => 'version >= 0.7',
],
'account.delete' => [
'description' => 'This event triggers when the account is deleted.',
'model' => Response::MODEL_USER,
'note' => '',
],
'account.sessions.create' => [
'description' => 'This event triggers when the account session is created.',
'model' => Response::MODEL_SESSION,
'note' => '',
],
'account.sessions.delete' => [
'description' => 'This event triggers when the account session is deleted.',
'model' => Response::MODEL_SESSION,
'note' => '',
],
'database.collections.create' => [
'description' => 'This event triggers when a database collection is created.',
'model' => Response::MODEL_COLLECTION,
'note' => '',
],
'database.collections.update' => [
'description' => 'This event triggers when a database collection is updated.',
'model' => Response::MODEL_COLLECTION,
'note' => '',
],
'database.collections.delete' => [
'description' => 'This event triggers when a database collection is deleted.',
'model' => Response::MODEL_COLLECTION,
'note' => '',
],
'database.documents.create' => [
'description' => 'This event triggers when a database document is created.',
'model' => Response::MODEL_ANY,
'note' => '',
],
'database.documents.patch' => [
'description' => 'This event triggers when a database document is patched.',
'database.documents.update' => [
'description' => 'This event triggers when a database document is updated.',
'model' => Response::MODEL_ANY,
'note' => '',
],
'database.documents.delete' => [
'description' => 'This event triggers when a database document is deleted.',
'model' => Response::MODEL_ANY,
'note' => '',
],
'storage.files.create' => [
'description' => 'This event triggers when a storage file is created.',
'model' => Response::MODEL_FILE,
'note' => '',
],
'storage.files.update' => [
'description' => 'This event triggers when a storage file is updated.',
'model' => Response::MODEL_FILE,
'note' => '',
],
'storage.files.delete' => [
'description' => 'This event triggers when a storage file is deleted.',
'model' => Response::MODEL_FILE,
'note' => '',
],
'users.create' => [
'description' => 'This event triggers when a user is created from the users API.',
'model' => Response::MODEL_USER,
'note' => 'version >= 0.7',
],
'users.update.status' => [
'description' => 'This event triggers when a user status is updated from the users API.',
'model' => Response::MODEL_USER,
'note' => 'version >= 0.7',
],
'users.delete' => [
'description' => 'This event triggers when a user is deleted from users API.',
'model' => Response::MODEL_USER,
'note' => 'version >= 0.7',
],
'users.sessions.delete' => [
'description' => 'This event triggers when a user session is deleted from users API.',
'model' => Response::MODEL_SESSION,
'note' => 'version >= 0.7',
],
'teams.create' => [
'description' => 'This event triggers when a team is created.',
'model' => Response::MODEL_TEAM,
'note' => 'version >= 0.7',
],
'teams.update' => [
'description' => 'This event triggers when a team is updated.',
'model' => Response::MODEL_TEAM,
'note' => 'version >= 0.7',
],
'teams.delete' => [
'description' => 'This event triggers when a team is deleted.',
'model' => Response::MODEL_TEAM,
'note' => 'version >= 0.7',
],
'teams.memberships.create' => [
'description' => 'This event triggers when a team memberships is created.',
'model' => Response::MODEL_MEMBERSHIP,
'note' => 'version >= 0.7',
],
'teams.memberships.update.status' => [
'description' => 'This event triggers when a team memberships status is updated.',
'model' => Response::MODEL_MEMBERSHIP,
'note' => 'version >= 0.7',
],
'teams.memberships.delete' => [
'description' => 'This event triggers when a team memberships is deleted.',
'model' => Response::MODEL_MEMBERSHIP,
'note' => 'version >= 0.7',
],
];

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -37,6 +37,9 @@ App::post('/v1/account')
->label('sdk.namespace', 'account')
->label('sdk.method', 'create')
->label('sdk.description', '/docs/references/account/create.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->label('abuse-limit', 10)
->param('email', '', new Email(), 'User email.')
->param('password', '', new Password(), 'User password. Must be between 6 to 32 chars.')
@ -102,6 +105,10 @@ App::post('/v1/account')
Authorization::enable();
Authorization::unsetRole('role:'.Auth::USER_ROLE_GUEST);
Authorization::setRole('user:'.$user->getId());
Authorization::setRole('role:'.Auth::USER_ROLE_MEMBER);
if (false === $user) {
throw new Exception('Failed saving user to DB', 500);
}
@ -112,10 +119,6 @@ App::post('/v1/account')
->setParam('resource', 'users/'.$user->getId())
;
$user
->setAttribute('roles', Authorization::getRoles())
;
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($user, Response::MODEL_USER)
@ -131,6 +134,9 @@ App::post('/v1/account/sessions')
->label('sdk.namespace', 'account')
->label('sdk.method', 'createSession')
->label('sdk.description', '/docs/references/account/create-session.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_SESSION)
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},email:{param-email}')
->param('email', '', new Email(), 'User email.')
@ -184,12 +190,12 @@ App::post('/v1/account/sessions')
$session = new Document([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$permissions' => ['read' => ['user:'.$profile->getId()], 'write' => ['user:'.$profile->getId()]],
'userId' => $profile->getId(),
'type' => Auth::TOKEN_TYPE_LOGIN,
'secret' => Auth::hash($secret), // On way hash encryption to protect DB leak
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
'expire' => $expiry,
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
'osCode' => $osCode,
'osName' => $osName,
'osVersion' => $osVersion,
@ -499,7 +505,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
// Create session token, verify user account and update OAuth2 ID and Access Token
$dd = new DeviceDetector($request->getUserAgent('UNKNOWN'));
$dd->parse();
@ -522,12 +527,12 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$session = new Document([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$permissions' => ['read' => ['user:'.$user['$id']], 'write' => ['user:'.$user['$id']]],
'userId' => $user->getId(),
'type' => Auth::TOKEN_TYPE_LOGIN,
'secret' => Auth::hash($secret), // On way hash encryption to protect DB leak
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
'expire' => $expiry,
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
'osCode' => $osCode,
'osName' => $osName,
'osVersion' => $osVersion,
@ -611,15 +616,13 @@ App::get('/v1/account')
->label('sdk.namespace', 'account')
->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/account/get.md')
->label('sdk.response', ['200' => 'user'])
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->action(function ($response, $user) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
$user
->setAttribute('roles', Authorization::getRoles())
;
$response->dynamic($user, Response::MODEL_USER);
}, ['response', 'user']);
@ -631,13 +634,16 @@ App::get('/v1/account/prefs')
->label('sdk.namespace', 'account')
->label('sdk.method', 'getPrefs')
->label('sdk.description', '/docs/references/account/get-prefs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ANY)
->action(function ($response, $user) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
$prefs = $user->getAttribute('prefs', new \stdClass);
$response->json($prefs);
$response->dynamic(new Document($prefs), Response::MODEL_ANY);
}, ['response', 'user']);
App::get('/v1/account/sessions')
@ -648,6 +654,9 @@ App::get('/v1/account/sessions')
->label('sdk.namespace', 'account')
->label('sdk.method', 'getSessions')
->label('sdk.description', '/docs/references/account/get-sessions.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_SESSION_LIST)
->action(function ($response, $user, $locale) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
@ -685,6 +694,9 @@ App::get('/v1/account/logs')
->label('sdk.namespace', 'account')
->label('sdk.method', 'getLogs')
->label('sdk.description', '/docs/references/account/get-logs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_LOG_LIST)
->action(function ($response, $register, $project, $user, $locale, $geodb) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
@ -783,6 +795,9 @@ App::patch('/v1/account/name')
->label('sdk.namespace', 'account')
->label('sdk.method', 'updateName')
->label('sdk.description', '/docs/references/account/update-name.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('name', '', new Text(128), 'User name. Max length: 128 chars.')
->action(function ($name, $response, $user, $projectDB, $audits) {
/** @var Appwrite\Utopia\Response $response */
@ -798,8 +813,6 @@ App::patch('/v1/account/name')
throw new Exception('Failed saving user to DB', 500);
}
$user->setAttribute('roles', Authorization::getRoles());
$audits
->setParam('userId', $user->getId())
->setParam('event', 'account.update.name')
@ -818,6 +831,9 @@ App::patch('/v1/account/password')
->label('sdk.namespace', 'account')
->label('sdk.method', 'updatePassword')
->label('sdk.description', '/docs/references/account/update-password.md')
->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(), 'New user password. Must be between 6 to 32 chars.')
->param('oldPassword', '', new Password(), 'Old user password. Must be between 6 to 32 chars.')
->action(function ($password, $oldPassword, $response, $user, $projectDB, $audits) {
@ -838,8 +854,6 @@ App::patch('/v1/account/password')
throw new Exception('Failed saving user to DB', 500);
}
$user->setAttribute('roles', Authorization::getRoles());
$audits
->setParam('userId', $user->getId())
->setParam('event', 'account.update.password')
@ -858,6 +872,9 @@ App::patch('/v1/account/email')
->label('sdk.namespace', 'account')
->label('sdk.method', 'updateEmail')
->label('sdk.description', '/docs/references/account/update-email.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('email', '', new Email(), 'User email.')
->param('password', '', new Password(), 'User password. Must be between 6 to 32 chars.')
->action(function ($email, $password, $response, $user, $projectDB, $audits) {
@ -892,8 +909,6 @@ App::patch('/v1/account/email')
if (false === $user) {
throw new Exception('Failed saving user to DB', 500);
}
$user->setAttribute('roles', Authorization::getRoles());
$audits
->setParam('userId', $user->getId())
@ -913,6 +928,9 @@ App::patch('/v1/account/prefs')
->label('sdk.namespace', 'account')
->label('sdk.method', 'updatePrefs')
->label('sdk.description', '/docs/references/account/update-prefs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ANY)
->param('prefs', [], new Assoc(), 'Prefs key-value JSON object.')
->action(function ($prefs, $response, $user, $projectDB, $audits) {
/** @var Appwrite\Utopia\Response $response */
@ -933,9 +951,7 @@ App::patch('/v1/account/prefs')
->setParam('resource', 'users/'.$user->getId())
;
$prefs = $user->getAttribute('prefs', new \stdClass);
$response->json($prefs);
$response->dynamic($user, Response::MODEL_USER);
}, ['response', 'user', 'projectDB', 'audits']);
App::delete('/v1/account')
@ -947,13 +963,16 @@ App::delete('/v1/account')
->label('sdk.namespace', 'account')
->label('sdk.method', 'delete')
->label('sdk.description', '/docs/references/account/delete.md')
->action(function ($request, $response, $user, $projectDB, $audits, $webhooks) {
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->action(function ($request, $response, $user, $projectDB, $audits, $events) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $events */
$protocol = $request->getProtocol();
$user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
@ -979,7 +998,7 @@ App::delete('/v1/account')
->setParam('data', $user->getArrayCopy())
;
$webhooks
$events
->setParam('payload', $response->output($user, Response::MODEL_USER))
;
@ -994,7 +1013,7 @@ App::delete('/v1/account')
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
->noContent()
;
}, ['request', 'response', 'user', 'projectDB', 'audits', 'webhooks']);
}, ['request', 'response', 'user', 'projectDB', 'audits', 'events']);
App::delete('/v1/account/sessions/:sessionId')
->desc('Delete Account Session')
@ -1005,15 +1024,18 @@ App::delete('/v1/account/sessions/:sessionId')
->label('sdk.namespace', 'account')
->label('sdk.method', 'deleteSession')
->label('sdk.description', '/docs/references/account/delete-session.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->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.')
->action(function ($sessionId, $request, $response, $user, $projectDB, $audits, $webhooks) {
->action(function ($sessionId, $request, $response, $user, $projectDB, $audits, $events) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $events */
$protocol = $request->getProtocol();
$sessionId = ($sessionId === 'current')
@ -1034,29 +1056,33 @@ App::delete('/v1/account/sessions/:sessionId')
->setParam('resource', '/user/'.$user->getId())
;
$webhooks
->setParam('payload', $response->output($user, Response::MODEL_USER))
;
if (!Config::getParam('domainVerification')) {
$response
->addHeader('X-Fallback-Cookies', \json_encode([]))
;
}
$token->setAttribute('current', false);
if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$token->setAttribute('current', true);
$response
->addCookie(Auth::$cookieName.'_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
;
}
$events
->setParam('payload', $response->output($token, Response::MODEL_SESSION))
;
return $response->noContent();
}
}
throw new Exception('Session not found', 404);
}, ['request', 'response', 'user', 'projectDB', 'audits', 'webhooks']);
}, ['request', 'response', 'user', 'projectDB', 'audits', 'events']);
App::delete('/v1/account/sessions')
->desc('Delete All Account Sessions')
@ -1067,14 +1093,17 @@ App::delete('/v1/account/sessions')
->label('sdk.namespace', 'account')
->label('sdk.method', 'deleteSessions')
->label('sdk.description', '/docs/references/account/delete-sessions.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->label('abuse-limit', 100)
->action(function ($request, $response, $user, $projectDB, $audits, $webhooks) {
->action(function ($request, $response, $user, $projectDB, $audits, $events) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $events */
$protocol = $request->getProtocol();
$tokens = $user->getAttribute('tokens', []);
@ -1089,10 +1118,6 @@ App::delete('/v1/account/sessions')
->setParam('event', 'account.sessions.delete')
->setParam('resource', '/user/'.$user->getId())
;
$webhooks
->setParam('payload', $response->output($user, Response::MODEL_USER))
;
if (!Config::getParam('domainVerification')) {
$response
@ -1100,30 +1125,44 @@ App::delete('/v1/account/sessions')
;
}
$token->setAttribute('current', false);
if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$token->setAttribute('current', true);
$response
->addCookie(Auth::$cookieName.'_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
;
}
}
$events
->setParam('payload', $response->output(new Document([
'sum' => count($tokens),
'sessions' => $tokens
]), Response::MODEL_SESSION_LIST))
;
$response->noContent();
}, ['request', 'response', 'user', 'projectDB', 'audits', 'webhooks']);
}, ['request', 'response', 'user', 'projectDB', 'audits', 'events']);
App::post('/v1/account/recovery')
->desc('Create Password Recovery')
->groups(['api', 'account'])
->label('scope', 'public')
->label('event', 'account.recovery.create')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'createRecovery')
->label('sdk.description', '/docs/references/account/create-recovery.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TOKEN)
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},email:{param-email}')
->param('email', '', new Email(), 'User email.')
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the recovery 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'])
->action(function ($email, $url, $request, $response, $projectDB, $project, $locale, $mails, $audits) {
->action(function ($email, $url, $request, $response, $projectDB, $project, $locale, $mails, $audits, $events) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
@ -1131,6 +1170,10 @@ App::post('/v1/account/recovery')
/** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $mails */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
$profile = $projectDB->getCollectionFirst([ // Get user by email address
'limit' => 1,
@ -1148,8 +1191,9 @@ App::post('/v1/account/recovery')
$recovery = new Document([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$permissions' => ['read' => ['user:'.$profile->getId()], 'write' => ['user:'.$profile->getId()]],
'userId' => $profile->getId(),
'type' => Auth::TOKEN_TYPE_RECOVERY,
'secret' => Auth::hash($secret), // On way hash encryption to protect DB leak
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
'expire' => \time() + Auth::TOKEN_EXPIRATION_RECOVERY,
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
@ -1205,6 +1249,17 @@ App::post('/v1/account/recovery')
->trigger();
;
$events
->setParam('payload',
$response->output($recovery->setAttribute('secret', $secret),
Response::MODEL_TOKEN
))
;
$recovery // Hide secret for clients, sp
->setAttribute('secret',
($isPreviliggedUser || $isAppUser) ? $secret : '');
$audits
->setParam('userId', $profile->getId())
->setParam('event', 'account.recovery.create')
@ -1213,18 +1268,22 @@ App::post('/v1/account/recovery')
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($recovery->getArrayCopy(['$id', 'type', 'expire']))
->dynamic($recovery, Response::MODEL_TOKEN)
;
}, ['request', 'response', 'projectDB', 'project', 'locale', 'mails', 'audits']);
}, ['request', 'response', 'projectDB', 'project', 'locale', 'mails', 'audits', 'events']);
App::put('/v1/account/recovery')
->desc('Complete Password Recovery')
->groups(['api', 'account'])
->label('scope', 'public')
->label('event', 'account.recovery.update')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'updateRecovery')
->label('sdk.description', '/docs/references/account/update-recovery.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->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.')
@ -1286,21 +1345,25 @@ App::put('/v1/account/recovery')
$recovery = $profile->search('$id', $recovery, $profile->getAttribute('tokens', []));
$response->json($recovery->getArrayCopy(['$id', 'type', 'expire']));
$response->dynamic($recovery, Response::MODEL_TOKEN);
}, ['response', 'projectDB', 'audits']);
App::post('/v1/account/verification')
->desc('Create Email Verification')
->groups(['api', 'account'])
->label('scope', 'account')
->label('event', 'account.verification.create')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'createVerification')
->label('sdk.description', '/docs/references/account/create-verification.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TOKEN)
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},email:{param-email}')
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the verification 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 built-in confirm page
->action(function ($url, $request, $response, $project, $user, $projectDB, $locale, $audits, $mails) {
->action(function ($url, $request, $response, $project, $user, $projectDB, $locale, $audits, $events, $mails) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
@ -1308,15 +1371,20 @@ App::post('/v1/account/verification')
/** @var Appwrite\Database\Database $projectDB */
/** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $mails */
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
$verificationSecret = Auth::tokenGenerator();
$verification = new Document([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$permissions' => ['read' => ['user:'.$user->getId()], 'write' => ['user:'.$user->getId()]],
'userId' => $user->getId(),
'type' => Auth::TOKEN_TYPE_VERIFICATION,
'secret' => Auth::hash($verificationSecret), // On way hash encryption to protect DB leak
'secret' => Auth::hash($verificationSecret), // One way hash encryption to protect DB leak
'expire' => \time() + Auth::TOKEN_EXPIRATION_CONFIRM,
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
@ -1372,6 +1440,17 @@ App::post('/v1/account/verification')
->trigger()
;
$events
->setParam('payload',
$response->output($verification->setAttribute('secret', $verificationSecret),
Response::MODEL_TOKEN
))
;
$verification // Hide secret for clients, sp
->setAttribute('secret',
($isPreviliggedUser || $isAppUser) ? $verificationSecret : '');
$audits
->setParam('userId', $user->getId())
->setParam('event', 'account.verification.create')
@ -1380,18 +1459,22 @@ App::post('/v1/account/verification')
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($verification->getArrayCopy(['$id', 'type', 'expire']))
->dynamic($verification, Response::MODEL_TOKEN)
;
}, ['request', 'response', 'project', 'user', 'projectDB', 'locale', 'audits', 'mails']);
}, ['request', 'response', 'project', 'user', 'projectDB', 'locale', 'audits', 'events', 'mails']);
App::put('/v1/account/verification')
->desc('Complete Email Verification')
->groups(['api', 'account'])
->label('scope', 'public')
->label('event', 'account.verification.update')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'updateVerification')
->label('sdk.description', '/docs/references/account/update-verification.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->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.')
@ -1446,5 +1529,5 @@ App::put('/v1/account/verification')
$verification = $profile->search('$id', $verification, $profile->getAttribute('tokens', []));
$response->json($verification->getArrayCopy(['$id', 'type', 'expire']));
$response->dynamic($verification, Response::MODEL_TOKEN);
}, ['response', 'user', 'projectDB', 'audits']);

View file

@ -11,6 +11,7 @@ use Utopia\Cache\Cache;
use Utopia\Cache\Adapter\Filesystem;
use Appwrite\Resize\Resize;
use Appwrite\URL\URL as URLParse;
use Appwrite\Utopia\Response;
use Utopia\Config\Config;
use Utopia\Validator\HexColor;
use chillerlan\QRCode\QRCode;
@ -88,6 +89,8 @@ App::get('/v1/avatars/credit-cards/:code')
->label('sdk.method', 'getCreditCard')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-credit-card.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE_PNG)
->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-credit-cards'))), 'Credit Card Code. Possible values: '.\implode(', ', \array_keys(Config::getParam('avatar-credit-cards'))).'.')
->param('width', 100, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
@ -105,6 +108,8 @@ App::get('/v1/avatars/browsers/:code')
->label('sdk.method', 'getBrowser')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-browser.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE_PNG)
->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-browsers'))), 'Browser Code.')
->param('width', 100, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
@ -122,6 +127,8 @@ App::get('/v1/avatars/flags/:code')
->label('sdk.method', 'getFlag')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-flag.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE_PNG)
->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-flags'))), 'Country Code. ISO Alpha-2 country code format.')
->param('width', 100, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
@ -139,6 +146,8 @@ App::get('/v1/avatars/image')
->label('sdk.method', 'getImage')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-image.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE)
->param('url', '', new URL(), 'Image URL which you want to crop.')
->param('width', 400, new Range(0, 2000), 'Resize preview image width, Pass an integer between 0 to 2000.', true)
->param('height', 400, new Range(0, 2000), 'Resize preview image height, Pass an integer between 0 to 2000.', true)
@ -205,6 +214,8 @@ App::get('/v1/avatars/favicon')
->label('sdk.method', 'getFavicon')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-favicon.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE)
->param('url', '', new URL(), 'Website URL which you want to fetch the favicon from.')
->action(function ($url, $response) {
/** @var Appwrite\Utopia\Response $response */
@ -355,6 +366,8 @@ App::get('/v1/avatars/qr')
->label('sdk.method', 'getQR')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-qr.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE_PNG)
->param('text', '', new Text(512), 'Plain text to be converted to QR code image.')
->param('size', 400, new Range(0, 1000), 'QR code size. Pass an integer between 0 to 1000. Defaults to 400.', true)
->param('margin', 1, new Range(0, 10), 'Margin from edge. Pass an integer between 0 to 10. Defaults to 1.', true)
@ -390,6 +403,8 @@ App::get('/v1/avatars/initials')
->label('sdk.method', 'getInitials')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-initials.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE_PNG)
->param('name', '', new Text(128), 'Full Name. When empty, current user name or email will be used. Max length: 128 chars.', true)
->param('width', 500, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 500, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)

View file

@ -27,6 +27,9 @@ App::post('/v1/database/collections')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.method', 'createCollection')
->label('sdk.description', '/docs/references/database/create-collection.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_COLLECTION)
->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.')
->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.')
@ -93,6 +96,9 @@ App::get('/v1/database/collections')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.method', 'listCollections')
->label('sdk.description', '/docs/references/database/list-collections.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->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, 40000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
@ -127,6 +133,9 @@ App::get('/v1/database/collections/:collectionId')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.method', 'getCollection')
->label('sdk.description', '/docs/references/database/get-collection.md')
->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.')
->action(function ($collectionId, $response, $projectDB) {
/** @var Appwrite\Utopia\Response $response */
@ -150,6 +159,9 @@ App::put('/v1/database/collections/:collectionId')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.method', 'updateCollection')
->label('sdk.description', '/docs/references/database/update-collection.md')
->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('name', null, new Text(128), 'Collection name. Max length: 128 chars.')
->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.')
@ -219,11 +231,14 @@ App::delete('/v1/database/collections/:collectionId')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.method', 'deleteCollection')
->label('sdk.description', '/docs/references/database/delete-collection.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->param('collectionId', '', new UID(), 'Collection unique ID.')
->action(function ($collectionId, $response, $projectDB, $webhooks, $audits) {
->action(function ($collectionId, $response, $projectDB, $events, $audits, $deletes) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
$collection = $projectDB->getDocument($collectionId, false);
@ -235,8 +250,12 @@ App::delete('/v1/database/collections/:collectionId')
if (!$projectDB->deleteDocument($collectionId)) {
throw new Exception('Failed to remove collection from DB', 500);
}
$webhooks
$deletes
->setParam('document', $collection)
;
$events
->setParam('payload', $response->output($collection, Response::MODEL_COLLECTION))
;
@ -247,7 +266,7 @@ App::delete('/v1/database/collections/:collectionId')
;
$response->noContent();
}, ['response', 'projectDB', 'webhooks', 'audits']);
}, ['response', 'projectDB', 'events', 'audits', 'deletes']);
App::post('/v1/database/collections/:collectionId/documents')
->desc('Create Document')
@ -258,6 +277,9 @@ App::post('/v1/database/collections/:collectionId/documents')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.method', 'createDocument')
->label('sdk.description', '/docs/references/database/create-document.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ANY)
->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('data', [], new JSON(), 'Document data as JSON object.')
->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.')
@ -371,10 +393,13 @@ App::get('/v1/database/collections/:collectionId/documents')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.method', 'listDocuments')
->label('sdk.description', '/docs/references/database/list-documents.md')
->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', 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('filters', [], new ArrayList(new Text(128)), 'Array of filter strings. Each filter is constructed from a key name, comparison operator (=, !=, >, <, <=, >=) and a value. You can also use a dot (.) separator in attribute names to filter by child document attributes. Examples: \'name=John Doe\' or \'category.$id>=5bed2d152c362\'.', true)
->param('limit', 25, new Range(0, 1000), 'Maximum number of documents to return in response. Use this value to manage pagination.', true)
->param('offset', 0, new Range(0, 900000000), 'Offset value. Use this value to manage pagination.', true)
->param('limit', 25, new Range(0, 100), 'Maximum number of documents to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 900000000), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->param('orderField', '$id', new Text(128), 'Document field that results will be sorted by.', true)
->param('orderType', 'ASC', new WhiteList(['DESC', 'ASC'], true), 'Order direction. Possible values are DESC for descending order, or ASC for ascending order.', true)
->param('orderCast', 'string', new WhiteList(['int', 'string', 'date', 'time', 'datetime'], true), 'Order field type casting. Possible values are int, string, date, time or datetime. The database will attempt to cast the order field to the value you pass here. The default value is a string.', true)
@ -429,6 +454,9 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.method', 'getDocument')
->label('sdk.description', '/docs/references/database/get-document.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ANY)
->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.')
->action(function ($collectionId, $documentId, $request, $response, $projectDB) {
@ -455,6 +483,9 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.method', 'updateDocument')
->label('sdk.description', '/docs/references/database/update-document.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ANY)
->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('data', [], new JSON(), 'Document data as JSON object.')
@ -529,12 +560,15 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.method', 'deleteDocument')
->label('sdk.description', '/docs/references/database/delete-document.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->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.')
->action(function ($collectionId, $documentId, $response, $projectDB, $webhooks, $audits) {
->action(function ($collectionId, $documentId, $response, $projectDB, $events, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
$collection = $projectDB->getDocument($collectionId, false);
@ -558,7 +592,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
throw new Exception('Failed to remove document from DB', 500);
}
$webhooks
$events
->setParam('payload', $response->output($document, Response::MODEL_ANY))
;
@ -569,4 +603,4 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
;
$response->noContent();
}, ['response', 'projectDB', 'webhooks', 'audits']);
}, ['response', 'projectDB', 'events', 'audits']);

View file

@ -29,6 +29,9 @@ App::post('/v1/functions')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'create')
->label('sdk.description', '/docs/references/functions/create-function.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION)
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
->param('env', '', new WhiteList(array_keys(Config::getParam('environments')), true), 'Execution enviornment.')
->param('vars', [], new Assoc(), 'Key-value JSON object.', true)
@ -74,6 +77,9 @@ App::get('/v1/functions')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'list')
->label('sdk.description', '/docs/references/functions/list-functions.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION_LIST)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
@ -105,6 +111,9 @@ App::get('/v1/functions/:functionId')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/functions/get-function.md')
->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.')
->action(function ($functionId, $response, $projectDB) {
$function = $projectDB->getDocument($functionId);
@ -237,6 +246,9 @@ App::put('/v1/functions/:functionId')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'update')
->label('sdk.description', '/docs/references/functions/update-function.md')
->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('name', '', new Text(128), 'Function name. Max length: 128 chars.')
->param('vars', [], new Assoc(), 'Key-value JSON object.', true)
@ -292,6 +304,9 @@ App::patch('/v1/functions/:functionId/tag')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'updateTag')
->label('sdk.description', '/docs/references/functions/update-tag.md')
->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.')
->action(function ($functionId, $tag, $response, $projectDB) {
@ -330,6 +345,9 @@ App::delete('/v1/functions/:functionId')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'delete')
->label('sdk.description', '/docs/references/functions/delete-function.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->param('functionId', '', new UID(), 'Function unique ID.')
->action(function ($functionId, $response, $projectDB, $deletes) {
/** @var Appwrite\Utopia\Response $response */
@ -361,6 +379,10 @@ App::post('/v1/functions/:functionId/tags')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'createTag')
->label('sdk.description', '/docs/references/functions/create-tag.md')
->label('sdk.request.type', 'multipart/form-data')
->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('command', '', new Text('1028'), 'Code execution command.')
->param('code', [], new File(), 'Gzip file containing your code.', false)
@ -443,6 +465,9 @@ App::get('/v1/functions/:functionId/tags')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'listTags')
->label('sdk.description', '/docs/references/functions/list-tags.md')
->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('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)
@ -482,6 +507,9 @@ App::get('/v1/functions/:functionId/tags/:tagId')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getTag')
->label('sdk.description', '/docs/references/functions/get-tag.md')
->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.')
->action(function ($functionId, $tagId, $response, $projectDB) {
@ -512,6 +540,9 @@ App::delete('/v1/functions/:functionId/tags/:tagId')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'deleteTag')
->label('sdk.description', '/docs/references/functions/delete-tag.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->param('functionId', '', new UID(), 'Function unique ID.')
->param('tagId', '', new UID(), 'Tag unique ID.')
->action(function ($functionId, $tagId, $response, $projectDB, $usage) {
@ -564,6 +595,9 @@ App::post('/v1/functions/:functionId/executions')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'createExecution')
->label('sdk.description', '/docs/references/functions/create-execution.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_EXECUTION)
->param('functionId', '', new UID(), 'Function unique ID.')
// ->param('async', 1, new Range(0, 1), 'Execute code asynchronously. Pass 1 for true, 0 for false. Default value is 1.', true)
->action(function ($functionId, /*$async,*/ $response, $project, $projectDB) {
@ -629,6 +663,9 @@ App::get('/v1/functions/:functionId/executions')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'listExecutions')
->label('sdk.description', '/docs/references/functions/list-executions.md')
->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('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)
@ -668,6 +705,9 @@ App::get('/v1/functions/:functionId/executions/:executionId')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getExecution')
->label('sdk.description', '/docs/references/functions/get-execution.md')
->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.')
->action(function ($functionId, $executionId, $response, $projectDB) {

View file

@ -13,6 +13,9 @@ App::get('/v1/locale')
->label('sdk.namespace', 'locale')
->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/locale/get-locale.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_LOCALE)
->action(function ($request, $response, $locale, $geodb) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
@ -71,6 +74,9 @@ App::get('/v1/locale/countries')
->label('sdk.namespace', 'locale')
->label('sdk.method', 'getCountries')
->label('sdk.description', '/docs/references/locale/get-countries.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_COUNTRY_LIST)
->action(function ($response, $locale) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
@ -98,6 +104,9 @@ App::get('/v1/locale/countries/eu')
->label('sdk.namespace', 'locale')
->label('sdk.method', 'getCountriesEU')
->label('sdk.description', '/docs/references/locale/get-countries-eu.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_COUNTRY_LIST)
->action(function ($response, $locale) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
@ -128,6 +137,9 @@ App::get('/v1/locale/countries/phones')
->label('sdk.namespace', 'locale')
->label('sdk.method', 'getCountriesPhones')
->label('sdk.description', '/docs/references/locale/get-countries-phones.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PHONE_LIST)
->action(function ($response, $locale) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
@ -159,6 +171,9 @@ App::get('/v1/locale/continents')
->label('sdk.namespace', 'locale')
->label('sdk.method', 'getContinents')
->label('sdk.description', '/docs/references/locale/get-continents.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_CONTINENT_LIST)
->action(function ($response, $locale) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Locale\Locale $locale */
@ -185,6 +200,9 @@ App::get('/v1/locale/currencies')
->label('sdk.namespace', 'locale')
->label('sdk.method', 'getCurrencies')
->label('sdk.description', '/docs/references/locale/get-currencies.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_CURRENCY_LIST)
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
@ -206,6 +224,9 @@ App::get('/v1/locale/languages')
->label('sdk.namespace', 'locale')
->label('sdk.method', 'getLanguages')
->label('sdk.description', '/docs/references/locale/get-languages.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_LANGUAGE_LIST)
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */

View file

@ -26,6 +26,9 @@ App::post('/v1/projects')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'create')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('name', null, new Text(128), 'Project name. Max length: 128 chars.')
->param('teamId', '', new UID(), 'Team unique ID.')
->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true)
@ -92,6 +95,9 @@ App::get('/v1/projects')
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'list')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->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, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
@ -124,6 +130,9 @@ App::get('/v1/projects/:projectId')
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'get')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->action(function ($projectId, $response, $consoleDB) {
/** @var Appwrite\Utopia\Response $response */
@ -314,6 +323,9 @@ App::patch('/v1/projects/:projectId')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'update')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('name', null, new Text(128), 'Project name. Max length: 128 chars.')
->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true)
@ -361,6 +373,9 @@ App::patch('/v1/projects/:projectId/oauth2')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateOAuth2')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'Provider Name', false)
->param('appId', '', new Text(256), 'Provider app ID. Max length: 256 chars.', true)
@ -393,6 +408,9 @@ App::delete('/v1/projects/:projectId')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'delete')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('password', '', new UID(), 'Your user password for confirmation. Must be between 6 to 32 chars.')
->action(function ($projectId, $password, $response, $user, $consoleDB, $deletes) {
@ -442,6 +460,9 @@ App::post('/v1/projects/:projectId/webhooks')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createWebhook')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_WEBHOOK)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.')
->param('events', null, new ArrayList(new WhiteList(array_keys(Config::getParam('events'), true), true)), 'Events list.')
@ -499,6 +520,9 @@ App::get('/v1/projects/:projectId/webhooks')
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listWebhooks')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_WEBHOOK_LIST)
->param('projectId', '', new UID(), 'Project unique ID.')
->action(function ($projectId, $response, $consoleDB) {
/** @var Appwrite\Utopia\Response $response */
@ -524,6 +548,9 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getWebhook')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_WEBHOOK)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('webhookId', null, new UID(), 'Webhook unique ID.')
->action(function ($projectId, $webhookId, $response, $consoleDB) {
@ -551,6 +578,9 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateWebhook')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_WEBHOOK)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('webhookId', null, new UID(), 'Webhook unique ID.')
->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.')
@ -599,6 +629,9 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteWebhook')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('webhookId', null, new UID(), 'Webhook unique ID.')
->action(function ($projectId, $webhookId, $response, $consoleDB) {
@ -632,6 +665,9 @@ App::post('/v1/projects/:projectId/keys')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createKey')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_KEY)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new ArrayList(new WhiteList(Config::getParam('scopes'), true)), 'Key scopes list.')
@ -680,6 +716,9 @@ App::get('/v1/projects/:projectId/keys')
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listKeys')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_KEY_LIST)
->param('projectId', null, new UID(), 'Project unique ID.')
->action(function ($projectId, $response, $consoleDB) {
/** @var Appwrite\Utopia\Response $response */
@ -705,6 +744,9 @@ App::get('/v1/projects/:projectId/keys/:keyId')
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getKey')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_KEY)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('keyId', null, new UID(), 'Key unique ID.')
->action(function ($projectId, $keyId, $response, $consoleDB) {
@ -729,6 +771,9 @@ App::put('/v1/projects/:projectId/keys/:keyId')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateKey')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_KEY)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('keyId', null, new UID(), 'Key unique ID.')
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
@ -767,6 +812,9 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteKey')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('keyId', null, new UID(), 'Key unique ID.')
->action(function ($projectId, $keyId, $response, $consoleDB) {
@ -800,6 +848,9 @@ App::post('/v1/projects/:projectId/tasks')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createTask')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TASK)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('name', null, new Text(128), 'Task name. Max length: 128 chars.')
->param('status', null, new WhiteList(['play', 'pause'], true), 'Task status.')
@ -875,6 +926,9 @@ App::get('/v1/projects/:projectId/tasks')
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listTasks')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TASK_LIST)
->param('projectId', '', new UID(), 'Project unique ID.')
->action(function ($projectId, $response, $consoleDB) {
/** @var Appwrite\Utopia\Response $response */
@ -901,6 +955,9 @@ App::get('/v1/projects/:projectId/tasks/:taskId')
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getTask')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TASK)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('taskId', null, new UID(), 'Task unique ID.')
->action(function ($projectId, $taskId, $response, $consoleDB) {
@ -928,6 +985,9 @@ App::put('/v1/projects/:projectId/tasks/:taskId')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateTask')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TASK)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('taskId', null, new UID(), 'Task unique ID.')
->param('name', null, new Text(128), 'Task name. Max length: 128 chars.')
@ -991,6 +1051,9 @@ App::delete('/v1/projects/:projectId/tasks/:taskId')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteTask')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('taskId', null, new UID(), 'Task unique ID.')
->action(function ($projectId, $taskId, $response, $consoleDB) {
@ -1024,6 +1087,9 @@ App::post('/v1/projects/:projectId/platforms')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createPlatform')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PLATFORM)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('type', null, new WhiteList(['web', 'flutter-ios', 'flutter-android', 'ios', 'android', 'unity'], true), 'Platform type.')
->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.')
@ -1079,6 +1145,9 @@ App::get('/v1/projects/:projectId/platforms')
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listPlatforms')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PLATFORM_LIST)
->param('projectId', '', new UID(), 'Project unique ID.')
->action(function ($projectId, $response, $consoleDB) {
/** @var Appwrite\Utopia\Response $response */
@ -1104,6 +1173,9 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getPlatform')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PLATFORM)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('platformId', null, new UID(), 'Platform unique ID.')
->action(function ($projectId, $platformId, $response, $consoleDB) {
@ -1131,6 +1203,9 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updatePlatform')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PLATFORM)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('platformId', null, new UID(), 'Platform unique ID.')
->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.')
@ -1174,6 +1249,9 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deletePlatform')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('platformId', null, new UID(), 'Platform unique ID.')
->action(function ($projectId, $platformId, $response, $consoleDB) {
@ -1207,6 +1285,9 @@ App::post('/v1/projects/:projectId/domains')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createDomain')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DOMAIN)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('domain', null, new DomainValidator(), 'Domain name.')
->action(function ($projectId, $domain, $response, $consoleDB) {
@ -1271,6 +1352,9 @@ App::get('/v1/projects/:projectId/domains')
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listDomains')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DOMAIN_LIST)
->param('projectId', '', new UID(), 'Project unique ID.')
->action(function ($projectId, $response, $consoleDB) {
/** @var Appwrite\Utopia\Response $response */
@ -1296,6 +1380,9 @@ App::get('/v1/projects/:projectId/domains/:domainId')
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getDomain')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DOMAIN)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('domainId', null, new UID(), 'Domain unique ID.')
->action(function ($projectId, $domainId, $response, $consoleDB) {
@ -1323,6 +1410,9 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateDomainVerification')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DOMAIN)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('domainId', null, new UID(), 'Domain unique ID.')
->action(function ($projectId, $domainId, $response, $consoleDB) {
@ -1381,6 +1471,9 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteDomain')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->param('projectId', null, new UID(), 'Project unique ID.')
->param('domainId', null, new UID(), 'Domain unique ID.')
->action(function ($projectId, $domainId, $response, $consoleDB) {

View file

@ -32,8 +32,11 @@ App::post('/v1/storage/files')
->label('sdk.namespace', 'storage')
->label('sdk.method', 'createFile')
->label('sdk.description', '/docs/references/storage/create-file.md')
->label('sdk.consumes', 'multipart/form-data')
->label('sdk.request.type', 'multipart/form-data')
->label('sdk.methodType', 'upload')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE)
->param('file', [], new File(), 'Binary file.', false)
->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.')
@ -46,12 +49,10 @@ App::post('/v1/storage/files')
/** @var Appwrite\Event\Event $usage */
$file = $request->getFiles('file');
$read = (empty($read)) ? ['user:'.$user->getId()] : $read;
$write = (empty($write)) ? ['user:'.$user->getId()] : $write;
/*
* Validators
*/
* Validators
*/
//$fileType = new FileType(array(FileType::FILE_TYPE_PNG, FileType::FILE_TYPE_GIF, FileType::FILE_TYPE_JPEG));
$fileSize = new FileSize(App::getEnv('_APP_STORAGE_LIMIT', 0));
$upload = new Upload();
@ -163,6 +164,9 @@ App::get('/v1/storage/files')
->label('sdk.namespace', 'storage')
->label('sdk.method', 'listFiles')
->label('sdk.description', '/docs/references/storage/list-files.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->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, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
@ -197,6 +201,9 @@ App::get('/v1/storage/files/:fileId')
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getFile')
->label('sdk.description', '/docs/references/storage/get-file.md')
->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.')
->action(function ($fileId, $response, $projectDB) {
/** @var Appwrite\Utopia\Response $response */
@ -219,7 +226,8 @@ App::get('/v1/storage/files/:fileId/preview')
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getFilePreview')
->label('sdk.description', '/docs/references/storage/get-file-preview.md')
->label('sdk.response.type', 'image/*')
->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('width', 0, new Range(0, 4000), 'Resize preview image width, Pass an integer between 0 to 4000.', true)
@ -345,7 +353,8 @@ App::get('/v1/storage/files/:fileId/download')
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getFileDownload')
->label('sdk.description', '/docs/references/storage/get-file-download.md')
->label('sdk.response.type', '*')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', '*/*')
->label('sdk.methodType', 'location')
->param('fileId', '', new UID(), 'File unique ID.')
->action(function ($fileId, $response, $projectDB) {
@ -400,7 +409,8 @@ App::get('/v1/storage/files/:fileId/view')
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getFileView')
->label('sdk.description', '/docs/references/storage/get-file-view.md')
->label('sdk.response.type', '*')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', '*/*')
->label('sdk.methodType', 'location')
->param('fileId', '', new UID(), 'File unique ID.')
->param('as', '', new WhiteList(['pdf', /*'html',*/ 'text'], true), 'Choose a file format to convert your file to. Currently you can only convert word and pdf files to pdf or txt. This option is currently experimental only, use at your own risk.', true)
@ -474,6 +484,9 @@ App::put('/v1/storage/files/:fileId')
->label('sdk.namespace', 'storage')
->label('sdk.method', 'updateFile')
->label('sdk.description', '/docs/references/storage/update-file.md')
->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.')
@ -517,11 +530,14 @@ App::delete('/v1/storage/files/:fileId')
->label('sdk.namespace', 'storage')
->label('sdk.method', 'deleteFile')
->label('sdk.description', '/docs/references/storage/delete-file.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->param('fileId', '', new UID(), 'File unique ID.')
->action(function ($fileId, $response, $projectDB, $webhooks, $audits, $usage) {
->action(function ($fileId, $response, $projectDB, $events, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $usage */
@ -548,12 +564,12 @@ App::delete('/v1/storage/files/:fileId')
->setParam('storage', $file->getAttribute('size', 0) * -1)
;
$webhooks
$events
->setParam('payload', $response->output($file, Response::MODEL_FILE))
;
$response->noContent();
}, ['response', 'projectDB', 'webhooks', 'audits', 'usage']);
}, ['response', 'projectDB', 'events', 'audits', 'usage']);
// App::get('/v1/storage/files/:fileId/scan')
// ->desc('Scan Storage')

View file

@ -23,21 +23,27 @@ use DeviceDetector\DeviceDetector;
App::post('/v1/teams')
->desc('Create Team')
->groups(['api', 'teams'])
->label('event', 'teams.create')
->label('scope', 'teams.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'teams')
->label('sdk.method', 'create')
->label('sdk.description', '/docs/references/teams/create-team.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEAM)
->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)
->action(function ($name, $roles, $response, $user, $projectDB, $mode) {
->action(function ($name, $roles, $response, $user, $projectDB) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var bool $mode */
Authorization::disable();
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
$team = $projectDB->createDocument([
'$collection' => Database::SYSTEM_COLLECTION_TEAMS,
'$permissions' => [
@ -45,7 +51,7 @@ App::post('/v1/teams')
'write' => ['team:{self}/owner'],
],
'name' => $name,
'sum' => ($mode !== APP_MODE_ADMIN && $user->getId()) ? 1 : 0,
'sum' => ($isPreviliggedUser || $isAppUser) ? 0 : 1,
'dateCreated' => \time(),
]);
@ -55,7 +61,7 @@ App::post('/v1/teams')
throw new Exception('Failed saving team to DB', 500);
}
if ($mode !== APP_MODE_ADMIN && $user->getId()) { // Don't add user on server mode
if (!$isPreviliggedUser && !$isAppUser) { // Don't add user on server mode
$membership = new Document([
'$collection' => Database::SYSTEM_COLLECTION_MEMBERSHIPS,
'$permissions' => [
@ -85,7 +91,7 @@ App::post('/v1/teams')
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($team, Response::MODEL_TEAM)
;
}, ['response', 'user', 'projectDB', 'mode']);
}, ['response', 'user', 'projectDB']);
App::get('/v1/teams')
->desc('List Teams')
@ -95,6 +101,9 @@ App::get('/v1/teams')
->label('sdk.namespace', 'teams')
->label('sdk.method', 'list')
->label('sdk.description', '/docs/references/teams/list-teams.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEAM_LIST)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
@ -129,6 +138,9 @@ App::get('/v1/teams/:teamId')
->label('sdk.namespace', 'teams')
->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/teams/get-team.md')
->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.')
->action(function ($teamId, $response, $projectDB) {
/** @var Appwrite\Utopia\Response $response */
@ -146,11 +158,15 @@ App::get('/v1/teams/:teamId')
App::put('/v1/teams/:teamId')
->desc('Update Team')
->groups(['api', 'teams'])
->label('event', 'teams.update')
->label('scope', 'teams.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'teams')
->label('sdk.method', 'update')
->label('sdk.description', '/docs/references/teams/update-team.md')
->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('name', null, new Text(128), 'Team name. Max length: 128 chars.')
->action(function ($teamId, $name, $response, $projectDB) {
@ -177,15 +193,20 @@ App::put('/v1/teams/:teamId')
App::delete('/v1/teams/:teamId')
->desc('Delete Team')
->groups(['api', 'teams'])
->label('event', 'teams.delete')
->label('scope', 'teams.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'teams')
->label('sdk.method', 'delete')
->label('sdk.description', '/docs/references/teams/delete-team.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->param('teamId', '', new UID(), 'Team unique ID.')
->action(function ($teamId, $response, $projectDB) {
->action(function ($teamId, $response, $projectDB, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $events */
$team = $projectDB->getDocument($teamId);
@ -212,30 +233,40 @@ App::delete('/v1/teams/:teamId')
throw new Exception('Failed to remove team from DB', 500);
}
$events
->setParam('payload', $response->output($team, Response::MODEL_TEAM))
;
$response->noContent();
}, ['response', 'projectDB']);
}, ['response', 'projectDB', 'events']);
App::post('/v1/teams/:teamId/memberships')
->desc('Create Team Membership')
->groups(['api', 'teams'])
->label('event', 'teams.memberships.create')
->label('scope', 'teams.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'teams')
->label('sdk.method', 'createMembership')
->label('sdk.description', '/docs/references/teams/create-team-membership.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
->param('teamId', '', new UID(), 'Team unique ID.')
->param('email', '', new Email(), 'New team member email.')
->param('name', '', new Text(128), 'New team member name. Max length: 128 chars.', true)
->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
->action(function ($teamId, $email, $name, $roles, $url, $response, $project, $user, $projectDB, $locale, $audits, $mails, $mode) {
->action(function ($teamId, $email, $name, $roles, $url, $response, $project, $user, $projectDB, $locale, $audits, $mails) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Document $user */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $mails */
/** @var bool $mode */
$isPreviliggedUser = Auth::isPreviliggedUser(Authorization::$roles);
$isAppUser = Auth::isAppUser(Authorization::$roles);
$name = (empty($name)) ? $email : $name;
$team = $projectDB->getDocument($teamId);
@ -305,7 +336,7 @@ App::post('/v1/teams/:teamId/memberships')
}
}
if (!$isOwner && APP_MODE_ADMIN !== $mode && $user->getId()) { // Not owner, not admin, not app (server)
if (!$isOwner && !$isPreviliggedUser && !$isAppUser) { // Not owner, not admin, not app (server)
throw new Exception('User is not allowed to send invitations for this team', 401);
}
@ -321,12 +352,12 @@ App::post('/v1/teams/:teamId/memberships')
'teamId' => $team->getId(),
'roles' => $roles,
'invited' => \time(),
'joined' => (APP_MODE_ADMIN === $mode || !$user->getId()) ? \time() : 0,
'confirm' => (APP_MODE_ADMIN === $mode || !$user->getId()),
'joined' => ($isPreviliggedUser || $isAppUser) ? \time() : 0,
'confirm' => ($isPreviliggedUser || $isAppUser),
'secret' => Auth::hash($secret),
]);
if (APP_MODE_ADMIN === $mode || !$user->getId()) { // Allow admin to create membership
if ($isPreviliggedUser || $isAppUser) { // Allow admin to create membership
Authorization::disable();
$membership = $projectDB->createDocument($membership->getArrayCopy());
@ -377,7 +408,7 @@ App::post('/v1/teams/:teamId/memberships')
->setParam('{{text-cta}}', '#ffffff')
;
if (APP_MODE_ADMIN !== $mode && $user->getId()) { // No need in comfirmation when in admin or app mode
if (!$isPreviliggedUser && !$isAppUser) { // No need in comfirmation when in admin or app mode
$mails
->setParam('event', 'teams.membership.create')
->setParam('from', ($project->getId() === 'console') ? '' : \sprintf($locale->getText('account.emails.team'), $project->getAttribute('name')))
@ -402,7 +433,7 @@ App::post('/v1/teams/:teamId/memberships')
'name' => $name,
])), Response::MODEL_MEMBERSHIP)
;
}, ['response', 'project', 'user', 'projectDB', 'locale', 'audits', 'mails', 'mode']);
}, ['response', 'project', 'user', 'projectDB', 'locale', 'audits', 'mails']);
App::get('/v1/teams/:teamId/memberships')
->desc('Get Team Memberships')
@ -412,6 +443,9 @@ App::get('/v1/teams/:teamId/memberships')
->label('sdk.namespace', 'teams')
->label('sdk.method', 'getMemberships')
->label('sdk.description', '/docs/references/teams/get-team-members.md')
->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('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)
@ -458,11 +492,15 @@ App::get('/v1/teams/:teamId/memberships')
App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
->desc('Update Team Membership Status')
->groups(['api', 'teams'])
->label('event', 'teams.memberships.update.status')
->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'teams')
->label('sdk.method', 'updateMembershipStatus')
->label('sdk.description', '/docs/references/teams/update-team-membership-status.md')
->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('inviteId', '', new UID(), 'Invite unique ID.')
->param('userId', '', new UID(), 'User unique ID.')
@ -553,8 +591,9 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
$session = new Document([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$permissions' => ['read' => ['user:'.$user->getId()], 'write' => ['user:'.$user->getId()]],
'userId' => $user->getId(),
'type' => Auth::TOKEN_TYPE_LOGIN,
'secret' => Auth::hash($secret), // On way hash encryption to protect DB leak
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
'expire' => $expiry,
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
@ -633,17 +672,22 @@ App::patch('/v1/teams/:teamId/memberships/:inviteId/status')
App::delete('/v1/teams/:teamId/memberships/:inviteId')
->desc('Delete Team Membership')
->groups(['api', 'teams'])
->label('event', 'teams.memberships.delete')
->label('scope', 'teams.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'teams')
->label('sdk.method', 'deleteMembership')
->label('sdk.description', '/docs/references/teams/delete-team-membership.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->param('teamId', '', new UID(), 'Team unique ID.')
->param('inviteId', '', new UID(), 'Invite unique ID.')
->action(function ($teamId, $inviteId, $response, $projectDB, $audits) {
->action(function ($teamId, $inviteId, $response, $projectDB, $audits, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $events */
$membership = $projectDB->getDocument($inviteId);
@ -681,5 +725,9 @@ App::delete('/v1/teams/:teamId/memberships/:inviteId')
->setParam('resource', 'teams/'.$teamId)
;
$events
->setParam('payload', $response->output($membership, Response::MODEL_MEMBERSHIP))
;
$response->noContent();
}, ['response', 'projectDB', 'audits']);
}, ['response', 'projectDB', 'audits', 'events']);

View file

@ -27,6 +27,9 @@ App::post('/v1/users')
->label('sdk.namespace', 'users')
->label('sdk.method', 'create')
->label('sdk.description', '/docs/references/users/create-user.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('email', '', new Email(), 'User email.')
->param('password', '', new Password(), 'User password. Must be between 6 to 32 chars.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
@ -80,6 +83,9 @@ App::get('/v1/users')
->label('sdk.namespace', 'users')
->label('sdk.method', 'list')
->label('sdk.description', '/docs/references/users/list-users.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER_LIST)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
@ -114,6 +120,9 @@ App::get('/v1/users/:userId')
->label('sdk.namespace', 'users')
->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/users/get-user.md')
->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.')
->action(function ($userId, $response, $projectDB) {
/** @var Appwrite\Utopia\Response $response */
@ -136,6 +145,9 @@ App::get('/v1/users/:userId/prefs')
->label('sdk.namespace', 'users')
->label('sdk.method', 'getPrefs')
->label('sdk.description', '/docs/references/users/get-user-prefs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ANY)
->param('userId', '', new UID(), 'User unique ID.')
->action(function ($userId, $response, $projectDB) {
/** @var Appwrite\Utopia\Response $response */
@ -149,7 +161,7 @@ App::get('/v1/users/:userId/prefs')
$prefs = $user->getAttribute('prefs', '');
$response->json($prefs);
$response->dynamic(new Document($prefs), Response::MODEL_ANY);
}, ['response', 'projectDB']);
App::get('/v1/users/:userId/sessions')
@ -160,6 +172,9 @@ App::get('/v1/users/:userId/sessions')
->label('sdk.namespace', 'users')
->label('sdk.method', 'getSessions')
->label('sdk.description', '/docs/references/users/get-user-sessions.md')
->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.')
->action(function ($userId, $response, $projectDB, $locale) {
/** @var Appwrite\Utopia\Response $response */
@ -203,6 +218,9 @@ App::get('/v1/users/:userId/logs')
->label('sdk.namespace', 'users')
->label('sdk.method', 'getLogs')
->label('sdk.description', '/docs/references/users/get-user-logs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_LOG_LIST)
->param('userId', '', new UID(), 'User unique ID.')
->action(function ($userId, $response, $register, $project, $projectDB, $locale, $geodb) {
/** @var Appwrite\Utopia\Response $response */
@ -309,6 +327,9 @@ App::patch('/v1/users/:userId/status')
->label('sdk.namespace', 'users')
->label('sdk.method', 'updateStatus')
->label('sdk.description', '/docs/references/users/update-user-status.md')
->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', '', new WhiteList([Auth::USER_STATUS_ACTIVATED, Auth::USER_STATUS_BLOCKED, Auth::USER_STATUS_UNACTIVATED], true), 'User Status code. To activate the user pass '.Auth::USER_STATUS_ACTIVATED.', to block the user pass '.Auth::USER_STATUS_BLOCKED.' and for disabling the user pass '.Auth::USER_STATUS_UNACTIVATED)
->action(function ($userId, $status, $response, $projectDB) {
@ -340,6 +361,9 @@ App::patch('/v1/users/:userId/prefs')
->label('sdk.namespace', 'users')
->label('sdk.method', 'updatePrefs')
->label('sdk.description', '/docs/references/users/update-user-prefs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_ANY)
->param('userId', '', new UID(), 'User unique ID.')
->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.')
->action(function ($userId, $prefs, $response, $projectDB) {
@ -360,7 +384,7 @@ App::patch('/v1/users/:userId/prefs')
throw new Exception('Failed saving user to DB', 500);
}
$response->json($prefs);
$response->dynamic(new Document($prefs), Response::MODEL_ANY);
}, ['response', 'projectDB']);
App::delete('/v1/users/:userId/sessions/:sessionId')
@ -372,13 +396,16 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
->label('sdk.namespace', 'users')
->label('sdk.method', 'deleteSession')
->label('sdk.description', '/docs/references/users/delete-user-session.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->label('abuse-limit', 100)
->param('userId', '', new UID(), 'User unique ID.')
->param('sessionId', null, new UID(), 'User unique session ID.')
->action(function ($userId, $sessionId, $response, $projectDB, $webhooks) {
->action(function ($userId, $sessionId, $response, $projectDB, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $events */
$user = $projectDB->getDocument($userId);
@ -394,14 +421,14 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
throw new Exception('Failed to remove token from DB', 500);
}
$webhooks
$events
->setParam('payload', $response->output($user, Response::MODEL_USER))
;
}
}
$response->noContent();
}, ['response', 'projectDB', 'webhooks']);
}, ['response', 'projectDB', 'events']);
App::delete('/v1/users/:userId/sessions')
->desc('Delete User Sessions')
@ -412,12 +439,15 @@ App::delete('/v1/users/:userId/sessions')
->label('sdk.namespace', 'users')
->label('sdk.method', 'deleteSessions')
->label('sdk.description', '/docs/references/users/delete-user-sessions.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->label('abuse-limit', 100)
->param('userId', '', new UID(), 'User unique ID.')
->action(function ($userId, $response, $projectDB, $webhooks) {
->action(function ($userId, $response, $projectDB, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $events */
$user = $projectDB->getDocument($userId);
@ -433,12 +463,12 @@ App::delete('/v1/users/:userId/sessions')
}
}
$webhooks
$events
->setParam('payload', $response->output($user, Response::MODEL_USER))
;
$response->noContent();
}, ['response', 'projectDB', 'webhooks']);
}, ['response', 'projectDB', 'events']);
App::delete('/v1/users/:userId')
->desc('Delete User')
@ -449,12 +479,15 @@ App::delete('/v1/users/:userId')
->label('sdk.namespace', 'users')
->label('sdk.method', 'deleteUser')
->label('sdk.description', '/docs/references/users/delete-user.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_NONE)
->label('abuse-limit', 100)
->param('userId', '', function () {return new UID();}, 'User unique ID.')
->action(function ($userId, $response, $projectDB, $webhooks, $deletes) {
->action(function ($userId, $response, $projectDB, $events, $deletes) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Database $projectDB */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $deletes */
$user = $projectDB->getDocument($userId);
@ -486,9 +519,9 @@ App::delete('/v1/users/:userId')
->setParam('document', $user)
;
$webhooks
$events
->setParam('payload', $response->output($user, Response::MODEL_USER))
;
$response->noContent();
}, ['response', 'projectDB', 'webhooks', 'deletes']);
}, ['response', 'projectDB', 'events', 'deletes']);

View file

@ -22,21 +22,19 @@ Config::setParam('domainVerification', false);
Config::setParam('cookieDomain', 'localhost');
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
App::init(function ($utopia, $request, $response, $console, $project, $user, $locale, $webhooks, $audits, $usage, $deletes, $clients) {
App::init(function ($utopia, $request, $response, $console, $project, $user, $locale, $events, $audits, $usage, $deletes, $clients) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $console */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Database\Document $user */
/** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $usage */
/** @var Appwrite\Event\Event $deletes */
/** @var bool $mode */
/** @var array $clients */
Authorization::$roles = ['*'];
$localeParam = (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', ''));
@ -172,7 +170,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
*/
if (null !== $key && $user->isEmpty()) {
$user = new Document([
'$id' => 0,
'$id' => '',
'status' => Auth::USER_STATUS_ACTIVATED,
'email' => 'app.'.$project->getId().'@service.'.$request->getHostname(),
'password' => '',
@ -222,8 +220,9 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
/*
* Background Jobs
*/
$webhooks
$events
->setParam('projectId', $project->getId())
->setParam('userId', $user->getId())
->setParam('event', $route->getLabel('event', ''))
->setParam('payload', [])
;
@ -251,25 +250,33 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo
$deletes
->setParam('projectId', $project->getId())
;
}, ['utopia', 'request', 'response', 'console', 'project', 'user', 'locale', 'webhooks', 'audits', 'usage', 'deletes', 'clients']);
}, ['utopia', 'request', 'response', 'console', 'project', 'user', 'locale', 'events', 'audits', 'usage', 'deletes', 'clients']);
App::shutdown(function ($utopia, $request, $response, $project, $webhooks, $audits, $usage, $deletes, $mode) {
App::shutdown(function ($utopia, $request, $response, $project, $events, $audits, $usage, $deletes, $mode) {
/** @var Utopia\App $utopia */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Database\Document $project */
/** @var Appwrite\Event\Event $webhooks */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $usage */
/** @var Appwrite\Event\Event $deletes */
/** @var bool $mode */
if (!empty($webhooks->getParam('event'))) {
if(empty($webhooks->getParam('payload'))) {
$webhooks->setParam('payload', $response->getPayload());
if (!empty($events->getParam('event'))) {
if(empty($events->getParam('payload'))) {
$events->setParam('payload', $response->getPayload());
}
$webhooks->trigger();
$events
->setQueue('v1-webhooks')
->setClass('WebhooksV1')
->trigger();
$events
->setQueue('v1-functions')
->setClass('FunctionsV1')
->trigger();
}
if (!empty($audits->getParam('event'))) {
@ -283,8 +290,8 @@ App::shutdown(function ($utopia, $request, $response, $project, $webhooks, $audi
$route = $utopia->match($request);
if ($project->getId()
&& $mode !== APP_MODE_ADMIN
&& !empty($route->getLabel('sdk.namespace', null))) { // Don't calculate console usage and admin mode
&& $mode !== APP_MODE_ADMIN //TODO: add check to make sure user is admin
&& !empty($route->getLabel('sdk.namespace', null))) { // Don't calculate console usage on admin mode
$usage
->setParam('networkRequestSize', $request->getSize() + $usage->getParam('storage'))
@ -292,7 +299,7 @@ App::shutdown(function ($utopia, $request, $response, $project, $webhooks, $audi
->trigger()
;
}
}, ['utopia', 'request', 'response', 'project', 'webhooks', 'audits', 'usage', 'deletes', 'mode']);
}, ['utopia', 'request', 'response', 'project', 'events', 'audits', 'usage', 'deletes', 'mode']);
App::options(function ($request, $response) {
/** @var Utopia\Swoole\Request $request */

View file

@ -157,7 +157,7 @@ App::post('/v1/mock/tests/general/upload')
->label('sdk.namespace', 'general')
->label('sdk.method', 'upload')
->label('sdk.description', 'Mock a delete request for SDK tests')
->label('sdk.consumes', 'multipart/form-data')
->label('sdk.request.type', 'multipart/form-data')
->label('sdk.mock', true)
->param('x', '', new Text(100), 'Sample string param')
->param('y', '', new Numeric(), 'Sample numeric param')

View file

@ -1,11 +1,13 @@
<?php
use Appwrite\Template\Template;
use Appwrite\Specification\Format\OpenAPI3;
use Appwrite\Specification\Format\Swagger2;
use Appwrite\Specification\Specification;
use Utopia\App;
use Utopia\View;
use Utopia\Config\Config;
use Utopia\Exception;
use Utopia\Validator\WhiteList;
use Utopia\Validator\Range;
App::init(function ($layout) {
/** @var Utopia\View $layout */
@ -180,30 +182,26 @@ App::get('/error/:code')
->setParam('body', $page);
}, ['layout']);
App::get('/open-api-2.json')
App::get('/specs/:format')
->groups(['web', 'home'])
->label('scope', 'public')
->label('docs', false)
->param('format', 'swagger2', new WhiteList(['swagger2', 'open-api3'], true), 'Spec format.', true)
->param('platform', APP_PLATFORM_CLIENT, new WhiteList([APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER, APP_PLATFORM_CONSOLE], true), 'Choose target platform.', true)
->param('extensions', 0, new Range(0, 1), 'Show extra data.', true)
->param('tests', 0, new Range(0, 1), 'Include only test services.', true)
->action(function ($platform, $extensions, $tests, $utopia, $request, $response) {
->action(function ($format, $platform, $utopia, $request, $response) {
/** @var Utopia\App $utopia */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
$security = [
APP_PLATFORM_CLIENT => ['Project' => []],
APP_PLATFORM_SERVER => ['Project' => [], 'Key' => []],
APP_PLATFORM_CONSOLE => ['Project' => [], 'Key' => []],
];
$platforms = [
'client' => APP_PLATFORM_CLIENT,
'server' => APP_PLATFORM_SERVER,
'all' => APP_PLATFORM_CONSOLE,
'console' => APP_PLATFORM_CONSOLE,
];
$routes = [];
$models = [];
$keys = [
APP_PLATFORM_CLIENT => [
'Project' => [
@ -267,98 +265,14 @@ App::get('/open-api-2.json')
],
];
/*
* Specifications (v3.0.0):
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
*/
$output = [
'swagger' => '2.0',
'info' => [
'version' => APP_VERSION_STABLE,
'title' => APP_NAME,
'description' => 'Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)',
'termsOfService' => 'https://appwrite.io/policy/terms',
'contact' => [
'name' => 'Appwrite Team',
'url' => 'https://appwrite.io/support',
'email' => App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM),
],
'license' => [
'name' => 'BSD-3-Clause',
'url' => 'https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE',
],
],
'host' => \parse_url(App::getEnv('_APP_HOME', $request->getHostname()), PHP_URL_HOST),
'basePath' => '/v1',
'schemes' => ['https'],
'consumes' => ['application/json', 'multipart/form-data'],
'produces' => ['application/json'],
'securityDefinitions' => $keys[$platform],
'paths' => [],
'definitions' => [
// 'Pet' => [
// 'required' => ['id', 'name'],
// 'properties' => [
// 'id' => [
// 'type' => 'integer',
// 'format' => 'int64',
// ],
// 'name' => [
// 'type' => 'string',
// ],
// 'tag' => [
// 'type' => 'string',
// ],
// ],
// ],
// 'Pets' => array(
// 'type' => 'array',
// 'items' => array(
// '$ref' => '#/definitions/Pet',
// ),
// ),
'Error' => array(
'required' => array(
0 => 'code',
1 => 'message',
),
'properties' => array(
'code' => array(
'type' => 'integer',
'format' => 'int32',
),
'message' => array(
'type' => 'string',
),
),
),
],
'externalDocs' => [
'description' => 'Full API docs, specs and tutorials',
'url' => $request->getProtocol().'://'.$request->getHostname().'/docs',
],
$security = [
APP_PLATFORM_CLIENT => ['Project' => []],
APP_PLATFORM_SERVER => ['Project' => [], 'Key' => []],
APP_PLATFORM_CONSOLE => ['Project' => [], 'Key' => []],
];
if ($extensions) {
if (isset($output['securityDefinitions']['Project'])) {
$output['securityDefinitions']['Project']['extensions'] = ['demo' => '5df5acd0d48c2'];
}
if (isset($output['securityDefinitions']['Key'])) {
$output['securityDefinitions']['Key']['extensions'] = ['demo' => '919c2d18fb5d4...a2ae413da83346ad2'];
}
if (isset($output['securityDefinitions']['Locale'])) {
$output['securityDefinitions']['Locale']['extensions'] = ['demo' => 'en'];
}
if (isset($output['securityDefinitions']['Mode'])) {
$output['securityDefinitions']['Mode']['extensions'] = ['demo' => ''];
}
}
foreach ($utopia->getRoutes() as $key => $method) {
foreach ($method as $route) { /* @var $route \Utopia\Route */
foreach ($method as $route) { /** @var \Utopia\Route $route */
if (!$route->getLabel('docs', true)) {
continue;
}
@ -375,184 +289,56 @@ App::get('/open-api-2.json')
continue;
}
$url = \str_replace('/v1', '', $route->getURL());
$scope = $route->getLabel('scope', '');
$hide = $route->getLabel('sdk.hide', false);
$consumes = ['application/json'];
if ($hide) {
continue;
$routes[] = $route;
$model = $response->getModel($route->getLabel('sdk.response.model', 'none'));
if($model) {
$models[$model->getType()] = $model;
}
$desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath(__DIR__.'/../../../'.$route->getLabel('sdk.description', '')) : null;
$temp = [
'summary' => $route->getDesc(),
'operationId' => $route->getLabel('sdk.method', \uniqid()),
'consumes' => [],
'tags' => [$route->getLabel('sdk.namespace', 'default')],
'description' => ($desc) ? \file_get_contents($desc) : '',
// 'responses' => [
// 200 => [
// 'description' => 'An paged array of pets',
// 'schema' => [
// '$ref' => '#/definitions/Pet',
// ],
// ],
// ],
];
if ($extensions) {
$platformList = $route->getLabel('sdk.platform', []);
$temp['extensions'] = [
'weight' => $route->getOrder(),
'cookies' => $route->getLabel('sdk.cookies', false),
'type' => $route->getLabel('sdk.methodType', ''),
'demo' => 'docs/examples/'. Template::fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')).'/'.Template::fromCamelCaseToDash($temp['operationId']).'.md',
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $route->getLabel('sdk.description', ''),
'rate-limit' => $route->getLabel('abuse-limit', 0),
'rate-time' => $route->getLabel('abuse-time', 3600),
'rate-key' => $route->getLabel('abuse-key', 'url:{url},ip:{ip}'),
'scope' => $route->getLabel('scope', ''),
'platforms' => $platformList,
];
}
if ((!empty($scope))) { // && 'public' != $scope
$temp['security'][] = $route->getLabel('sdk.security', $security[$platform]);
}
$requestBody = [
'content' => [
'application/x-www-form-urlencoded' => [
'schema' => [
'type' => 'object',
'properties' => [],
],
'required' => [],
],
],
];
foreach ($route->getParams() as $name => $param) {
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $utopia->getResources($param['resources'])) : $param['validator']; /* @var $validator \Utopia\Validator */
$node = [
'name' => $name,
'description' => $param['description'],
'required' => !$param['optional'],
];
switch ((!empty($validator)) ? \get_class($validator) : '') {
case 'Utopia\Validator\Text':
$node['type'] = 'string';
$node['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
break;
case 'Utopia\Validator\Boolean':
$node['type'] = 'boolean';
$node['x-example'] = false;
break;
case 'Appwrite\Database\Validator\UID':
$node['type'] = 'string';
$node['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
break;
case 'Utopia\Validator\Email':
$node['type'] = 'string';
$node['format'] = 'email';
$node['x-example'] = 'email@example.com';
break;
case 'Utopia\Validator\URL':
$node['type'] = 'string';
$node['format'] = 'url';
$node['x-example'] = 'https://example.com';
break;
case 'Utopia\Validator\JSON':
case 'Utopia\Validator\Mock':
case 'Utopia\Validator\Assoc':
$node['type'] = 'object';
$node['type'] = 'object';
$node['x-example'] = '{}';
//$node['format'] = 'json';
break;
case 'Appwrite\Storage\Validator\File':
$consumes = ['multipart/form-data'];
$node['type'] = 'file';
break;
case 'Utopia\Validator\ArrayList':
$node['type'] = 'array';
$node['collectionFormat'] = 'multi';
$node['items'] = [
'type' => 'string',
];
break;
case 'Appwrite\Auth\Validator\Password':
$node['type'] = 'string';
$node['format'] = 'format';
$node['x-example'] = 'password';
break;
case 'Utopia\Validator\Range': /* @var $validator \Utopia\Validator\Range */
$node['type'] = 'integer';
$node['format'] = 'int32';
$node['x-example'] = $validator->getMin();
break;
case 'Utopia\Validator\Numeric':
$node['type'] = 'integer';
$node['format'] = 'int32';
break;
case 'Utopia\Validator\Length':
$node['type'] = 'string';
break;
case 'Utopia\Validator\Host':
$node['type'] = 'string';
$node['format'] = 'url';
$node['x-example'] = 'https://example.com';
break;
case 'Utopia\Validator\WhiteList': /* @var $validator \Utopia\Validator\WhiteList */
$node['type'] = 'string';
$node['x-example'] = $validator->getList()[0];
break;
default:
$node['type'] = 'string';
break;
}
if ($param['optional'] && !\is_null($param['default'])) { // Param has default value
$node['default'] = $param['default'];
}
if (false !== \strpos($url, ':'.$name)) { // Param is in URL path
$node['in'] = 'path';
$temp['parameters'][] = $node;
} elseif ($key == 'GET') { // Param is in query
$node['in'] = 'query';
$temp['parameters'][] = $node;
} else { // Param is in payload
$node['in'] = 'formData';
$temp['parameters'][] = $node;
$requestBody['content']['application/x-www-form-urlencoded']['schema']['properties'][] = $node;
if (!$param['optional']) {
$requestBody['content']['application/x-www-form-urlencoded']['required'][] = $name;
}
}
$url = \str_replace(':'.$name, '{'.$name.'}', $url);
}
$temp['consumes'] = $consumes;
$output['paths'][$url][\strtolower($route->getMethod())] = $temp;
}
}
/*foreach ($consoleDB->getMocks() as $mock) {
var_dump($mock['name']);
}*/
$models = $response->getModels();
\ksort($output['paths']);
foreach ($models as $key => $value) {
if($platform !== APP_PLATFORM_CONSOLE && !$value->isPublic()) {
unset($models[$key]);
}
}
switch ($format) {
case 'swagger2':
$format = new Swagger2($utopia, $routes, $models, $keys[$platform], $security[$platform]);
break;
case 'open-api3':
$format = new OpenAPI3($utopia, $routes, $models, $keys[$platform], $security[$platform]);
break;
default:
throw new Exception('Format not found', 404);
break;
}
$specs = new Specification($format);
$format
->setParam('name', APP_NAME)
->setParam('description', 'Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)')
->setParam('endpoint', App::getEnv('_APP_HOME', $request->getProtocol().'://'.$request->getHostname()).'/v1')
->setParam('version', APP_VERSION_STABLE)
->setParam('terms', App::getEnv('_APP_HOME', $request->getProtocol().'://'.$request->getHostname()).'/policy/terms')
->setParam('support.email', App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM))
->setParam('support.url', App::getEnv('_APP_HOME', $request->getProtocol().'://'.$request->getHostname()).'/support')
->setParam('contact.name', APP_NAME.' Team')
->setParam('contact.email', App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM))
->setParam('contact.url', App::getEnv('_APP_HOME', $request->getProtocol().'://'.$request->getHostname()).'/support')
->setParam('license.name', 'BSD-3-Clause')
->setParam('license.url', 'https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE')
->setParam('docs.description', 'Full API docs, specs and tutorials')
->setParam('docs.url', App::getEnv('_APP_HOME', $request->getProtocol().'://'.$request->getHostname()).'/docs')
;
$response
->json($output);
->json($specs->parse());
}, ['utopia', 'request', 'response']);

View file

@ -2,6 +2,7 @@
require_once __DIR__.'/../vendor/autoload.php';
use Appwrite\Database\Validator\Authorization;
use Utopia\Swoole\Files;
use Utopia\Swoole\Request;
use Appwrite\Utopia\Response;
@ -19,8 +20,6 @@ ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
sleep(2);
$http = new Server("0.0.0.0", 80);
$payloadSize = max(4000000 /* 4mb */, App::getEnv('_APP_STORAGE_LIMIT', 10000000 /* 10mb */));
@ -96,6 +95,9 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
$app = new App('America/New_York');
try {
Authorization::cleanRoles();
Authorization::setRole('*');
$app->run($request, $response);
} catch (\Throwable $th) {
Console::error('[Error] Type: '.get_class($th));

View file

@ -34,9 +34,10 @@ const APP_DOMAIN = 'appwrite.io';
const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address
const APP_EMAIL_SECURITY = 'security@localhost.test'; // Default security email address
const APP_USERAGENT = APP_NAME.'-Server v%s. Please report abuse at %s';
const APP_MODE_DEFAULT = 'default';
const APP_MODE_ADMIN = 'admin';
const APP_PAGING_LIMIT = 12;
const APP_CACHE_BUSTER = 138;
const APP_CACHE_BUSTER = 141;
const APP_VERSION_STABLE = '0.7.0';
const APP_STORAGE_UPLOADS = '/storage/uploads';
const APP_STORAGE_FUNCTIONS = '/storage/functions';
@ -202,24 +203,6 @@ $register->set('smtp', function () {
$register->set('geodb', function () {
return new Reader(__DIR__.'/db/DBIP/dbip-country-lite-2020-01.mmdb');
});
$register->set('queue-webhooks', function () {
return new Event('v1-webhooks', 'WebhooksV1');
});
$register->set('queue-audits', function () {
return new Event('v1-audits', 'AuditsV1');
});
$register->set('queue-usage', function () {
return new Event('v1-usage', 'UsageV1');
});
$register->set('queue-mails', function () {
return new Event('v1-mails', 'MailsV1');
});
$register->set('queue-deletes', function () {
return new Event('v1-deletes', 'DeletesV1');
});
$register->set('queue-functions', function () {
return new Event('v1-functions', 'FunctionsV1');
});
/*
* Localization
@ -310,28 +293,24 @@ App::setResource('locale', function() {
});
// Queues
App::setResource('webhooks', function($register) {
return $register->get('queue-webhooks');
App::setResource('events', function($register) {
return new Event('', '');
}, ['register']);
App::setResource('audits', function($register) {
return $register->get('queue-audits');
return new Event('v1-audits', 'AuditsV1');
}, ['register']);
App::setResource('usage', function($register) {
return $register->get('queue-usage');
return new Event('v1-usage', 'UsageV1');
}, ['register']);
App::setResource('mails', function($register) {
return $register->get('queue-mails');
return new Event('v1-mails', 'MailsV1');
}, ['register']);
App::setResource('deletes', function($register) {
return $register->get('queue-deletes');
}, ['register']);
App::setResource('functions', function($register) {
return $register->get('queue-functions');
return new Event('v1-deletes', 'DeletesV1');
}, ['register']);
// Test Mock
@ -381,8 +360,7 @@ App::setResource('user', function($mode, $project, $console, $request, $response
$session = Auth::decodeSession(
$request->getCookie(Auth::$cookieName, // Get sessions
$request->getCookie(Auth::$cookieName.'_legacy', // Get fallback session from old clients (no SameSite support)
$request->getHeader('x-appwrite-key', '')))); // Get API Key
$request->getCookie(Auth::$cookieName.'_legacy', '')));// Get fallback session from old clients (no SameSite support)
// Get fallback session from clients who block 3rd-party cookies
$response->addHeader('X-Debug-Fallback', 'false');
@ -463,7 +441,7 @@ App::setResource('projectDB', function($register, $project) {
App::setResource('mode', function($request) {
/** @var Utopia\Swoole\Request $request */
return $request->getParam('mode', $request->getHeader('x-appwrite-mode', 'default'));
return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT));
}, ['request']);
App::setResource('geodb', function($register) {

View file

@ -21,41 +21,41 @@ $cli
Console::log("\n".'👩‍⚕️ Running '.APP_NAME.' Doctor for version '.App::getEnv('_APP_VERSION', 'UNKNOWN').' ...'."\n");
Console::log('Checking for production best practices...');
$domain = new Domain(App::getEnv('_APP_DOMAIN'));
if(!$domain->isKnown() || $domain->isTest()) {
Console::log('🔴 Hostname has a public suffix ('.$domain->get().')');
Console::log('🔴 Hostname has no public suffix ('.$domain->get().')');
}
else {
Console::log('🟢 Hostname has a public suffix ('.$domain->get().')');
}
$domain = new Domain(App::getEnv('_APP_DOMAIN_TARGET'));
if(!$domain->isKnown() || $domain->isTest()) {
Console::log('🔴 CNAME target has a public suffix ('.$domain->get().')');
Console::log('🔴 CNAME target has no public suffix ('.$domain->get().')');
}
else {
Console::log('🟢 CNAME target has a public suffix ('.$domain->get().')');
}
if(App::getEnv('_APP_OPENSSL_KEY_V1', 'your-secret-key') === 'your-secret-key' || empty(App::getEnv('_APP_OPENSSL_KEY_V1', 'your-secret-key'))) {
Console::log('🔴 Using a unique secret key for encryption');
if(App::getEnv('_APP_OPENSSL_KEY_V1') === 'your-secret-key' || empty(App::getEnv('_APP_OPENSSL_KEY_V1'))) {
Console::log('🔴 Not using a unique secret key for encryption');
}
else {
Console::log('🟢 Using a unique secret key for encryption');
}
if(App::getEnv('_APP_ENV', 'development') !== 'production') {
Console::log('🔴 App enviornment is set for production');
Console::log('🔴 App environment is set for development');
}
else {
Console::log('🟢 App enviornment is set for production');
Console::log('🟢 App environment is set for production');
}
if(App::getEnv('_APP_OPTIONS_ABUSE', 'disabled') === 'disabled' || empty(App::getEnv('_APP_OPTIONS_ABUSE', 'disabled'))) {
Console::log('🔴 Abuse protection is enabled');
if('enabled' !== App::getEnv('_APP_OPTIONS_ABUSE', 'disabled')) {
Console::log('🔴 Abuse protection is disabled');
}
else {
Console::log('🟢 Abuse protection is enabled');
@ -74,8 +74,8 @@ $cli
else {
Console::log('🟢 Console access limits are enabled');
}
if('disabled' === App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') || empty(App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled'))) {
if('enabled' !== App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled')) {
Console::log('🔴 HTTPS force option is disabled');
}
else {
@ -133,17 +133,18 @@ $cli
$mail->Subject = 'Test SMTP Connection';
$mail->Body = 'Hello World';
$mail->AltBody = 'Hello World';
$mail->send();
Console::success('SMTP................connected 👍');
} catch (\Throwable $th) {
Console::error('SMTP.............disconnected 👎');
var_dump($th);
}
$host = App::getEnv('_APP_STATSD_HOST', 'telegraf');
$port = App::getEnv('_APP_STATSD_PORT', 8125);
if($fp = @\fsockopen('udp://'.$host, $port, $errCode, $errStr, 2)){
if($fp = @\fsockopen('udp://'.$host, $port, $errCode, $errStr, 2)){
Console::success('StatsD..............connected 👍');
\fclose($fp);
} else {
@ -153,7 +154,7 @@ $cli
$host = App::getEnv('_APP_INFLUXDB_HOST', '');
$port = App::getEnv('_APP_INFLUXDB_PORT', '');
if($fp = @\fsockopen($host, $port, $errCode, $errStr, 2)){
if($fp = @\fsockopen($host, $port, $errCode, $errStr, 2)){
Console::success('InfluxDB............connected 👍');
\fclose($fp);
} else {
@ -179,7 +180,7 @@ $cli
else {
Console::error('🔴 '.$key.' Volume is unreadable');
}
if (\is_writable($device->getRoot())) {
Console::success('🟢 '.$key.' Volume is writeable');
}
@ -203,9 +204,9 @@ $cli
$percentage = (($device->getPartitionTotalSpace() - $device->getPartitionFreeSpace())
/ $device->getPartitionTotalSpace()) * 100;
$message = $key.' Volume has '.Storage::human($device->getPartitionFreeSpace()) . ' free space ('.\round($percentage, 2).'% used)';
if ($percentage < 80) {
Console::success('🟢 ' . $message);
}
@ -213,12 +214,12 @@ $cli
Console::error('🔴 ' . $message);
}
}
try {
if(App::isProduction()) {
Console::log('');
$version = \json_decode(@\file_get_contents(App::getEnv('_APP_HOME', 'http://localhost').'/v1/health/version'), true);
if ($version && isset($version['version'])) {
if(\version_compare($version['version'], App::getEnv('_APP_VERSION', 'UNKNOWN')) === 0) {
Console::info('You are running the latest version of '.APP_NAME.'! 🥳');

View file

@ -143,9 +143,9 @@ $cli
$stdout = '';
$stderr = '';
Console::log("Running \"docker-compose -f {$path}/docker-compose.yml up -d --remove-orphans\"");
Console::log("Running \"docker-compose -f {$path}/docker-compose.yml up -d --remove-orphans --renew-anon-volumes\"");
$exit = Console::execute("docker-compose -f {$path}/docker-compose.yml up -d --remove-orphans", '', $stdout, $stderr);
$exit = Console::execute("docker-compose -f {$path}/docker-compose.yml up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr);
if ($exit !== 0) {
Console::error("Failed to install Appwrite dockers");

View file

@ -202,13 +202,13 @@ $graph = $this->getParam('graph', false);
<div class="link flutter-new"><img src="/images/clients/flutter.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="Flutter Platform Logo" class="avatar xxs margin-end-small" loading="lazy" /> New Flutter App &nbsp;<span class="text-size-tiny">(beta)</span></div>
</li>
<li class="disabled">
<div class="link ios-new"><img src="/images/clients/ios.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="iOS Platform Logo" class="avatar xxs margin-end-small" loading="lazy" /> New iOS App</div>
<div class="link ios-new"><img src="/images/clients/ios.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="iOS Platform Logo" class="avatar xxs margin-end-small" loading="lazy" /> New iOS App <span class="text-fade text-size-small">(soon)</span></div>
</li>
<li class="disabled">
<div class="link android-new"><img src="/images/clients/android.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="Android Platform Logo" class="avatar xxs margin-end-small" loading="lazy" /> New Android App</div>
<div class="link android-new"><img src="/images/clients/android.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="Android Platform Logo" class="avatar xxs margin-end-small" loading="lazy" /> New Android App <span class="text-fade text-size-small">(soon)</span></div>
</li>
<li class="disabled">
<div class="link unity-new"><img src="/images/clients/unity.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="Unity Platform Logo" class="avatar xxs margin-end-small" loading="lazy" /> New Unity Game</div>
<div class="link unity-new"><img src="/images/clients/unity.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="Unity Platform Logo" class="avatar xxs margin-end-small" loading="lazy" /> New Unity Game <span class="text-fade text-size-small">(soon)</span></div>
</li>
</ul>
</div>

View file

@ -67,6 +67,11 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_SMTP_HOST
- _APP_SMTP_PORT
- _APP_SMTP_SECURE
- _APP_SMTP_USERNAME
- _APP_SMTP_PASSWORD
- _APP_INFLUXDB_HOST
- _APP_INFLUXDB_PORT
- _APP_STORAGE_LIMIT
@ -262,7 +267,7 @@ services:
- _APP_REDIS_PORT
mariadb:
image: appwrite/mariadb:1.1.0 # fix issues when upgrading using: mysql_upgrade -u root -p
image: appwrite/mariadb:1.2.0 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb
restart: unless-stopped
networks:

View file

@ -10,31 +10,43 @@ use Appwrite\Database\Database;
use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
use Appwrite\Database\Adapter\Redis as RedisAdapter;
use Appwrite\Database\Document;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Storage\Device\Local;
use Utopia\CLI\Console;
use Utopia\Config\Config;
class DeletesV1
{
public $args = [];
protected $consoleDB = null;
public function setUp(): void
{
}
public function perform()
{
$projectId = $this->args['projectId'];
$document = $this->args['document'];
$document = new Document($document);
switch ($document->getCollection()) {
switch (strval($document->getCollection())) {
case Database::SYSTEM_COLLECTION_PROJECTS:
$this->deleteProject($document);
break;
case Database::SYSTEM_COLLECTION_USERS:
$this->deleteUser($document);
case Database::SYSTEM_COLLECTION_FUNCTIONS:
$this->deleteFunction($document, $projectId);
break;
case Database::SYSTEM_COLLECTION_USERS:
$this->deleteUser($document, $projectId);
break;
case Database::SYSTEM_COLLECTION_COLLECTIONS:
$this->deleteDocuments($document, $projectId);
break;
default:
Console::error('No lazy delete operation available for document of type: '.$document->getCollection());
break;
}
}
@ -43,50 +55,163 @@ class DeletesV1
{
// ... Remove environment for this job
}
protected function deleteDocuments(Document $document, $projectId)
{
$collectionId = $document->getId();
// Delete Documents in the deleted collection
$this->deleteByGroup([
'$collection='.$collectionId
], $this->getProjectDB($projectId));
}
protected function deleteProject(Document $document)
{
global $register;
$consoleDB = new Database();
$consoleDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
$consoleDB->setNamespace('app_console'); // Main DB
$consoleDB->setMocks(Config::getParam('collections', []));
// Delete all DBs
$consoleDB->deleteNamespace($document->getId());
$this->getConsoleDB()->deleteNamespace($document->getId());
$uploads = new Local(APP_STORAGE_UPLOADS.'/app-'.$document->getId());
$cache = new Local(APP_STORAGE_CACHE.'/app-'.$document->getId());
// Delete all storage directories
$uploads->delete($uploads->getRoot(), true);
$cache->delete($cache->getRoot(), true);
}
protected function deleteUser(Document $user)
protected function deleteUser(Document $document, $projectId)
{
global $projectDB;
$tokens = $user->getAttribute('tokens', []);
$tokens = $document->getAttribute('tokens', []);
foreach ($tokens as $token) {
if (!$projectDB->deleteDocument($token->getId())) {
if (!$this->getProjectDB($projectId)->deleteDocument($token->getId())) {
throw new Exception('Failed to remove token from DB', 500);
}
}
$memberships = $projectDB->getCollection([
'limit' => 2000, // TODO add members limit
'offset' => 0,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_MEMBERSHIPS,
'userId='.$user->getId(),
],
]);
// Delete Memberships
$this->deleteByGroup([
'$collection='.Database::SYSTEM_COLLECTION_MEMBERSHIPS,
'userId='.$document->getId(),
], $this->getProjectDB($projectId));
}
foreach ($memberships as $membership) {
if (!$projectDB->deleteDocument($membership->getId())) {
throw new Exception('Failed to remove team membership from DB', 500);
protected function deleteFunction(Document $document, $projectId)
{
$projectDB = $this->getProjectDB($projectId);
$device = new Local(APP_STORAGE_FUNCTIONS.'/app-'.$projectId);
// Delete Tags
$this->deleteByGroup([
'$collection='.Database::SYSTEM_COLLECTION_TAGS,
'functionId='.$document->getId(),
], $projectDB, function(Document $document) use ($device) {
if ($device->delete($document->getAttribute('path', ''))) {
Console::success('Delete code tag: '.$document->getAttribute('path', ''));
}
else {
Console::error('Dailed to delete code tag: '.$document->getAttribute('path', ''));
}
});
// Delete Executions
$this->deleteByGroup([
'$collection='.Database::SYSTEM_COLLECTION_EXECUTIONS,
'functionId='.$document->getId(),
], $projectDB);
}
protected function deleteById(Document $document, Database $database, callable $callback = null): bool
{
Authorization::disable();
if($database->deleteDocument($document->getId())) {
Console::success('Deleted document "'.$document->getId().'" successfully');
if(is_callable($callback)) {
$callback($document);
}
return true;
}
else {
Console::error('Failed to delete document: '.$document->getId());
return false;
}
Authorization::reset();
}
protected function deleteByGroup(array $filters, Database $database, callable $callback = null)
{
$count = 0;
$chunk = 0;
$limit = 50;
$results = [];
$sum = $limit;
$executionStart = \microtime(true);
while($sum === $limit) {
$chunk++;
Authorization::disable();
$results = $database->getCollection([
'limit' => $limit,
'offset' => 0,
'orderField' => '$id',
'orderType' => 'ASC',
'orderCast' => 'string',
'filters' => $filters,
]);
Authorization::reset();
$sum = count($results);
Console::info('Deleting chunk #'.$chunk.'. Found '.$sum.' documents');
foreach ($results as $document) {
$this->deleteById($document, $database, $callback);
$count++;
}
}
$executionEnd = \microtime(true);
Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds");
}
}
/**
* @return Database;
*/
protected function getConsoleDB(): Database
{
global $register;
if($this->consoleDB === null) {
$this->consoleDB = new Database();
$this->consoleDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
$this->consoleDB->setNamespace('app_console'); // Main DB
$this->consoleDB->setMocks(Config::getParam('collections', []));
}
return $this->consoleDB;
}
/**
* @return Database;
*/
protected function getProjectDB($projectId): Database
{
global $register;
$projectDB = new Database();
$projectDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register));
$projectDB->setNamespace('app_'.$projectId); // Main DB
$projectDB->setMocks(Config::getParam('collections', []));
return $projectDB;
}
}

View file

@ -1,6 +1,7 @@
<?php
use Utopia\App;
use Utopia\CLI\Console;
require_once __DIR__.'/../init.php';
@ -23,6 +24,11 @@ class MailsV1
{
global $register;
if(empty(App::getEnv('_APP_SMTP_HOST'))) {
Console::info('Skipped mail processing. No SMTP server hostname has been set.');
return;
}
$event = $this->args['event'];
$from = $this->args['from'];
$recipient = $this->args['recipient'];

View file

@ -34,8 +34,9 @@ class WebhooksV1
$errors = [];
// Event
$projectId = $this->args['projectId'];
$event = $this->args['event'];
$projectId = $this->args['projectId'] ?? '';
$userId = $this->args['userId'] ?? '';
$event = $this->args['event'] ?? '';
$payload = \json_encode($this->args['payload']);
// Webhook
@ -55,6 +56,7 @@ class WebhooksV1
continue;
}
$id = $webhook['$id'] ?? '';
$name = $webhook['name'] ?? '';
$signature = $webhook['signature'] ?? 'not-yet-implemented';
$url = $webhook['url'] ?? '';
@ -78,8 +80,11 @@ class WebhooksV1
[
'Content-Type: application/json',
'Content-Length: '.\strlen($payload),
'X-'.APP_NAME.'-Webhook-Id: '.$id,
'X-'.APP_NAME.'-Webhook-Event: '.$event,
'X-'.APP_NAME.'-Webhook-Name: '.$name,
'X-'.APP_NAME.'-Webhook-User-Id: '.$userId,
'X-'.APP_NAME.'-Webhook-Project-Id: '.$projectId,
'X-'.APP_NAME.'-Webhook-Signature: '.$signature,
]
);

View file

@ -34,7 +34,7 @@
"appwrite/php-clamav": "1.0.*",
"utopia-php/framework": "0.9.6",
"utopia-php/framework": "0.9.8",
"utopia-php/abuse": "0.2.*",
"utopia-php/audit": "0.3.*",
"utopia-php/cache": "0.2.*",
@ -47,18 +47,18 @@
"utopia-php/swoole": "0.2.*",
"resque/php-resque": "1.3.6",
"piwik/device-detector": "3.13.0",
"matomo/device-detector": "3.13.0",
"dragonmantank/cron-expression": "3.0.1",
"domnikl/statsd": "3.0.*",
"influxdb/influxdb-php": "1.15.*",
"domnikl/statsd": "3.0.2",
"influxdb/influxdb-php": "1.15.1",
"phpmailer/phpmailer": "6.1.7",
"chillerlan/php-qrcode": "^4.2"
"chillerlan/php-qrcode": "4.2.0"
},
"require-dev": {
"swoole/ide-helper": "4.5.5",
"appwrite/sdk-generator": "master",
"phpunit/phpunit": "^9.3",
"vimeo/psalm": "4.0.1"
"phpunit/phpunit": "9.4.2",
"vimeo/psalm": "4.1.1"
},
"repositories": [
{

423
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": "5f0e8470b46e87d70f3858b3f4ad9b30",
"content-hash": "18cec0afcb76ef57fa38386fe04fd87b",
"packages": [
{
"name": "appwrite/php-clamav",
@ -178,16 +178,16 @@
},
{
"name": "colinmollenhour/credis",
"version": "v1.11.4",
"version": "v1.12.1",
"source": {
"type": "git",
"url": "https://github.com/colinmollenhour/credis.git",
"reference": "b458b7c65d156744f5f0c4667c0f8ce45d955435"
"reference": "c27faa11724229986335c23f4b6d0f1d8d6547fb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/colinmollenhour/credis/zipball/b458b7c65d156744f5f0c4667c0f8ce45d955435",
"reference": "b458b7c65d156744f5f0c4667c0f8ce45d955435",
"url": "https://api.github.com/repos/colinmollenhour/credis/zipball/c27faa11724229986335c23f4b6d0f1d8d6547fb",
"reference": "c27faa11724229986335c23f4b6d0f1d8d6547fb",
"shasum": ""
},
"require": {
@ -214,7 +214,7 @@
],
"description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.",
"homepage": "https://github.com/colinmollenhour/credis",
"time": "2020-10-13T23:55:13+00:00"
"time": "2020-11-06T16:09:14+00:00"
},
{
"name": "domnikl/statsd",
@ -601,6 +601,61 @@
],
"time": "2020-09-18T13:24:03+00:00"
},
{
"name": "matomo/device-detector",
"version": "3.13.0",
"source": {
"type": "git",
"url": "https://github.com/matomo-org/device-detector.git",
"reference": "75ca5b690e38c40d199ade93e677bc5d7c3bc498"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/75ca5b690e38c40d199ade93e677bc5d7c3bc498",
"reference": "75ca5b690e38c40d199ade93e677bc5d7c3bc498",
"shasum": ""
},
"require": {
"mustangostang/spyc": "*",
"php": ">=5.5"
},
"require-dev": {
"fabpot/php-cs-fixer": "~1.7",
"matthiasmullie/scrapbook": "@stable",
"phpunit/phpunit": "^4.8.36",
"psr/cache": "^1.0",
"psr/simple-cache": "^1.0"
},
"suggest": {
"doctrine/cache": "Can directly be used for caching purpose",
"ext-yaml": "Necessary for using the Pecl YAML parser"
},
"type": "library",
"autoload": {
"psr-4": {
"DeviceDetector\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "The Matomo Team",
"email": "hello@matomo.org",
"homepage": "https://matomo.org/team/"
}
],
"description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.",
"homepage": "https://matomo.org",
"keywords": [
"devicedetection",
"parser",
"useragent"
],
"time": "2020-08-17T07:37:33+00:00"
},
{
"name": "mustangostang/spyc",
"version": "dev-master",
@ -719,62 +774,6 @@
],
"time": "2020-07-14T18:50:27+00:00"
},
{
"name": "piwik/device-detector",
"version": "3.13.0",
"source": {
"type": "git",
"url": "https://github.com/matomo-org/device-detector.git",
"reference": "75ca5b690e38c40d199ade93e677bc5d7c3bc498"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/75ca5b690e38c40d199ade93e677bc5d7c3bc498",
"reference": "75ca5b690e38c40d199ade93e677bc5d7c3bc498",
"shasum": ""
},
"require": {
"mustangostang/spyc": "*",
"php": ">=5.5"
},
"require-dev": {
"fabpot/php-cs-fixer": "~1.7",
"matthiasmullie/scrapbook": "@stable",
"phpunit/phpunit": "^4.8.36",
"psr/cache": "^1.0",
"psr/simple-cache": "^1.0"
},
"suggest": {
"doctrine/cache": "Can directly be used for caching purpose",
"ext-yaml": "Necessary for using the Pecl YAML parser"
},
"type": "library",
"autoload": {
"psr-4": {
"DeviceDetector\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "The Matomo Team",
"email": "hello@matomo.org",
"homepage": "https://matomo.org/team/"
}
],
"description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.",
"homepage": "https://matomo.org",
"keywords": [
"devicedetection",
"parser",
"useragent"
],
"abandoned": "matomo/device-detector",
"time": "2020-08-17T07:37:33+00:00"
},
{
"name": "psr/http-client",
"version": "dev-master",
@ -1333,16 +1332,16 @@
},
{
"name": "utopia-php/framework",
"version": "0.9.6",
"version": "0.9.8",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/framework.git",
"reference": "959767e401c0497f0ddf31446d8d4dfa125aa5dc"
"reference": "4caec144554f028b3ec710a0cdd4f75193f01285"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/framework/zipball/959767e401c0497f0ddf31446d8d4dfa125aa5dc",
"reference": "959767e401c0497f0ddf31446d8d4dfa125aa5dc",
"url": "https://api.github.com/repos/utopia-php/framework/zipball/4caec144554f028b3ec710a0cdd4f75193f01285",
"reference": "4caec144554f028b3ec710a0cdd4f75193f01285",
"shasum": ""
},
"require": {
@ -1374,7 +1373,7 @@
"php",
"upf"
],
"time": "2020-10-26T00:20:11+00:00"
"time": "2020-11-11T20:34:58+00:00"
},
{
"name": "utopia-php/locale",
@ -1580,12 +1579,12 @@
"source": {
"type": "git",
"url": "https://github.com/amphp/amp.git",
"reference": "eb2f325586bc6ebb12d27834fc779fa140c38a57"
"reference": "ecdc3c476b3ccff02f8e5d5bcc04f7ccfd18751c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/amphp/amp/zipball/eb2f325586bc6ebb12d27834fc779fa140c38a57",
"reference": "eb2f325586bc6ebb12d27834fc779fa140c38a57",
"url": "https://api.github.com/repos/amphp/amp/zipball/ecdc3c476b3ccff02f8e5d5bcc04f7ccfd18751c",
"reference": "ecdc3c476b3ccff02f8e5d5bcc04f7ccfd18751c",
"shasum": ""
},
"require": {
@ -1656,7 +1655,7 @@
"type": "github"
}
],
"time": "2020-10-10T13:54:50+00:00"
"time": "2020-11-03T16:23:45+00:00"
},
{
"name": "amphp/byte-stream",
@ -1736,7 +1735,7 @@
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator",
"reference": "ad1ee55f61967546c0889d377b628e244182311e"
"reference": "e78d94eb1bc538e988d7e45afca7304a98ffc3f1"
},
"require": {
"ext-curl": "*",
@ -1766,7 +1765,7 @@
}
],
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"time": "2020-10-20T10:23:43+00:00"
"time": "2020-11-11T09:53:31+00:00"
},
{
"name": "composer/package-versions-deprecated",
@ -1774,12 +1773,12 @@
"source": {
"type": "git",
"url": "https://github.com/composer/package-versions-deprecated.git",
"reference": "c8c9aa8a14cc3d3bec86d0a8c3fa52ea79936855"
"reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/c8c9aa8a14cc3d3bec86d0a8c3fa52ea79936855",
"reference": "c8c9aa8a14cc3d3bec86d0a8c3fa52ea79936855",
"url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/7413f0b55a051e89485c5cb9f765fe24bb02a7b6",
"reference": "7413f0b55a051e89485c5cb9f765fe24bb02a7b6",
"shasum": ""
},
"require": {
@ -1835,7 +1834,7 @@
"type": "tidelift"
}
],
"time": "2020-08-25T05:50:16+00:00"
"time": "2020-11-11T10:22:58+00:00"
},
{
"name": "composer/semver",
@ -2006,36 +2005,31 @@
},
{
"name": "doctrine/instantiator",
"version": "dev-master",
"version": "1.5.x-dev",
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
"reference": "3e7a22aed197e9333cc929e7f6b4300bdae91fcc"
"reference": "6410c4b8352cb64218641457cef64997e6b784fb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/3e7a22aed197e9333cc929e7f6b4300bdae91fcc",
"reference": "3e7a22aed197e9333cc929e7f6b4300bdae91fcc",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/6410c4b8352cb64218641457cef64997e6b784fb",
"reference": "6410c4b8352cb64218641457cef64997e6b784fb",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^6.0",
"doctrine/coding-standard": "^8.0",
"ext-pdo": "*",
"ext-phar": "*",
"phpbench/phpbench": "^0.13",
"phpstan/phpstan-phpunit": "^0.11",
"phpstan/phpstan-shim": "^0.11",
"phpunit/phpunit": "^7.0"
"phpbench/phpbench": "^0.13 || 1.0.0-alpha2",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan-phpunit": "^0.12",
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
@ -2049,7 +2043,7 @@
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com",
"homepage": "http://ocramius.github.com/"
"homepage": "https://ocramius.github.io/"
}
],
"description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
@ -2072,7 +2066,7 @@
"type": "tidelift"
}
],
"time": "2020-06-15T18:51:04+00:00"
"time": "2020-11-10T19:05:51+00:00"
},
{
"name": "felixfbecker/advanced-json-rpc",
@ -2795,12 +2789,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "7c0684ac5818c3a5a79d9dd9d85aac2aebdadb05"
"reference": "5d66bde3afba51e21c6eb7d1a3776bd3b88dfafc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7c0684ac5818c3a5a79d9dd9d85aac2aebdadb05",
"reference": "7c0684ac5818c3a5a79d9dd9d85aac2aebdadb05",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5d66bde3afba51e21c6eb7d1a3776bd3b88dfafc",
"reference": "5d66bde3afba51e21c6eb7d1a3776bd3b88dfafc",
"shasum": ""
},
"require": {
@ -2860,7 +2854,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:10:23+00:00"
"time": "2020-11-09T10:25:30+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -2868,12 +2862,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "437553634c79817d89b412cf2e6711ef1ccf6418"
"reference": "854c530d02bcc6b5b96942e6e4cfeead11b35aad"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/437553634c79817d89b412cf2e6711ef1ccf6418",
"reference": "437553634c79817d89b412cf2e6711ef1ccf6418",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/854c530d02bcc6b5b96942e6e4cfeead11b35aad",
"reference": "854c530d02bcc6b5b96942e6e4cfeead11b35aad",
"shasum": ""
},
"require": {
@ -2916,7 +2910,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:12:04+00:00"
"time": "2020-11-09T10:26:59+00:00"
},
{
"name": "phpunit/php-invoker",
@ -2924,12 +2918,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-invoker.git",
"reference": "27560ee0808c5ba9144ad228887e788c608c8f58"
"reference": "0cd572c9935a3a8373416e733ad1566d7c602ff1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/27560ee0808c5ba9144ad228887e788c608c8f58",
"reference": "27560ee0808c5ba9144ad228887e788c608c8f58",
"url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/0cd572c9935a3a8373416e733ad1566d7c602ff1",
"reference": "0cd572c9935a3a8373416e733ad1566d7c602ff1",
"shasum": ""
},
"require": {
@ -2975,7 +2969,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:12:14+00:00"
"time": "2020-11-09T10:27:08+00:00"
},
{
"name": "phpunit/php-text-template",
@ -2983,12 +2977,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-text-template.git",
"reference": "2d6aa395e3f5a69ba3a5449d090cf7b845552081"
"reference": "0d0bdf563575ba6715c5a6754541c80a9aa6dde2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/2d6aa395e3f5a69ba3a5449d090cf7b845552081",
"reference": "2d6aa395e3f5a69ba3a5449d090cf7b845552081",
"url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0d0bdf563575ba6715c5a6754541c80a9aa6dde2",
"reference": "0d0bdf563575ba6715c5a6754541c80a9aa6dde2",
"shasum": ""
},
"require": {
@ -3030,7 +3024,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:12:53+00:00"
"time": "2020-11-09T10:27:42+00:00"
},
{
"name": "phpunit/php-timer",
@ -3038,12 +3032,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-timer.git",
"reference": "735b06b2ce91f813d4574d0671a4e8165c8ece9c"
"reference": "16bcf8ca6821c270f22794c02e50b8107cb375d8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/735b06b2ce91f813d4574d0671a4e8165c8ece9c",
"reference": "735b06b2ce91f813d4574d0671a4e8165c8ece9c",
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/16bcf8ca6821c270f22794c02e50b8107cb375d8",
"reference": "16bcf8ca6821c270f22794c02e50b8107cb375d8",
"shasum": ""
},
"require": {
@ -3085,20 +3079,20 @@
"type": "github"
}
],
"time": "2020-10-30T14:12:23+00:00"
"time": "2020-11-09T10:27:16+00:00"
},
{
"name": "phpunit/phpunit",
"version": "dev-master",
"version": "9.4.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "abbb373dfdc7be1c77a1a700da67f28a7013d2e7"
"reference": "3866b2eeeed21b1b099c4bc0b7a1690ac6fd5baa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/abbb373dfdc7be1c77a1a700da67f28a7013d2e7",
"reference": "abbb373dfdc7be1c77a1a700da67f28a7013d2e7",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3866b2eeeed21b1b099c4bc0b7a1690ac6fd5baa",
"reference": "3866b2eeeed21b1b099c4bc0b7a1690ac6fd5baa",
"shasum": ""
},
"require": {
@ -3114,7 +3108,7 @@
"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",
"phpunit/php-file-iterator": "^3.0.5",
"phpunit/php-invoker": "^3.1.1",
"phpunit/php-text-template": "^2.0.3",
@ -3145,7 +3139,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "9.5-dev"
"dev-master": "9.4-dev"
}
},
"autoload": {
@ -3184,7 +3178,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:20:26+00:00"
"time": "2020-10-19T09:23:29+00:00"
},
{
"name": "psr/container",
@ -3241,12 +3235,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/cli-parser.git",
"reference": "a8a95d7ddcf625e12c9eca502af7a2f72d6e3e7a"
"reference": "0810c53718aa9ea7e42f2de2865a049ce3f5122f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/a8a95d7ddcf625e12c9eca502af7a2f72d6e3e7a",
"reference": "a8a95d7ddcf625e12c9eca502af7a2f72d6e3e7a",
"url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/0810c53718aa9ea7e42f2de2865a049ce3f5122f",
"reference": "0810c53718aa9ea7e42f2de2865a049ce3f5122f",
"shasum": ""
},
"require": {
@ -3285,7 +3279,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:13:23+00:00"
"time": "2020-11-09T10:28:10+00:00"
},
{
"name": "sebastian/code-unit",
@ -3293,12 +3287,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/code-unit.git",
"reference": "97d3d265ae9e0692bc19553d0a28d2e99c6a13dc"
"reference": "2af9894d3d514c2ee6d6ad03cc23bb4625050649"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/97d3d265ae9e0692bc19553d0a28d2e99c6a13dc",
"reference": "97d3d265ae9e0692bc19553d0a28d2e99c6a13dc",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/2af9894d3d514c2ee6d6ad03cc23bb4625050649",
"reference": "2af9894d3d514c2ee6d6ad03cc23bb4625050649",
"shasum": ""
},
"require": {
@ -3337,7 +3331,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:10:33+00:00"
"time": "2020-11-09T10:25:39+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
@ -3345,12 +3339,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
"reference": "b7b8338be2131558c2eaeb0ec0b62ee3885181a8"
"reference": "2f9aa793a37d4f39b5cf5ee3c9a1bd506885d60c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/b7b8338be2131558c2eaeb0ec0b62ee3885181a8",
"reference": "b7b8338be2131558c2eaeb0ec0b62ee3885181a8",
"url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/2f9aa793a37d4f39b5cf5ee3c9a1bd506885d60c",
"reference": "2f9aa793a37d4f39b5cf5ee3c9a1bd506885d60c",
"shasum": ""
},
"require": {
@ -3388,7 +3382,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:10:42+00:00"
"time": "2020-11-09T10:25:48+00:00"
},
{
"name": "sebastian/comparator",
@ -3396,12 +3390,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "237324bd32872e15f51bc2eadac2a9e43e28409d"
"reference": "6d3dc38d8631941ca18c76cac3d7a15838071693"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/237324bd32872e15f51bc2eadac2a9e43e28409d",
"reference": "237324bd32872e15f51bc2eadac2a9e43e28409d",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6d3dc38d8631941ca18c76cac3d7a15838071693",
"reference": "6d3dc38d8631941ca18c76cac3d7a15838071693",
"shasum": ""
},
"require": {
@ -3458,7 +3452,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:10:53+00:00"
"time": "2020-11-09T10:25:57+00:00"
},
{
"name": "sebastian/complexity",
@ -3466,12 +3460,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/complexity.git",
"reference": "3bd69c610bb8e2958f3289d1a9583514471b62e6"
"reference": "cf04d6dfa8f99e37a677bf67195c43fc30024282"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/3bd69c610bb8e2958f3289d1a9583514471b62e6",
"reference": "3bd69c610bb8e2958f3289d1a9583514471b62e6",
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/cf04d6dfa8f99e37a677bf67195c43fc30024282",
"reference": "cf04d6dfa8f99e37a677bf67195c43fc30024282",
"shasum": ""
},
"require": {
@ -3511,7 +3505,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:13:03+00:00"
"time": "2020-11-09T10:27:51+00:00"
},
{
"name": "sebastian/diff",
@ -3519,12 +3513,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "7b02596262b190492663ff4df9227100f70c7823"
"reference": "2c921642f8495dbf50fdcb920c425f9ccbb6a843"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7b02596262b190492663ff4df9227100f70c7823",
"reference": "7b02596262b190492663ff4df9227100f70c7823",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/2c921642f8495dbf50fdcb920c425f9ccbb6a843",
"reference": "2c921642f8495dbf50fdcb920c425f9ccbb6a843",
"shasum": ""
},
"require": {
@ -3573,7 +3567,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:11:04+00:00"
"time": "2020-11-09T10:26:06+00:00"
},
{
"name": "sebastian/environment",
@ -3581,12 +3575,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
"reference": "7a613938b2aa9c873233f810e6300bf06c42ca17"
"reference": "6fe240eca4c1230f6442747269ff94234674f30e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/7a613938b2aa9c873233f810e6300bf06c42ca17",
"reference": "7a613938b2aa9c873233f810e6300bf06c42ca17",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6fe240eca4c1230f6442747269ff94234674f30e",
"reference": "6fe240eca4c1230f6442747269ff94234674f30e",
"shasum": ""
},
"require": {
@ -3632,7 +3626,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:11:15+00:00"
"time": "2020-11-09T10:26:15+00:00"
},
{
"name": "sebastian/exporter",
@ -3640,12 +3634,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "33fcbffef7131af7ee4ad12f52bf6a3d02124557"
"reference": "04229817d2bb369fa6b25229316e7bbc53baece2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/33fcbffef7131af7ee4ad12f52bf6a3d02124557",
"reference": "33fcbffef7131af7ee4ad12f52bf6a3d02124557",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/04229817d2bb369fa6b25229316e7bbc53baece2",
"reference": "04229817d2bb369fa6b25229316e7bbc53baece2",
"shasum": ""
},
"require": {
@ -3705,7 +3699,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:11:24+00:00"
"time": "2020-11-09T10:26:24+00:00"
},
{
"name": "sebastian/global-state",
@ -3713,12 +3707,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "e9696869db3ba851de7221f4d6997b4262b623d9"
"reference": "bae2a2a7c5dac8eb4ab11e178fcd88242c1f8b17"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e9696869db3ba851de7221f4d6997b4262b623d9",
"reference": "e9696869db3ba851de7221f4d6997b4262b623d9",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bae2a2a7c5dac8eb4ab11e178fcd88242c1f8b17",
"reference": "bae2a2a7c5dac8eb4ab11e178fcd88242c1f8b17",
"shasum": ""
},
"require": {
@ -3765,7 +3759,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:11:34+00:00"
"time": "2020-11-09T10:26:33+00:00"
},
{
"name": "sebastian/lines-of-code",
@ -3773,12 +3767,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/lines-of-code.git",
"reference": "edc91041d25e5f667f5adf1deb0f96a11153575e"
"reference": "62f65495957a0d4b39045a059bbf8d6d74db06f5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/edc91041d25e5f667f5adf1deb0f96a11153575e",
"reference": "edc91041d25e5f667f5adf1deb0f96a11153575e",
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/62f65495957a0d4b39045a059bbf8d6d74db06f5",
"reference": "62f65495957a0d4b39045a059bbf8d6d74db06f5",
"shasum": ""
},
"require": {
@ -3818,7 +3812,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:13:13+00:00"
"time": "2020-11-09T10:28:00+00:00"
},
{
"name": "sebastian/object-enumerator",
@ -3826,12 +3820,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-enumerator.git",
"reference": "d913c474aa28f6646d5a899d18b546a9d8a94134"
"reference": "bd926d4c9372e89f8eeff4d3e1d46886b3647959"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/d913c474aa28f6646d5a899d18b546a9d8a94134",
"reference": "d913c474aa28f6646d5a899d18b546a9d8a94134",
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/bd926d4c9372e89f8eeff4d3e1d46886b3647959",
"reference": "bd926d4c9372e89f8eeff4d3e1d46886b3647959",
"shasum": ""
},
"require": {
@ -3871,7 +3865,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:11:44+00:00"
"time": "2020-11-09T10:26:42+00:00"
},
{
"name": "sebastian/object-reflector",
@ -3879,12 +3873,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-reflector.git",
"reference": "4acdc9cb8f4eae4da631c414ece1fff21d3578b0"
"reference": "2ae863c8667f2c149b20d13782d11ce23ab995f8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4acdc9cb8f4eae4da631c414ece1fff21d3578b0",
"reference": "4acdc9cb8f4eae4da631c414ece1fff21d3578b0",
"url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/2ae863c8667f2c149b20d13782d11ce23ab995f8",
"reference": "2ae863c8667f2c149b20d13782d11ce23ab995f8",
"shasum": ""
},
"require": {
@ -3922,7 +3916,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:11:55+00:00"
"time": "2020-11-09T10:26:50+00:00"
},
{
"name": "sebastian/recursion-context",
@ -3930,12 +3924,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
"reference": "d09a5073917f11f0b1e2dce2443d91acb50d0671"
"reference": "4c073073e68e3d2d5dd7a50ebfc53a912116b498"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/d09a5073917f11f0b1e2dce2443d91acb50d0671",
"reference": "d09a5073917f11f0b1e2dce2443d91acb50d0671",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/4c073073e68e3d2d5dd7a50ebfc53a912116b498",
"reference": "4c073073e68e3d2d5dd7a50ebfc53a912116b498",
"shasum": ""
},
"require": {
@ -3981,7 +3975,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:12:33+00:00"
"time": "2020-11-09T10:27:25+00:00"
},
{
"name": "sebastian/resource-operations",
@ -4040,12 +4034,12 @@
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/type.git",
"reference": "d6eb978632b7b77cad85a9a655e5c3dbb9c7100c"
"reference": "dcac0d6b8489ed9975d01303f0903958ebad0ce4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/d6eb978632b7b77cad85a9a655e5c3dbb9c7100c",
"reference": "d6eb978632b7b77cad85a9a655e5c3dbb9c7100c",
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/dcac0d6b8489ed9975d01303f0903958ebad0ce4",
"reference": "dcac0d6b8489ed9975d01303f0903958ebad0ce4",
"shasum": ""
},
"require": {
@ -4084,7 +4078,7 @@
"type": "github"
}
],
"time": "2020-10-30T14:12:43+00:00"
"time": "2020-11-09T10:27:33+00:00"
},
{
"name": "sebastian/version",
@ -4175,12 +4169,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "f372360d0dfa520b23b91fc70e2bba6a232ac62d"
"reference": "d1d8b8fd9b605630aa73b1b384e246fee54e8ffa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/f372360d0dfa520b23b91fc70e2bba6a232ac62d",
"reference": "f372360d0dfa520b23b91fc70e2bba6a232ac62d",
"url": "https://api.github.com/repos/symfony/console/zipball/d1d8b8fd9b605630aa73b1b384e246fee54e8ffa",
"reference": "d1d8b8fd9b605630aa73b1b384e246fee54e8ffa",
"shasum": ""
},
"require": {
@ -4261,7 +4255,7 @@
"type": "tidelift"
}
],
"time": "2020-10-29T07:35:47+00:00"
"time": "2020-11-05T20:05:54+00:00"
},
{
"name": "symfony/polyfill-ctype",
@ -4940,12 +4934,12 @@
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "b163c7f71698165ccf13738ac77273b40d5776f8"
"reference": "e133d1fd8c9f22dd9bc43d1b7fc46e0f35692226"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/b163c7f71698165ccf13738ac77273b40d5776f8",
"reference": "b163c7f71698165ccf13738ac77273b40d5776f8",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/e133d1fd8c9f22dd9bc43d1b7fc46e0f35692226",
"reference": "e133d1fd8c9f22dd9bc43d1b7fc46e0f35692226",
"shasum": ""
},
"require": {
@ -5007,20 +5001,20 @@
"type": "tidelift"
}
],
"time": "2020-10-27T19:26:34+00:00"
"time": "2020-11-09T13:33:24+00:00"
},
{
"name": "vimeo/psalm",
"version": "4.0.1",
"version": "4.1.1",
"source": {
"type": "git",
"url": "https://github.com/vimeo/psalm.git",
"reference": "b1e2e30026936ef8d5bf6a354d1c3959b6231f44"
"reference": "16bfbd9224698bd738c665f33039fade2a1a3977"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/b1e2e30026936ef8d5bf6a354d1c3959b6231f44",
"reference": "b1e2e30026936ef8d5bf6a354d1c3959b6231f44",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/16bfbd9224698bd738c665f33039fade2a1a3977",
"reference": "16bfbd9224698bd738c665f33039fade2a1a3977",
"shasum": ""
},
"require": {
@ -5041,10 +5035,9 @@
"netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0",
"nikic/php-parser": "^4.10.1",
"openlss/lib-array2xml": "^1.0",
"php": "^7.3|^8",
"php": "^7.1|^8",
"sebastian/diff": "^3.0 || ^4.0",
"symfony/console": "^3.4.17 || ^4.1.6 || ^5.0",
"webmozart/glob": "^4.1",
"webmozart/path-util": "^2.3"
},
"provide": {
@ -5055,6 +5048,7 @@
"bamarni/composer-bin-plugin": "^1.2",
"brianium/paratest": "^4.0.0",
"ext-curl": "*",
"php": "^7.3|^8",
"phpdocumentor/reflection-docblock": "^5",
"phpmyadmin/sql-parser": "5.1.0",
"phpspec/prophecy": ">=1.9.0",
@ -5108,7 +5102,7 @@
"inspection",
"php"
],
"time": "2020-10-20T13:40:17+00:00"
"time": "2020-11-02T05:54:12+00:00"
},
{
"name": "webmozart/assert",
@ -5159,53 +5153,6 @@
],
"time": "2020-07-08T17:02:28+00:00"
},
{
"name": "webmozart/glob",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/webmozart/glob.git",
"reference": "8da14867b709e8776d9f6272faaf844aefc695e3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozart/glob/zipball/8da14867b709e8776d9f6272faaf844aefc695e3",
"reference": "8da14867b709e8776d9f6272faaf844aefc695e3",
"shasum": ""
},
"require": {
"php": "^5.3.3|^7.0",
"webmozart/path-util": "^2.2"
},
"require-dev": {
"phpunit/phpunit": "^4.6",
"sebastian/version": "^1.0.1",
"symfony/filesystem": "^2.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.1-dev"
}
},
"autoload": {
"psr-4": {
"Webmozart\\Glob\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
}
],
"description": "A PHP implementation of Ant's glob.",
"time": "2016-08-15T15:31:26+00:00"
},
{
"name": "webmozart/path-util",
"version": "dev-master",

View file

@ -88,7 +88,7 @@ services:
- _APP_SMTP_PORT=25
mariadb:
image: appwrite/mariadb:1.1.0 # fix issues when upgrading using: mysql_upgrade -u root -p
image: appwrite/mariadb:1.2.0 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb
restart: unless-stopped
networks:

View file

@ -82,6 +82,11 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_SMTP_HOST
- _APP_SMTP_PORT
- _APP_SMTP_SECURE
- _APP_SMTP_USERNAME
- _APP_SMTP_PASSWORD
- _APP_INFLUXDB_HOST
- _APP_INFLUXDB_PORT
- _APP_STORAGE_LIMIT
@ -288,7 +293,7 @@ services:
- _APP_REDIS_PORT
mariadb:
image: appwrite/mariadb:1.1.0 # fix issues when upgrading using: mysql_upgrade -u root -p
image: appwrite/mariadb:1.2.0 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb
restart: unless-stopped
networks:
@ -305,24 +310,6 @@ services:
command: 'mysqld --innodb-flush-method=fsync' # add ' --query_cache_size=0' for DB tests
# command: mv /var/lib/mysql/ib_logfile0 /var/lib/mysql/ib_logfile0.bu && mv /var/lib/mysql/ib_logfile1 /var/lib/mysql/ib_logfile1.bu
maildev: # used mainly for dev tests
image: djfarrelly/maildev
container_name: appwrite-maildev
restart: unless-stopped
ports:
- '1080:80'
networks:
- appwrite
request-catcher: # used mainly for dev tests
image: smarterdm/http-request-catcher
container_name: appwrite-request-catcher
restart: unless-stopped
ports:
- '5000:5000'
networks:
- appwrite
# smtp:
# image: appwrite/smtp:1.2.0
# container_name: appwrite-smtp
@ -369,6 +356,24 @@ services:
networks:
- appwrite
maildev: # used mainly for dev tests
image: djfarrelly/maildev
container_name: appwrite-maildev
restart: unless-stopped
ports:
- '1080:80'
networks:
- appwrite
request-catcher: # used mainly for dev tests
image: smarterdm/http-request-catcher
container_name: appwrite-request-catcher
restart: unless-stopped
ports:
- '5000:5000'
networks:
- appwrite
# redis-commander:
# image: rediscommander/redis-commander:latest
# restart: unless-stopped

View file

@ -1 +1 @@
Use this endpoint to fetch the favorite icon (AKA favicon) of a any remote website URL.
Use this endpoint to fetch the favorite icon (AKA favicon) of any remote website URL.

View file

@ -112,7 +112,7 @@ If running in production, it might be easier to use a 3rd party SMTP server as i
### _APP_SMTP_HOST
SMTP server host name address. Default value is: 'smtp'
SMTP server host name address. Default value is: 'smtp'. Pass an empty string to disable all mail sending from the server.
### _APP_SMTP_PORT

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

@ -1 +1 @@
<svg version="1" xmlns="http://www.w3.org/2000/svg" width="512" height="342" viewBox="0 0 5120 3420"><g fill="#788c99"><path d="M1460 3413c-166-27-376-95-523-170-475-241-815-693-916-1216-30-155-30-479 0-634 59-307 214-616 422-840 243-264 510-420 887-520 86-23 118-26 340-30 220-4 255-3 346 16 149 31 336 94 447 151l97 50 98-50c110-57 297-120 446-151 91-19 126-20 346-16 222 4 254 7 340 30 310 82 529 194 740 377 293 255 496 605 569 983 30 155 30 479 0 634-59 307-215 617-424 842-241 261-509 418-885 518-86 23-118 26-340 30-220 4-255 3-346-16-149-31-336-94-446-151l-98-50-97 50c-110 57-298 120-444 150-81 17-133 20-316 19-120-1-229-4-243-6zm514-377c183-35 467-179 611-308 154-138 235-244 322-420 111-225 137-336 137-598s-26-373-137-598c-87-176-168-282-322-420-144-129-428-273-611-308-101-19-420-19-529 0-119 21-302 95-434 174-255 154-456 403-569 704-63 169-76 243-76 448s13 279 76 448c113 301 314 550 569 704 126 76 314 153 424 173 100 18 443 19 539 1z"/><path d="M1500 2128c0-7 26-123 59-258 135-553 179-740 180-767 1-9 25-13 86-13 47 0 85 3 85 8 0 15-109 485-197 850l-47 192h-83c-60 0-83-4-83-12zM1257 1806c-21-22-62-68-92-100-33-36-51-64-47-72 4-6 47-55 96-108l88-96h104c83 0 104 3 104 14 0 8-3 16-7 18-5 2-45 43-91 91l-82 89 26 31c14 18 55 62 90 98 35 37 64 70 64 73s-48 5-107 5l-108-1-38-42zM1900 1842c0-4 40-50 89-103l89-95-89-94c-48-52-88-101-89-107 0-10 27-13 103-13h103l85 93c46 50 89 99 96 108 10 13-3 31-84 117l-96 102h-103c-57 0-104-3-104-8z"/></g></svg>
<svg viewBox="0 0 272 182" xmlns="http://www.w3.org/2000/svg" xmlns:bx="https://boxy-svg.com"><path d="M271.058 91.03c0 49.722-40.306 90.028-90.028 90.028-23.096 0-44.159-8.697-60.096-22.993 25.531-11.493 43.305-37.157 43.305-66.971 0-29.852-17.818-55.542-43.4-67.014 15.949-14.346 37.05-23.078 60.191-23.078 49.722 0 90.028 40.306 90.028 90.028z" fill="#788c99"/><path d="M145.043 19.167a90.028 90.028 0 10-108.46 143.725 90.028 90.028 0 10108.46-143.725zM135.03 32.434a73.407 73.407 0 01-88.435 117.19 73.407 73.407 0 0188.435-117.19z" bx:shape="ring 186.727 313.243 73.407 73.407 90.028 90.028 1@7a525c32" bx:origin="0.499777 0.499705" fill="#788c99"/><path d="M93.225 59.882c-.108.266-1.485 5.672-2.967 12.084-1.538 6.413-3.976 16.535-5.351 22.524-2.653 10.969-4.243 18.177-4.243 19.132 0 .263 1.644.477 3.657.477h3.658l1.641-7.315c.956-3.974 3.076-13.09 4.771-20.244 1.697-7.155 3.762-15.847 4.559-19.345.793-3.499 1.588-6.676 1.749-7.047.159-.477-.744-.635-3.499-.635-2.068 0-3.871.158-3.975.369zM64.764 82.829l-4.875 5.301 1.432 1.695c.792.953 2.968 3.339 4.822 5.301l3.392 3.602h9.646l-4.557-4.929c-2.493-2.647-4.56-5.193-4.56-5.51 0-.371 1.909-2.755 4.241-5.299 2.331-2.596 4.24-4.824 4.24-5.089 0-.212-2.014-.371-4.451-.371h-4.399l-4.931 5.299zm37.098-4.982c0 .161.902 1.166 2.013 2.28 4.137 4.133 7.051 7.633 6.892 8.32-.106.373-2.121 2.861-4.557 5.458l-4.398 4.823h4.927l4.929-.052 4.503-4.928c2.493-2.754 4.506-5.196 4.506-5.513 0-.263-2.119-2.755-4.77-5.562l-4.768-5.143h-4.613c-2.597 0-4.664.159-4.664.317z" fill="#788c99"/></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1 +1 @@
<svg version="1" xmlns="http://www.w3.org/2000/svg" width="512" height="342" viewBox="0 0 5120 3420"><g fill="#f02e65"><path d="M1460 3413c-166-27-376-95-523-170-475-241-815-693-916-1216-30-155-30-479 0-634 59-307 214-616 422-840 243-264 510-420 887-520 86-23 118-26 340-30 220-4 255-3 346 16 149 31 336 94 447 151l97 50 98-50c110-57 297-120 446-151 91-19 126-20 346-16 222 4 254 7 340 30 310 82 529 194 740 377 293 255 496 605 569 983 30 155 30 479 0 634-59 307-215 617-424 842-241 261-509 418-885 518-86 23-118 26-340 30-220 4-255 3-346-16-149-31-336-94-446-151l-98-50-97 50c-110 57-298 120-444 150-81 17-133 20-316 19-120-1-229-4-243-6zm514-377c183-35 467-179 611-308 154-138 235-244 322-420 111-225 137-336 137-598s-26-373-137-598c-87-176-168-282-322-420-144-129-428-273-611-308-101-19-420-19-529 0-119 21-302 95-434 174-255 154-456 403-569 704-63 169-76 243-76 448s13 279 76 448c113 301 314 550 569 704 126 76 314 153 424 173 100 18 443 19 539 1z"/><path d="M1500 2128c0-7 26-123 59-258 135-553 179-740 180-767 1-9 25-13 86-13 47 0 85 3 85 8 0 15-109 485-197 850l-47 192h-83c-60 0-83-4-83-12zM1257 1806c-21-22-62-68-92-100-33-36-51-64-47-72 4-6 47-55 96-108l88-96h104c83 0 104 3 104 14 0 8-3 16-7 18-5 2-45 43-91 91l-82 89 26 31c14 18 55 62 90 98 35 37 64 70 64 73s-48 5-107 5l-108-1-38-42zM1900 1842c0-4 40-50 89-103l89-95-89-94c-48-52-88-101-89-107 0-10 27-13 103-13h103l85 93c46 50 89 99 96 108 10 13-3 31-84 117l-96 102h-103c-57 0-104-3-104-8z"/></g></svg>
<svg viewBox="0 0 272 182" xmlns="http://www.w3.org/2000/svg" xmlns:bx="https://boxy-svg.com"><path d="M271.058 91.03c0 49.722-40.306 90.028-90.028 90.028-23.096 0-44.159-8.697-60.096-22.993 25.531-11.493 43.305-37.157 43.305-66.971 0-29.852-17.818-55.542-43.4-67.014 15.949-14.346 37.05-23.078 60.191-23.078 49.722 0 90.028 40.306 90.028 90.028z" fill="#f02e65"/><path d="M145.043 19.167a90.028 90.028 0 10-108.46 143.725 90.028 90.028 0 10108.46-143.725zM135.03 32.434a73.407 73.407 0 01-88.435 117.19 73.407 73.407 0 0188.435-117.19z" bx:shape="ring 186.727 313.243 73.407 73.407 90.028 90.028 1@7a525c32" bx:origin="0.499777 0.499705" fill="#f02e65"/><path d="M93.225 59.882c-.108.266-1.485 5.672-2.967 12.084-1.538 6.413-3.976 16.535-5.351 22.524-2.653 10.969-4.243 18.177-4.243 19.132 0 .263 1.644.477 3.657.477h3.658l1.641-7.315c.956-3.974 3.076-13.09 4.771-20.244 1.697-7.155 3.762-15.847 4.559-19.345.793-3.499 1.588-6.676 1.749-7.047.159-.477-.744-.635-3.499-.635-2.068 0-3.871.158-3.975.369zM64.764 82.829l-4.875 5.301 1.432 1.695c.792.953 2.968 3.339 4.822 5.301l3.392 3.602h9.646l-4.557-4.929c-2.493-2.647-4.56-5.193-4.56-5.51 0-.371 1.909-2.755 4.241-5.299 2.331-2.596 4.24-4.824 4.24-5.089 0-.212-2.014-.371-4.451-.371h-4.399l-4.931 5.299zm37.098-4.982c0 .161.902 1.166 2.013 2.28 4.137 4.133 7.051 7.633 6.892 8.32-.106.373-2.121 2.861-4.557 5.458l-4.398 4.823h4.927l4.929-.052 4.503-4.928c2.493-2.754 4.506-5.196 4.506-5.513 0-.263-2.119-2.755-4.77-5.562l-4.768-5.143h-4.613c-2.597 0-4.664.159-4.664.317z" fill="#f02e65"/></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1,25 +1,3 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="1024.000000pt" height="1024.000000pt" viewBox="0 0 1024.000000 1024.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,1024.000000) scale(0.100000,-0.100000)"
fill="#96b7eb" stroke="none">
<path d="M5000 10143 c-904 -20 -1805 -293 -2565 -776 -1163 -740 -1970 -1911
-2240 -3252 -68 -337 -96 -634 -96 -1000 0 -483 55 -896 180 -1350 268 -971
836 -1854 1609 -2501 868 -726 1911 -1129 3032 -1173 700 -27 1388 85 2020
331 1324 514 2364 1554 2878 2878 197 508 309 1052 331 1610 42 1074 -243
2099 -830 2983 -742 1118 -1887 1889 -3194 2152 -354 71 -756 106 -1125 98z
m375 -673 c778 -48 1359 -279 1826 -728 377 -361 594 -768 676 -1267 25 -156
25 -525 -1 -680 -36 -221 -88 -386 -182 -583 -175 -368 -480 -723 -1099 -1281
-310 -279 -556 -524 -649 -647 -84 -111 -194 -331 -235 -474 -51 -175 -81
-419 -97 -803 l-7 -157 -506 2 -506 3 1 300 c1 508 55 822 199 1160 175 408
417 696 1105 1316 645 582 814 806 890 1181 39 188 26 461 -31 653 -122 415
-477 811 -894 997 -252 113 -552 162 -849 138 -373 -29 -659 -142 -929 -368
-301 -253 -521 -690 -621 -1237 -19 -103 -25 -120 -42 -122 -10 -2 -253 26
-539 60 l-520 62 3 60 c3 73 43 289 83 447 98 390 273 757 497 1043 84 107
305 322 417 405 120 90 277 185 402 245 446 213 1020 311 1608 275z m355
-8130 l0 -600 -600 0 -600 0 0 600 0 600 600 0 600 0 0 -600z"/>
</g>
</svg>
<svg height="100" width="100">
<circle cx="50" cy="50" r="40" fill="#c9c9c9" />
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 88 B

View file

@ -29,7 +29,7 @@
--config-color-success: #1bbf61;
--config-color-warning: #ffed4d;
--config-color-info: #386fd2;
--config-border-color: #f5f5f5;
--config-border-color: #f3f3f3;
--config-border-fade: #e0e3e4;
--config-border-radius: 10px;
--config-prism-background: #373738;

View file

@ -23,7 +23,6 @@ class Auth
const USER_ROLE_OWNER = 4;
const USER_ROLE_APP = 5;
const USER_ROLE_SYSTEM = 6;
const USER_ROLE_ALL = '*';
/**
* Token Types.
@ -49,9 +48,9 @@ class Auth
/**
* User Unique ID.
*
* @var int
* @var string
*/
public static $unique = 0;
public static $unique = '';
/**
* User Secret Key.
@ -75,7 +74,7 @@ class Auth
/**
* Encode Session.
*
* @param int $id
* @param string $id
* @param string $secret
*
* @return string
@ -206,4 +205,40 @@ class Auth
return false;
}
/**
* Is Previligged User?
*
* @param array $roles
*
* @return bool
*/
public static function isPreviliggedUser(array $roles): bool
{
if(
array_key_exists('role:'.self::USER_ROLE_OWNER, $roles) ||
array_key_exists('role:'.self::USER_ROLE_DEVELOPER, $roles) ||
array_key_exists('role:'.self::USER_ROLE_ADMIN, $roles)
) {
return true;
}
return false;
}
/**
* Is App User?
*
* @param array $roles
*
* @return bool
*/
public static function isAppUser(array $roles): bool
{
if(array_key_exists('role:'.self::USER_ROLE_APP, $roles)) {
return true;
}
return false;
}
}

View file

@ -455,13 +455,28 @@ class Database
foreach ($rules as $key => $rule) {
$key = $rule->getAttribute('key', null);
$filters = $rule->getAttribute('filter', null);
$type = $rule->getAttribute('type', null);
$array = $rule->getAttribute('array', false);
$filters = $rule->getAttribute('filter', []);
$value = $document->getAttribute($key, null);
if (($value !== null) && is_array($filters)) {
foreach ($filters as $filter) {
$value = $this->encodeAttribute($filter, $value);
$document->setAttribute($key, $value);
if (($value !== null)) {
if ($type === self::SYSTEM_VAR_TYPE_DOCUMENT) {
if($array) {
$list = [];
foreach ($value as $child) {
$list[] = $this->encode($child);
}
$document->setAttribute($key, $list);
} else {
$document->setAttribute($key, $this->encode($value));
}
} else {
foreach ($filters as $filter) {
$value = $this->encodeAttribute($filter, $value);
$document->setAttribute($key, $value);
}
}
}
}
@ -476,13 +491,28 @@ class Database
foreach ($rules as $key => $rule) {
$key = $rule->getAttribute('key', null);
$filters = $rule->getAttribute('filter', null);
$type = $rule->getAttribute('type', null);
$array = $rule->getAttribute('array', false);
$filters = $rule->getAttribute('filter', []);
$value = $document->getAttribute($key, null);
if (($value !== null) && is_array($filters)) {
foreach (array_reverse($filters) as $filter) {
$value = $this->decodeAttribute($filter, $value);
$document->setAttribute($key, $value);
if (($value !== null)) {
if ($type === self::SYSTEM_VAR_TYPE_DOCUMENT) {
if($array) {
$list = [];
foreach ($value as $child) {
$list[] = $this->decode($child);
}
$document->setAttribute($key, $list);
} else {
$document->setAttribute($key, $this->decode($value));
}
} else {
foreach (array_reverse($filters) as $filter) {
$value = $this->decodeAttribute($filter, $value);
$document->setAttribute($key, $value);
}
}
}
}
@ -499,6 +529,7 @@ class Database
static protected function encodeAttribute(string $name, $value)
{
if (!isset(self::$filters[$name])) {
return $value;
throw new Exception('Filter not found');
}
@ -520,6 +551,7 @@ class Database
static protected function decodeAttribute(string $name, $value)
{
if (!isset(self::$filters[$name])) {
return $value;
throw new Exception('Filter not found');
}

View file

@ -10,7 +10,7 @@ class Authorization extends Validator
/**
* @var array
*/
static $roles = ['*'];
static $roles = ['*' => true];
/**
* @var Document
@ -77,7 +77,7 @@ class Authorization extends Validator
foreach ($permissions[$this->action] as $permission) {
$permission = \str_replace(':{self}', ':'.$this->document->getId(), $permission);
if (\in_array($permission, self::getRoles())) {
if (\array_key_exists($permission, self::$roles)) {
return true;
}
}
@ -92,17 +92,45 @@ class Authorization extends Validator
*
* @return void
*/
public static function setRole($role): void
public static function setRole(string $role): void
{
self::$roles[] = $role;
self::$roles[$role] = true;
}
/**
* @param string $role
*
* @return void
*/
public static function unsetRole(string $role): void
{
unset(self::$roles[$role]);
}
/**
* @return array
*/
public static function getRoles()
public static function getRoles(): array
{
return self::$roles;
return \array_keys(self::$roles);
}
/**
* @return void
*/
public static function cleanRoles(): void
{
self::$roles = [];
}
/**
* @param string $role
*
* @return bool
*/
public static function isRole(string $role): bool
{
return (\array_key_exists($role, self::$roles));
}
/**

View file

@ -33,6 +33,42 @@ class Event
$this->class = $class;
}
/**
* @param string $queue
* return $this
*/
public function setQueue(string $queue): self
{
$this->queue = $queue;
return $this;
}
/**
* @return string
*/
public function getQueue()
{
return $this->queue;
}
/**
* @param string $class
* return $this
*/
public function setClass(string $class): self
{
$this->class = $class;
return $this;
}
/**
* @return string
*/
public function getClass()
{
return $this->class;
}
/**
* @param string $key
* @param mixed $value

View file

@ -0,0 +1,124 @@
<?php
namespace Appwrite\Specification;
use Utopia\App;
use Utopia\Route;
use Appwrite\Utopia\Response\Model;
abstract class Format
{
/**
* @var App
*/
protected $app;
/**
* @var Route[]
*/
protected $routes;
/**
* @var Model[]
*/
protected $models;
/**
* @var array
*/
protected $keys;
/**
* @var array
*/
protected $security;
/**
* @var array
*/
protected $params = [
'name' => '',
'description' => '',
'endpoint' => 'https://localhost',
'version' => '1.0.0',
'terms' => '',
'support.email' => '',
'support.url' => '',
'contact.name' => '',
'contact.email' => '',
'contact.url' => '',
'license.name' => '',
'license.url' => '',
];
/**
* @param App $app
* @param Route[] $routes
* @param Model[] $models
* @param array $keys
* @param array $security
*/
public function __construct(App $app, array $routes, array $models, array $keys, array $security)
{
$this->app = $app;
$this->routes = $routes;
$this->models = $models;
$this->keys = $keys;
$this->security = $security;
}
/**
* Get Name.
*
* Get format name
*
* @return string
*/
abstract public function getName(): string;
/**
* Parse
*
* Parses Appwrite App to given format
*
* @return array
*/
abstract public function parse(): array;
/**
* Set Param.
*
* Set param value
*
* @param string $key
* @param string $value
*
* @return self
*/
public function setParam(string $key, string $value): self
{
$this->params[$key] = $value;
return $this;
}
/**
* Get Param.
*
* Get param value
*
* @param string $key
* @param string $default
*
* @return string
*/
public function getParam(string $key, string $default = ''): string
{
if(!isset($this->params[$key])) {
return $default;
}
return $this->params[$key];
}
}

View file

@ -0,0 +1,403 @@
<?php
namespace Appwrite\Specification\Format;
use Appwrite\Specification\Format;
use Appwrite\Template\Template;
class OpenAPI3 extends Format
{
/**
* Get Name.
*
* Get format name
*
* @return string
*/
public function getName():string
{
return 'Open API 3';
}
/**
* Parse
*
* Parses Appwrite App to given format
*
* @return array
*/
public function parse(): array
{
/*
* Specifications (v3.0.0):
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
*/
$output = [
'openapi' => '3.0.0',
'info' => [
'version' => $this->getParam('version'),
'title' => $this->getParam('name'),
'description' => $this->getParam('description'),
'termsOfService' => $this->getParam('terms'),
'contact' => [
'name' => $this->getParam('contact.name'),
'url' => $this->getParam('contact.url'),
'email' => $this->getParam('contact.email'),
],
'license' => [
'name' => 'BSD-3-Clause',
'url' => 'https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE',
],
],
'servers' => [
[
'url' => $this->getParam('endpoint', ''),
],
],
'paths' => [],
'components' => [
'schemas' => [],
'securitySchemes' => $this->keys,
],
'externalDocs' => [
'description' => $this->getParam('docs.description'),
'url' => $this->getParam('docs.url'),
],
];
if (isset($output['components']['securitySchemes']['Project'])) {
$output['components']['securitySchemes']['Project']['x-appwrite'] = ['demo' => '5df5acd0d48c2'];
}
if (isset($output['components']['securitySchemes']['Key'])) {
$output['components']['securitySchemes']['Key']['x-appwrite'] = ['demo' => '919c2d18fb5d4...a2ae413da83346ad2'];
}
if (isset($output['components']['securitySchemes']['Locale'])) {
$output['components']['securitySchemes']['Locale']['x-appwrite'] = ['demo' => 'en'];
}
if (isset($output['components']['securitySchemes']['Mode'])) {
$output['components']['securitySchemes']['Mode']['x-appwrite'] = ['demo' => ''];
}
foreach ($this->routes as $route) { /* @var $route \Utopia\Route */
$url = \str_replace('/v1', '', $route->getURL());
$scope = $route->getLabel('scope', '');
$hide = $route->getLabel('sdk.hide', false);
$consumes = [$route->getLabel('sdk.request.type', 'application/json')];
if ($hide) {
continue;
}
$id = $route->getLabel('sdk.method', \uniqid());
$desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath(__DIR__.'/../../../../'.$route->getLabel('sdk.description', '')) : null;
$produces = $route->getLabel('sdk.response.type', 'application/json');
$model = $route->getLabel('sdk.response.model', 'none');
$temp = [
'summary' => $route->getDesc(),
'operationId' => $route->getLabel('sdk.namespace', 'default').ucfirst($id),
// 'consumes' => [],
// 'produces' => [$produces],
'tags' => [$route->getLabel('sdk.namespace', 'default')],
'description' => ($desc) ? \file_get_contents($desc) : '',
'responses' => [],
'x-appwrite' => [ // Appwrite related metadata
'method' => $route->getLabel('sdk.method', \uniqid()),
'weight' => $route->getOrder(),
'cookies' => $route->getLabel('sdk.cookies', false),
'type' => $route->getLabel('sdk.methodType', ''),
'demo' => 'docs/examples/'. Template::fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')).'/'.Template::fromCamelCaseToDash($id).'.md',
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $route->getLabel('sdk.description', ''),
'rate-limit' => $route->getLabel('abuse-limit', 0),
'rate-time' => $route->getLabel('abuse-time', 3600),
'rate-key' => $route->getLabel('abuse-key', 'url:{url},ip:{ip}'),
'scope' => $route->getLabel('scope', ''),
'platforms' => $route->getLabel('sdk.platform', []),
],
];
foreach ($this->models as $key => $value) {
if($value->getType() === $model) {
$model = $value;
break;
}
}
if($model->isNone()) {
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
'description' => (in_array($produces, [
'image/*',
'image/jpeg',
'image/gif',
'image/png',
'image/webp',
'image/svg-x',
'image/x-icon',
'image/bmp',
])) ? 'Image' : 'File',
// 'schema' => [
// 'type' => 'file'
// ],
];
} else {
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
'description' => $model->getName(),
'content' => [
$produces => [
'schema' => [
'$ref' => '#/components/schemas/'.$model->getType(),
],
],
],
];
}
if($route->getLabel('sdk.response.code', 500) === 204) {
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['description'] = 'No content';
unset($temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['schema']);
}
if ((!empty($scope))) { // && 'public' != $scope
$temp['security'][] = $route->getLabel('sdk.security', $this->security);
}
$body = [
'content' => [
$consumes[0] => [
'schema' => [
'type' => 'object',
'properties' => [],
],
],
],
];
$bodyRequired = [];
foreach ($route->getParams() as $name => $param) { // Set params
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['resources'])) : $param['validator']; /* @var $validator \Utopia\Validator */
$node = [
'name' => $name,
'description' => $param['description'],
'required' => !$param['optional'],
];
switch ((!empty($validator)) ? \get_class($validator) : '') {
case 'Utopia\Validator\Text':
$node['schema']['type'] = 'string';
$node['schema']['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
break;
case 'Utopia\Validator\Boolean':
$node['schema']['type'] = 'boolean';
$node['schema']['x-example'] = false;
break;
case 'Appwrite\Database\Validator\UID':
$node['schema']['type'] = 'string';
$node['schema']['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
break;
case 'Utopia\Validator\Email':
$node['schema']['type'] = 'string';
$node['schema']['format'] = 'email';
$node['schema']['x-example'] = 'email@example.com';
break;
case 'Utopia\Validator\URL':
$node['schema']['type'] = 'string';
$node['schema']['format'] = 'url';
$node['schema']['x-example'] = 'https://example.com';
break;
case 'Utopia\Validator\JSON':
case 'Utopia\Validator\Mock':
case 'Utopia\Validator\Assoc':
$node['schema']['type'] = 'object';
$node['schema']['type'] = 'object';
$node['schema']['x-example'] = '{}';
//$node['schema']['format'] = 'json';
break;
case 'Appwrite\Storage\Validator\File':
$consumes = ['multipart/form-data'];
$node['schema']['type'] = 'string';
$node['schema']['format'] = 'binary';
break;
case 'Utopia\Validator\ArrayList':
$node['schema']['type'] = 'array';
$node['schema']['items'] = [
'type' => 'string',
];
break;
case 'Appwrite\Auth\Validator\Password':
$node['schema']['type'] = 'string';
$node['schema']['format'] = 'format';
$node['schema']['x-example'] = 'password';
break;
case 'Utopia\Validator\Range': /* @var $validator \Utopia\Validator\Range */
$node['schema']['type'] = 'integer';
$node['schema']['format'] = 'int32';
$node['schema']['x-example'] = $validator->getMin();
break;
case 'Utopia\Validator\Numeric':
$node['schema']['type'] = 'integer';
$node['schema']['format'] = 'int32';
break;
case 'Utopia\Validator\Length':
$node['schema']['type'] = 'string';
break;
case 'Utopia\Validator\Host':
$node['schema']['type'] = 'string';
$node['schema']['format'] = 'url';
$node['schema']['x-example'] = 'https://example.com';
break;
case 'Utopia\Validator\WhiteList': /* @var $validator \Utopia\Validator\WhiteList */
$node['schema']['type'] = 'string';
$node['schema']['x-example'] = $validator->getList()[0];
break;
default:
$node['schema']['type'] = 'string';
break;
}
if ($param['optional'] && !\is_null($param['default'])) { // Param has default value
$node['schema']['default'] = $param['default'];
}
if (false !== \strpos($url, ':'.$name)) { // Param is in URL path
$node['in'] = 'path';
$temp['parameters'][] = $node;
} elseif ($route->getMethod() == 'GET') { // Param is in query
$node['in'] = 'query';
$temp['parameters'][] = $node;
} else { // Param is in payload
if(!$param['optional']) {
$bodyRequired[] = $name;
}
$body['content'][$consumes[0]]['schema']['properties'][$name] = [
'type' => $node['schema']['type'],
'description' => $node['description'],
'x-example' => $node['x-example'] ?? null,
];
if(isset($node['default'])) {
$body['content'][$consumes[0]]['schema']['properties'][$name]['default'] = $node['default'];
}
if(\array_key_exists('items', $node['schema'])) {
$body['content'][$consumes[0]]['schema']['properties'][$name]['items'] = $node['schema']['items'];
}
}
$url = \str_replace(':'.$name, '{'.$name.'}', $url);
}
if(!empty($bodyRequired)) {
$body['content'][$consumes[0]]['schema']['required'] = $bodyRequired;
}
if(!empty($body['content'][$consumes[0]]['schema']['properties'])) {
$temp['requestBody'] = $body;
}
//$temp['consumes'] = $consumes;
$output['paths'][$url][\strtolower($route->getMethod())] = $temp;
}
foreach ($this->models as $model) {
$required = $model->getRequired();
$rules = $model->getRules();
$output['components']['schemas'][$model->getType()] = [
'description' => $model->getName(),
'type' => 'object',
];
if(!empty($rules)) {
$output['components']['schemas'][$model->getType()]['properties'] = [];
}
if($model->isAny()) {
$output['components']['schemas'][$model->getType()]['additionalProperties'] = true;
}
if(!empty($required)) {
$output['components']['schemas'][$model->getType()]['required'] = $required;
}
foreach($model->getRules() as $name => $rule) {
$type = '';
$format = null;
$items = null;
switch ($rule['type']) {
case 'string':
case 'json':
$type = 'string';
break;
case 'integer':
$type = 'integer';
$format = 'int32';
break;
case 'float':
$type = 'number';
$format = 'float';
break;
case 'boolean':
$type = 'boolean';
break;
default:
$type = 'object';
$rule['type'] = ($rule['type']) ? $rule['type'] : 'none';
$items = [
'$ref' => '#/components/schemas/'.$rule['type'],
];
break;
}
if($rule['array']) {
$output['components']['schemas'][$model->getType()]['properties'][$name] = [
'type' => 'array',
'description' => $rule['description'] ?? '',
'items' => [
'type' => $type,
]
];
if($format) {
$output['components']['schemas'][$model->getType()]['properties'][$name]['items']['format'] = $format;
}
if($items) {
$output['components']['schemas'][$model->getType()]['properties'][$name]['items'] = $items;
}
} else {
$output['components']['schemas'][$model->getType()]['properties'][$name] = [
'type' => $type,
'description' => $rule['description'] ?? '',
//'default' => $rule['default'] ?? null,
'x-example' => $rule['example'] ?? null,
];
if($format) {
$output['components']['schemas'][$model->getType()]['properties'][$name]['format'] = $format;
}
if($items) {
$output['components']['schemas'][$model->getType()]['properties'][$name]['items'] = $items;
}
}
}
}
\ksort($output['paths']);
return $output;
}
}

View file

@ -0,0 +1,401 @@
<?php
namespace Appwrite\Specification\Format;
use Appwrite\Specification\Format;
use Appwrite\Template\Template;
class Swagger2 extends Format
{
/**
* Get Name.
*
* Get format name
*
* @return string
*/
public function getName():string
{
return 'Swagger 2';
}
/**
* Parse
*
* Parses Appwrite App to given format
*
* @return array
*/
public function parse(): array
{
/*
* Specifications (v3.0.0):
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
*/
$output = [
'swagger' => '2.0',
'info' => [
'version' => $this->getParam('version'),
'title' => $this->getParam('name'),
'description' => $this->getParam('description'),
'termsOfService' => $this->getParam('terms'),
'contact' => [
'name' => $this->getParam('contact.name'),
'url' => $this->getParam('contact.url'),
'email' => $this->getParam('contact.email'),
],
'license' => [
'name' => 'BSD-3-Clause',
'url' => 'https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE',
],
],
'host' => \parse_url($this->getParam('endpoint', ''), PHP_URL_HOST),
'basePath' => \parse_url($this->getParam('endpoint', ''), PHP_URL_PATH),
'schemes' => [\parse_url($this->getParam('endpoint', ''), PHP_URL_SCHEME)],
'consumes' => ['application/json', 'multipart/form-data'],
'produces' => ['application/json'],
'securityDefinitions' => $this->keys,
'paths' => [],
'definitions' => [],
'externalDocs' => [
'description' => $this->getParam('docs.description'),
'url' => $this->getParam('docs.url'),
],
];
if (isset($output['securityDefinitions']['Project'])) {
$output['securityDefinitions']['Project']['x-appwrite'] = ['demo' => '5df5acd0d48c2'];
}
if (isset($output['securityDefinitions']['Key'])) {
$output['securityDefinitions']['Key']['x-appwrite'] = ['demo' => '919c2d18fb5d4...a2ae413da83346ad2'];
}
if (isset($output['securityDefinitions']['Locale'])) {
$output['securityDefinitions']['Locale']['x-appwrite'] = ['demo' => 'en'];
}
if (isset($output['securityDefinitions']['Mode'])) {
$output['securityDefinitions']['Mode']['x-appwrite'] = ['demo' => ''];
}
foreach ($this->routes as $route) { /* @var $route \Utopia\Route */
$url = \str_replace('/v1', '', $route->getURL());
$scope = $route->getLabel('scope', '');
$hide = $route->getLabel('sdk.hide', false);
$consumes = [$route->getLabel('sdk.request.type', 'application/json')];
if ($hide) {
continue;
}
$id = $route->getLabel('sdk.method', \uniqid());
$desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath(__DIR__.'/../../../../'.$route->getLabel('sdk.description', '')) : null;
$produces = $route->getLabel('sdk.response.type', 'application/json');
$model = $route->getLabel('sdk.response.model', 'none');
$temp = [
'summary' => $route->getDesc(),
'operationId' => $route->getLabel('sdk.namespace', 'default').ucfirst($id),
'consumes' => [],
'produces' => [$produces],
'tags' => [$route->getLabel('sdk.namespace', 'default')],
'description' => ($desc) ? \file_get_contents($desc) : '',
'responses' => [],
'x-appwrite' => [ // Appwrite related metadata
'method' => $route->getLabel('sdk.method', \uniqid()),
'weight' => $route->getOrder(),
'cookies' => $route->getLabel('sdk.cookies', false),
'type' => $route->getLabel('sdk.methodType', ''),
'demo' => 'docs/examples/'. Template::fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')).'/'.Template::fromCamelCaseToDash($id).'.md',
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $route->getLabel('sdk.description', ''),
'rate-limit' => $route->getLabel('abuse-limit', 0),
'rate-time' => $route->getLabel('abuse-time', 3600),
'rate-key' => $route->getLabel('abuse-key', 'url:{url},ip:{ip}'),
'scope' => $route->getLabel('scope', ''),
'platforms' => $route->getLabel('sdk.platform', []),
],
];
foreach ($this->models as $key => $value) {
if($value->getType() === $model) {
$model = $value;
break;
}
}
if($model->isNone()) {
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
'description' => (in_array($produces, [
'image/*',
'image/jpeg',
'image/gif',
'image/png',
'image/webp',
'image/svg-x',
'image/x-icon',
'image/bmp',
])) ? 'Image' : 'File',
'schema' => [
'type' => 'file'
],
];
} else {
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')] = [
'description' => $model->getName(),
'schema' => [
'$ref' => '#/definitions/'.$model->getType(),
],
];
}
if($route->getLabel('sdk.response.code', 500) === 204) {
$temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['description'] = 'No content';
unset($temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['schema']);
}
if ((!empty($scope))) { // && 'public' != $scope
$temp['security'][] = $route->getLabel('sdk.security', $this->security);
}
$body = [
'name' => 'payload',
'in' => 'body',
'schema' => [
'type' => 'object',
'properties' => [],
],
];
$bodyRequired = [];
foreach ($route->getParams() as $name => $param) { // Set params
$validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['resources'])) : $param['validator']; /* @var $validator \Utopia\Validator */
$node = [
'name' => $name,
'description' => $param['description'],
'required' => !$param['optional'],
];
switch ((!empty($validator)) ? \get_class($validator) : '') {
case 'Utopia\Validator\Text':
$node['type'] = 'string';
$node['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
break;
case 'Utopia\Validator\Boolean':
$node['type'] = 'boolean';
$node['x-example'] = false;
break;
case 'Appwrite\Database\Validator\UID':
$node['type'] = 'string';
$node['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']';
break;
case 'Utopia\Validator\Email':
$node['type'] = 'string';
$node['format'] = 'email';
$node['x-example'] = 'email@example.com';
break;
case 'Utopia\Validator\URL':
$node['type'] = 'string';
$node['format'] = 'url';
$node['x-example'] = 'https://example.com';
break;
case 'Utopia\Validator\JSON':
case 'Utopia\Validator\Mock':
case 'Utopia\Validator\Assoc':
$node['type'] = 'object';
$node['type'] = 'object';
$node['x-example'] = '{}';
//$node['format'] = 'json';
break;
case 'Appwrite\Storage\Validator\File':
$consumes = ['multipart/form-data'];
$node['type'] = 'file';
break;
case 'Utopia\Validator\ArrayList':
$node['type'] = 'array';
$node['collectionFormat'] = 'multi';
$node['items'] = [
'type' => 'string',
];
break;
case 'Appwrite\Auth\Validator\Password':
$node['type'] = 'string';
$node['format'] = 'format';
$node['x-example'] = 'password';
break;
case 'Utopia\Validator\Range': /* @var $validator \Utopia\Validator\Range */
$node['type'] = 'integer';
$node['format'] = 'int32';
$node['x-example'] = $validator->getMin();
break;
case 'Utopia\Validator\Numeric':
$node['type'] = 'integer';
$node['format'] = 'int32';
break;
case 'Utopia\Validator\Length':
$node['type'] = 'string';
break;
case 'Utopia\Validator\Host':
$node['type'] = 'string';
$node['format'] = 'url';
$node['x-example'] = 'https://example.com';
break;
case 'Utopia\Validator\WhiteList': /* @var $validator \Utopia\Validator\WhiteList */
$node['type'] = 'string';
$node['x-example'] = $validator->getList()[0];
break;
default:
$node['type'] = 'string';
break;
}
if ($param['optional'] && !\is_null($param['default'])) { // Param has default value
$node['default'] = $param['default'];
}
if (false !== \strpos($url, ':'.$name)) { // Param is in URL path
$node['in'] = 'path';
$temp['parameters'][] = $node;
} elseif ($route->getMethod() == 'GET') { // Param is in query
$node['in'] = 'query';
$temp['parameters'][] = $node;
} else { // Param is in payload
if(\in_array('multipart/form-data', $consumes)) {
$node['in'] = 'formData';
$temp['parameters'][] = $node;
continue;
}
if(!$param['optional']) {
$bodyRequired[] = $name;
}
$body['schema']['properties'][$name] = [
'type' => $node['type'],
'description' => $node['description'],
'default' => $node['default'] ?? null,
'x-example' => $node['x-example'] ?? null,
];
if(\array_key_exists('items', $node)) {
$body['schema']['properties'][$name]['items'] = $node['items'];
}
}
$url = \str_replace(':'.$name, '{'.$name.'}', $url);
}
if(!empty($bodyRequired)) {
$body['schema']['required'] = $bodyRequired;
}
if(!empty($body['schema']['properties'])) {
$temp['parameters'][] = $body;
}
$temp['consumes'] = $consumes;
$output['paths'][$url][\strtolower($route->getMethod())] = $temp;
}
foreach ($this->models as $model) {
$required = $model->getRequired();
$rules = $model->getRules();
$output['definitions'][$model->getType()] = [
'description' => $model->getName(),
'type' => 'object',
];
if(!empty($rules)) {
$output['definitions'][$model->getType()]['properties'] = [];
}
if($model->isAny()) {
$output['definitions'][$model->getType()]['additionalProperties'] = true;
}
if(!empty($required)) {
$output['definitions'][$model->getType()]['required'] = $required;
}
foreach($model->getRules() as $name => $rule) {
$type = '';
$format = null;
$items = null;
switch ($rule['type']) {
case 'string':
case 'json':
$type = 'string';
break;
case 'integer':
$type = 'integer';
$format = 'int32';
break;
case 'float':
$type = 'number';
$format = 'float';
break;
case 'boolean':
$type = 'boolean';
break;
default:
$type = 'object';
$rule['type'] = ($rule['type']) ? $rule['type'] : 'none';
$items = [
'type' => $type,
'$ref' => '#/definitions/'.$rule['type'],
];
break;
}
if($rule['array']) {
$output['definitions'][$model->getType()]['properties'][$name] = [
'type' => 'array',
'description' => $rule['description'] ?? '',
'items' => [
'type' => $type,
]
];
if($format) {
$output['definitions'][$model->getType()]['properties'][$name]['items']['format'] = $format;
}
if($items) {
$output['definitions'][$model->getType()]['properties'][$name]['items'] = $items;
}
} else {
$output['definitions'][$model->getType()]['properties'][$name] = [
'type' => $type,
'description' => $rule['description'] ?? '',
//'default' => $rule['default'] ?? null,
'x-example' => $rule['example'] ?? null,
];
if($format) {
$output['definitions'][$model->getType()]['properties'][$name]['format'] = $format;
}
if($items) {
$output['definitions'][$model->getType()]['properties'][$name]['items'] = $items;
}
}
}
}
\ksort($output['paths']);
return $output;
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace Appwrite\Specification;
class Specification
{
/**
* @var Format
*/
protected $format;
/**
* @param Format $format
*/
public function __construct(Format $format)
{
$this->format = $format;
}
/**
* Get Name.
*
* Get format name
*
* @return string
*/
public function getName():string
{
return $this->format->getName();
}
/**
* Parse
*
* Parses Appwrite App to given format
*
* @return array
*/
public function parse(): array
{
return $this->format->parse();
}
}

View file

@ -7,6 +7,7 @@ use Utopia\Swoole\Response as SwooleResponse;
use Swoole\Http\Response as SwooleHTTPResponse;
use Appwrite\Database\Document;
use Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response\Model\None;
use Appwrite\Utopia\Response\Model\Any;
use Appwrite\Utopia\Response\Model\BaseList;
use Appwrite\Utopia\Response\Model\Collection;
@ -27,12 +28,14 @@ use Appwrite\Utopia\Response\Model\Team;
use Appwrite\Utopia\Response\Model\Locale;
use Appwrite\Utopia\Response\Model\Log;
use Appwrite\Utopia\Response\Model\Membership;
use Appwrite\Utopia\Response\Model\Permissions;
use Appwrite\Utopia\Response\Model\Phone;
use Appwrite\Utopia\Response\Model\Platform;
use Appwrite\Utopia\Response\Model\Project;
use Appwrite\Utopia\Response\Model\Rule;
use Appwrite\Utopia\Response\Model\Tag;
use Appwrite\Utopia\Response\Model\Task;
use Appwrite\Utopia\Response\Model\Token;
use Appwrite\Utopia\Response\Model\Webhook;
/**
@ -41,6 +44,7 @@ use Appwrite\Utopia\Response\Model\Webhook;
class Response extends SwooleResponse
{
// General
const MODEL_NONE = 'none';
const MODEL_ANY = 'any';
const MODEL_LOG = 'log';
const MODEL_LOG_LIST = 'logList';
@ -122,6 +126,8 @@ class Response extends SwooleResponse
{
$this
// General
->setModel(new None())
->setModel(new Any())
->setModel(new Error())
->setModel(new ErrorDev())
// Lists
@ -136,24 +142,25 @@ class Response extends SwooleResponse
->setModel(new BaseList('Functions List', self::MODEL_FUNCTION_LIST, 'functions', self::MODEL_FUNCTION))
->setModel(new BaseList('Tags List', self::MODEL_TAG_LIST, 'tags', self::MODEL_TAG))
->setModel(new BaseList('Executions List', self::MODEL_EXECUTION_LIST, 'executions', self::MODEL_EXECUTION))
->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT))
->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK))
->setModel(new BaseList('API Keys List', self::MODEL_KEY_LIST, 'keys', self::MODEL_KEY))
->setModel(new BaseList('Tasks List', self::MODEL_TASK_LIST, 'tasks', self::MODEL_TASK))
->setModel(new BaseList('Platforms List', self::MODEL_PLATFORM_LIST, 'platforms', self::MODEL_PLATFORM))
->setModel(new BaseList('Domains List', self::MODEL_DOMAIN_LIST, 'domains', self::MODEL_DOMAIN))
->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false))
->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK, true, false))
->setModel(new BaseList('API Keys List', self::MODEL_KEY_LIST, 'keys', self::MODEL_KEY, true, false))
->setModel(new BaseList('Tasks List', self::MODEL_TASK_LIST, 'tasks', self::MODEL_TASK, true, false))
->setModel(new BaseList('Platforms List', self::MODEL_PLATFORM_LIST, 'platforms', self::MODEL_PLATFORM, true, false))
->setModel(new BaseList('Domains List', self::MODEL_DOMAIN_LIST, 'domains', self::MODEL_DOMAIN, true, false))
->setModel(new BaseList('Countries List', self::MODEL_COUNTRY_LIST, 'countries', self::MODEL_COUNTRY))
->setModel(new BaseList('Continents List', self::MODEL_CONTINENT_LIST, 'continents', self::MODEL_CONTINENT))
->setModel(new BaseList('Languages List', self::MODEL_LANGUAGE_LIST, 'languages', self::MODEL_LANGUAGE))
->setModel(new BaseList('Currencies List', self::MODEL_CURRENCY_LIST, 'currencies', self::MODEL_CURRENCY))
->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE))
// Entities
->setModel(new Any())
->setModel(new Permissions())
->setModel(new Collection())
->setModel(new Rule())
->setModel(new Log())
->setModel(new User())
->setModel(new Session())
->setModel(new Token())
->setModel(new Locale())
->setModel(new File())
->setModel(new Team())
@ -215,6 +222,16 @@ class Response extends SwooleResponse
return $this->models[$key];
}
/**
* Get Models List
*
* @return Model[]
*/
public function getModels(): array
{
return $this->models;
}
/**
* Validate response objects and outputs
* the response according to given format type
@ -244,7 +261,8 @@ class Response extends SwooleResponse
$output = [];
if ($model->isAny()) {
return $document->getArrayCopy();
$this->payload = $document->getArrayCopy();
return $this->payload;
}
foreach ($model->getRules() as $key => $rule) {
@ -277,7 +295,7 @@ class Response extends SwooleResponse
$this->payload = $output;
return $output;
return $this->payload;
}
/**

View file

@ -4,11 +4,27 @@ namespace Appwrite\Utopia\Response;
abstract class Model
{
const TYPE_STRING = 'string';
const TYPE_INTEGER = 'integer';
const TYPE_FLOAT = 'float';
const TYPE_BOOLEAN = 'boolean';
const TYPE_JSON = 'json';
/**
* @var bool
*/
protected $none = false;
/**
* @var bool
*/
protected $any = false;
/**
* @var bool
*/
protected $public = true;
/**
* @var array
*/
@ -44,6 +60,7 @@ abstract class Model
protected function addRule(string $key, array $options): self
{
$this->rules[$key] = array_merge([
'require' => true,
'type' => '',
'description' => '',
'default' => null,
@ -54,8 +71,52 @@ abstract class Model
return $this;
}
public function getRequired()
{
$list = [];
foreach($this->rules as $key => $rule) {
if(isset($rule['require']) || $rule['require']) {
$list[] = $key;
}
}
return $list;
}
/**
* Is None
*
* Use to check if response is empty
*
* @return bool
*/
public function isNone(): bool
{
return $this->none;
}
/**
* Is Any
*
* Use to check if response is a wildcard
*
* @return bool
*/
public function isAny(): bool
{
return $this->any;
}
/**
* Is Public
*
* Should this model be publicly available in docs and spec files?
*
* @return bool
*/
public function isPublic(): bool
{
return $this->public;
}
}

View file

@ -17,22 +17,32 @@ class BaseList extends Model
*/
protected $type = '';
public function __construct(string $name, string $type, string $key, string $model, bool $paging = true)
/**
* @param string $name
* @param string $type
* @param string $key
* @param string $model
* @param bool $paging
* @param bool $public
*/
public function __construct(string $name, string $type, string $key, string $model, bool $paging = true, bool $public = true)
{
$this->name = $name;
$this->type = $type;
$this->public = $public;
if ($paging) {
$this->addRule('sum', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Total sum of items in the list.',
'example' => '5',
'default' => 0,
'example' => 5,
]);
}
$this->addRule($key, [
'type' => $model,
'description' => 'List of '.$key.'.',
'example' => [],
'default' => [],
'array' => true,
]);
}

View file

@ -11,7 +11,7 @@ class Collection extends Model
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Collection ID.',
'example' => '5e5ea5c16897e',
])
@ -22,24 +22,23 @@ class Collection extends Model
'array' => false,
])
->addRule('name', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Collection name.',
'example' => 'Movies',
])
->addRule('dateCreated', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Collection creation date in Unix timestamp.',
'example' => 1592981250,
])
->addRule('dateUpdated', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Collection creation date in Unix timestamp.',
'example' => 1592981550,
])
->addRule('rules', [
'type' => Response::MODEL_RULE,
'description' => 'Collection rules.',
'example' => [],
'default' => [],
'array' => true,
])

View file

@ -11,12 +11,12 @@ class Continent extends Model
{
$this
->addRule('name', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Continent name.',
'example' => 'Europe',
])
->addRule('code', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Continent two letter code.',
'example' => 'EU',
])

View file

@ -11,12 +11,12 @@ class Country extends Model
{
$this
->addRule('name', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Country name.',
'example' => 'United States',
])
->addRule('code', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Country two-character ISO 3166-1 alpha code.',
'example' => 'US',
])

View file

@ -11,42 +11,42 @@ class Currency extends Model
{
$this
->addRule('symbol', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Currency symbol.',
'example' => '$',
])
->addRule('name', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Currency name.',
'example' => 'US dollar',
])
->addRule('symbolNative', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Currency native symbol.',
'example' => '$',
])
->addRule('decimalDigits', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Number of decimal digits.',
'example' => 2,
])
->addRule('rounding', [
'type' => 'float',
'type' => self::TYPE_FLOAT,
'description' => 'Currency digit rounding.',
'example' => 0,
])
->addRule('code', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Currency code in [ISO 4217-1](http://en.wikipedia.org/wiki/ISO_4217) three-character format.',
'example' => 'USD',
])
->addRule('namePlural', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Currency plural name',
'example' => 'US dollars',
])
// ->addRule('locations', [
// 'type' => 'string',
// 'type' => self::TYPE_STRING,
// 'description' => 'Currency locations list. List of location in two-character ISO 3166-1 alpha code.',
// 'example' => ['US'],
// 'array' => true,

View file

@ -7,31 +7,36 @@ use Appwrite\Utopia\Response\Model;
class Domain extends Model
{
/**
* @var bool
*/
protected $public = false;
public function __construct()
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Domain ID.',
'example' => '5e5ea5c16897e',
])
->addRule('domain', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Domain name.',
'example' => 'appwrite.company.com',
])
->addRule('registerable', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Registerable domain name.',
'example' => 'company.com',
])
->addRule('tld', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'TLD name.',
'example' => 'com',
])
->addRule('verification', [
'type' => 'boolean',
'type' => self::TYPE_BOOLEAN,
'description' => 'Verification process status.',
'example' => true,
])

View file

@ -11,17 +11,17 @@ class Error extends Model
{
$this
->addRule('message', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Error message.',
'example' => 'Not found',
])
->addRule('code', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Error code.',
'example' => '404',
])
->addRule('version', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Server version number.',
'example' => '1.0',
])

View file

@ -6,23 +6,28 @@ use Appwrite\Utopia\Response;
class ErrorDev extends Error
{
/**
* @var bool
*/
protected $public = false;
public function __construct()
{
parent::__construct();
$this
->addRule('file', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'File path.',
'example' => '/usr/code/vendor/utopia-php/framework/src/App.php',
])
->addRule('line', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Line number.',
'example' => 209,
])
->addRule('trace', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Error trace.',
'example' => [
''

View file

@ -11,47 +11,47 @@ class Execution extends Model
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Execution ID.',
'example' => '5e5ea5c16897e',
])
->addRule('functionId', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Function ID.',
'example' => '5e5ea6g16897e',
])
->addRule('dateCreated', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'The execution creation date in Unix timestamp.',
'example' => 1592981250,
])
->addRule('trigger', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'The trigger that caused the function to execute. Possible values can be: `http`, `schedule`, or `event`.',
'example' => 'http',
])
->addRule('status', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'The status of the function execution. Possible values can be: `waiting`, `processing`, `completed`, or `failed`.',
'example' => 'processing',
])
->addRule('exitCode', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'The script exit code.',
'example' => 0,
])
->addRule('stdout', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'The script stdout output string.',
'example' => '',
])
->addRule('stderr', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'The script stderr output string.',
'example' => '',
])
->addRule('time', [
'type' => 'float',
'type' => self::TYPE_FLOAT,
'description' => 'The script execution time in seconds.',
'example' => 0.400,
])

View file

@ -11,7 +11,7 @@ class File extends Model
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'File ID.',
'example' => '5e5ea5c16897e',
])
@ -22,27 +22,27 @@ class File extends Model
'array' => false,
])
->addRule('name', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'File name.',
'example' => 'Pink.png',
])
->addRule('dateCreated', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'File creation date in Unix timestamp.',
'example' => 1592981250,
])
->addRule('signature', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'File MD5 signature.',
'example' => '5d529fd02b544198ae075bd57c1762bb',
])
->addRule('mimeType', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'File mime type.',
'example' => 'image/png',
])
->addRule('sizeOriginal', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'File original size in bytes.',
'example' => 17890,
])

View file

@ -11,74 +11,74 @@ class Func extends Model
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Function ID.',
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Function name.',
'example' => 'My Function',
])
->addRule('dateCreated', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Function creation date in Unix timestamp.',
'example' => 1592981250,
])
->addRule('dateUpdated', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Function update date in Unix timestamp.',
'example' => 1592981257,
])
->addRule('status', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Function status. Possible values: disabled, enabled',
'example' => 'enabled',
])
->addRule('env', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Function execution environment.',
'example' => 'python-3.8',
])
->addRule('tag', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Function active tag ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('vars', [
'type' => 'json',
'type' => self::TYPE_JSON,
'description' => 'Function environment variables.',
'default' => new \stdClass,
'example' => ['key' => 'value'],
])
->addRule('events', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Function trigger events.',
'default' => [],
'example' => ['account.create'],
'array' => true,
])
->addRule('schedule', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Function execution schedult in CRON format.',
'default' => '',
'example' => '5 4 * * *',
])
->addRule('scheduleNext', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Function next scheduled execution date in Unix timestamp.',
'example' => 1592981292,
'default' => 0,
])
->addRule('schedulePrevious', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Function next scheduled execution date in Unix timestamp.',
'example' => 1592981237,
'default' => 0,
])
->addRule('timeout', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Function execution timeout in seconds.',
'default' => 15,
'example' => 1592981237,

View file

@ -7,28 +7,33 @@ use Appwrite\Utopia\Response\Model;
class Key extends Model
{
/**
* @var bool
*/
protected $public = false;
public function __construct()
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Key ID.',
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Key name.',
'example' => 'My API Key',
])
->addRule('scopes', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Allowed permission scopes.',
'default' => [],
'example' => ['users.read', 'documents.write'],
'array' => true,
])
->addRule('secret', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Secret key.',
'example' => '919c2d18fb5d4...a2ae413da83346ad2',
])

View file

@ -11,17 +11,17 @@ class Language extends Model
{
$this
->addRule('name', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Language name.',
'example' => 'Italian',
])
->addRule('code', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Language two-character ISO 639-1 codes.',
'example' => 'it',
])
->addRule('nativeName', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Language native name.',
'example' => 'Italiano',
])

View file

@ -11,38 +11,38 @@ class Locale extends Model
{
$this
->addRule('ip', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'User IP address.',
'example' => '127.0.0.1',
])
->addRule('countryCode', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Country code in [ISO 3166-1](http://en.wikipedia.org/wiki/ISO_3166-1) two-character format',
'example' => 'US',
])
->addRule('country', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Country name. This field support localization.',
'example' => 'United States',
])
->addRule('continentCode', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Continent code. A two character continent code "AF" for Africa, "AN" for Antarctica, "AS" for Asia, "EU" for Europe, "NA" for North America, "OC" for Oceania, and "SA" for South America.',
'example' => 'NA',
])
->addRule('continent', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Continent name. This field support localization.',
'example' => 'North America',
])
->addRule('eu', [
'type' => 'Boolean',
'type' => self::TYPE_BOOLEAN,
'description' => 'True if country is part of the Europian Union.',
'default' => false,
'example' => false,
])
->addRule('currency', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Currency code in [ISO 4217-1](http://en.wikipedia.org/wiki/ISO_4217) three-character format',
'example' => 'USD',
])

View file

@ -11,100 +11,100 @@ class Log extends Model
{
$this
->addRule('event', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Event name.',
'example' => 'account.sessions.create',
])
->addRule('ip', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'IP session in use when the session was created.',
'example' => '127.0.0.1',
])
->addRule('time', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Log creation time in Unix timestamp.',
'example' => 1592981250,
])
->addRule('osCode', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Operating system code name. View list of [available options](https://github.com/appwrite/appwrite/blob/master/docs/lists/os.json).',
'default' => '',
'example' => 'Mac',
])
->addRule('osName', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Operating system name.',
'default' => '',
'example' => 'Mac',
])
->addRule('osVersion', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Operating system version.',
'default' => '',
'example' => 'Mac',
])
->addRule('clientType', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Client type.',
'default' => '',
'example' => 'browser',
])
->addRule('clientCode', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Client code name. View list of [available options](https://github.com/appwrite/appwrite/blob/master/docs/lists/clients.json).',
'default' => '',
'example' => 'CM',
])
->addRule('clientName', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Client name.',
'default' => '',
'example' => 'Chrome Mobile iOS',
])
->addRule('clientVersion', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Client version.',
'default' => '',
'example' => '84.0',
])
->addRule('clientEngine', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Client engine name.',
'default' => '',
'example' => 'WebKit',
])
->addRule('clientEngineVersion', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Client engine name.',
'default' => '',
'example' => '605.1.15',
])
->addRule('deviceName', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Device name.',
'default' => '',
'example' => 'smartphone',
])
->addRule('deviceBrand', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Device brand name.',
'default' => '',
'example' => 'Google',
])
->addRule('deviceModel', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Device model name.',
'default' => '',
'example' => 'Nexus 5',
])
->addRule('countryCode', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Country two-character ISO 3166-1 alpha code.',
'default' => '',
'example' => 'US',
])
->addRule('countryName', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Country name.',
'default' => '',
'example' => 'United States',

View file

@ -11,49 +11,49 @@ class Membership extends Model
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Membership ID.',
'example' => '5e5ea5c16897e',
])
->addRule('userId', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'User ID.',
'example' => '5e5ea5c16897e',
])
->addRule('teamId', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Team ID.',
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'User name.',
'default' => '',
'example' => 'VIP',
])
->addRule('email', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'User email address.',
'default' => '',
'example' => 'john@appwrite.io',
])
->addRule('invited', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Date, the user has been invited to join the team in Unix timestamp.',
'example' => 1592981250,
])
->addRule('joined', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Date, the user has accepted the invitation to join the team in Unix timestamp.',
'example' => 1592981250,
])
->addRule('confirm', [
'type' => 'boolean',
'type' => self::TYPE_BOOLEAN,
'description' => 'User confirmation status, true if the user has joined the team or false otherwise.',
'example' => false,
])
->addRule('roles', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'User list of roles',
'default' => [],
'example' => [],

View file

@ -0,0 +1,34 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class None extends Model
{
/**
* @var bool
*/
protected $none = true;
/**
* Get Name
*
* @return string
*/
public function getName():string
{
return 'None';
}
/**
* Get Collection
*
* @return string
*/
public function getType():string
{
return Response::MODEL_NONE;
}
}

View file

@ -11,13 +11,13 @@ class Permissions extends Model
{
$this
->addRule('read', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Read permissions.',
'example' => ['*', 'user:5e5ea5c16897e'],
'array' => true,
])
->addRule('write', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Write permissions.',
'example' => ['*', 'user:5e5ea5c16897e'],
'array' => true,

View file

@ -11,17 +11,17 @@ class Phone extends Model
{
$this
->addRule('code', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Phone code.',
'example' => '+1',
])
->addRule('countryCode', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Country two-character ISO 3166-1 alpha code.',
'example' => 'US',
])
->addRule('countryName', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Country name.',
'example' => 'United States',
])

View file

@ -7,47 +7,52 @@ use Appwrite\Utopia\Response\Model;
class Platform extends Model
{
/**
* @var bool
*/
protected $public = false;
public function __construct()
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Platform ID.',
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Platform name.',
'example' => 'My Web App',
])
->addRule('type', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Platform type. Possible values are: web, flutter-ios, flutter-android, ios, android, and unity.',
'example' => 'My Web App',
])
->addRule('key', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Platform Key. iOS bundle ID or Android package name. Empty string for other platforms.',
'example' => 'com.company.appname',
])
// ->addRule('store', [
// 'type' => 'string',
// 'type' => self::TYPE_STRING,
// 'description' => 'Link to platform store.',
// 'example' => '',
// ])
->addRule('hostname', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Web app hostname. Empty string for other platforms.',
'example' => true,
])
->addRule('httpUser', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'HTTP basic authentication username.',
'default' => '',
'example' => 'username',
])
->addRule('httpPass', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'HTTP basic authentication password.',
'default' => '',
'example' => 'password',

View file

@ -8,75 +8,80 @@ use Utopia\Config\Config;
class Project extends Model
{
/**
* @var bool
*/
protected $public = false;
public function __construct()
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Project ID.',
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Project name.',
'default' => '',
'example' => 'New Project',
])
->addRule('description', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Project description.',
'default' => '',
'example' => 'This is a new project.',
])
->addRule('teamId', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Project team ID.',
'example' => '1592981250',
])
->addRule('logo', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Project logo file ID.',
'default' => '',
'example' => '5f5c451b403cb',
])
->addRule('url', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Project website URL.',
'default' => '',
'example' => '5f5c451b403cb',
])
->addRule('legalName', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Company legal name.',
'default' => '',
'example' => 'Company LTD.',
])
->addRule('legalCountry', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Country code in [ISO 3166-1](http://en.wikipedia.org/wiki/ISO_3166-1) two-character format.',
'default' => '',
'example' => 'US',
])
->addRule('legalState', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'State name.',
'default' => '',
'example' => 'New York',
])
->addRule('legalCity', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'City name.',
'default' => '',
'example' => 'New York City.',
])
->addRule('legalAddress', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Company Address.',
'default' => '',
'example' => '620 Eighth Avenue, New York, NY 10018',
])
->addRule('legalTaxId', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Company Tax ID.',
'default' => '',
'example' => '131102020',
@ -129,13 +134,13 @@ class Project extends Model
$this
->addRule('usersOauth2'.\ucfirst($index).'Appid', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => $name.' OAuth app ID.',
'example' => '123247283472834787438',
'default' => '',
])
->addRule('usersOauth2'.\ucfirst($index).'Secret', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => $name.' OAuth secret ID.',
'example' => 'djsgudsdsewe43434343dd34...',
'default' => '',

View file

@ -11,48 +11,48 @@ class Rule extends Model
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Rule ID.',
'example' => '5e5ea5c16897e',
])
->addRule('$collection', [ // TODO remove this from public response
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Rule Collection.',
'example' => '5e5e66c16897e',
])
->addRule('type', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Rule type. Possible values: ',
'example' => 'title',
])
->addRule('key', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Rule key.',
'example' => 'title',
])
->addRule('label', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Rule label.',
'example' => 'Title',
])
->addRule('default', [ // TODO should be of mixed types
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Rule default value.',
'example' => 'Movie Name',
'default' => '',
])
->addRule('array', [
'type' => 'boolean',
'type' => self::TYPE_BOOLEAN,
'description' => 'Is array?',
'example' => false,
])
->addRule('required', [
'type' => 'boolean',
'type' => self::TYPE_BOOLEAN,
'description' => 'Is required?',
'example' => true,
])
->addRule('list', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'List of allowed values',
'array' => true,
'default' => [],

View file

@ -11,110 +11,116 @@ class Session extends Model
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Session ID.',
'example' => '5e5ea5c16897e',
])
->addRule('userId', [
'type' => self::TYPE_STRING,
'description' => 'User ID.',
'default' => '',
'example' => '5e5bb8c16897e',
])
->addRule('expire', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Session expiration date in Unix timestamp.',
'default' => 0,
'example' => 1592981250,
])
->addRule('ip', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'IP in use when the session was created.',
'default' => '',
'example' => '127.0.0.1',
])
->addRule('osCode', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Operating system code name. View list of [available options](https://github.com/appwrite/appwrite/blob/master/docs/lists/os.json).',
'default' => '',
'example' => 'Mac',
])
->addRule('osName', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Operating system name.',
'default' => '',
'example' => 'Mac',
])
->addRule('osVersion', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Operating system version.',
'default' => '',
'example' => 'Mac',
])
->addRule('clientType', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Client type.',
'default' => '',
'example' => 'browser',
])
->addRule('clientCode', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Client code name. View list of [available options](https://github.com/appwrite/appwrite/blob/master/docs/lists/clients.json).',
'default' => '',
'example' => 'CM',
])
->addRule('clientName', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Client name.',
'default' => '',
'example' => 'Chrome Mobile iOS',
])
->addRule('clientVersion', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Client version.',
'default' => '',
'example' => '84.0',
])
->addRule('clientEngine', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Client engine name.',
'default' => '',
'example' => 'WebKit',
])
->addRule('clientEngineVersion', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Client engine name.',
'default' => '',
'example' => '605.1.15',
])
->addRule('deviceName', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Device name.',
'default' => '',
'example' => 'smartphone',
])
->addRule('deviceBrand', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Device brand name.',
'default' => '',
'example' => 'Google',
])
->addRule('deviceModel', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Device model name.',
'default' => '',
'example' => 'Nexus 5',
])
->addRule('countryCode', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Country two-character ISO 3166-1 alpha code.',
'default' => '',
'example' => 'US',
])
->addRule('countryName', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Country name.',
'default' => '',
'example' => 'United States',
])
->addRule('current', [
'type' => 'boolean',
'type' => self::TYPE_BOOLEAN,
'description' => 'Returns true if this the current user session.',
'default' => '',
'default' => false,
'example' => true,
])
;

View file

@ -11,27 +11,27 @@ class Tag extends Model
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Tag ID.',
'example' => '5e5ea5c16897e',
])
->addRule('functionId', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Function ID.',
'example' => '5e5ea6g16897e',
])
->addRule('dateCreated', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'The tag creation date in Unix timestamp.',
'example' => 1592981250,
])
->addRule('command', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'The entrypoint command in use to execute the tag code.',
'example' => 'enabled',
])
->addRule('size', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'The code size in bytes.',
'example' => 'python-3.8',
])

View file

@ -7,95 +7,100 @@ use Appwrite\Utopia\Response\Model;
class Task extends Model
{
/**
* @var bool
*/
protected $public = false;
public function __construct()
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Task ID.',
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Task name.',
'example' => 'My Task',
])
->addRule('security', [
'type' => 'boolean',
'type' => self::TYPE_BOOLEAN,
'description' => 'Indicated if SSL / TLS Certificate verification is enabled.',
'example' => true,
])
->addRule('httpMethod', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Task HTTP Method.',
'example' => 'POST',
])
->addRule('httpUrl', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Task HTTP URL.',
'example' => 'https://example.com/task',
])
->addRule('httpHeaders', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Task HTTP headers.',
'default' => [],
'example' => ['key:value'],
'array' => true,
])
->addRule('httpUser', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'HTTP basic authentication username.',
'default' => '',
'example' => 'username',
])
->addRule('httpPass', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'HTTP basic authentication password.',
'default' => '',
'example' => 'password',
])
->addRule('duration', [
'type' => 'float',
'type' => self::TYPE_FLOAT,
'description' => 'Task duration in seconds.',
'default' => 0,
'example' => 1.2,
])
->addRule('delay', [
'type' => 'float',
'type' => self::TYPE_FLOAT,
'description' => 'Task delay time in seconds.',
'default' => 0,
'example' => 1.2,
])
->addRule('failures', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Number of recurring task failures.',
'default' => 0,
'example' => 0,
])
->addRule('schedule', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Task schedule in CRON syntax.',
'example' => '* * * * *',
])
->addRule('status', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Task status. Possible values: play, pause', // TODO - change to enabled disabled
'example' => 'enabled',
])
->addRule('updated', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Task last updated time in Unix timestamp.',
'default' => 0,
'example' => 1592981250,
])
->addRule('previous', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Task previous run time in Unix timestamp.',
'default' => 0,
'example' => 1592981250,
])
->addRule('next', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Task next run time in Unix timestamp.',
'default' => 0,
'example' => 1592981650,

View file

@ -11,23 +11,23 @@ class Team extends Model
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Team ID.',
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Team name.',
'default' => '',
'example' => 'VIP',
])
->addRule('dateCreated', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Team creation date in Unix timestamp.',
'example' => 1592981250,
])
->addRule('sum', [ // TODO change key name?
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'Total sum of team members.',
'example' => 7,
])

View file

@ -9,6 +9,30 @@ class Token extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'Token ID.',
'example' => 'bb8ea5c16897e',
])
->addRule('userId', [
'type' => self::TYPE_STRING,
'description' => 'User ID.',
'example' => '5e5ea5c168bb8',
])
->addRule('secret', [
'type' => self::TYPE_STRING,
'description' => 'Token secret key. This will return an empty string unless the response is returned using an API key or as part of a webhook payload.',
'default' => 0,
'example' => '',
])
->addRule('expire', [
'type' => self::TYPE_INTEGER,
'description' => 'Token expiration date in Unix timestamp.',
'default' => 0,
'example' => 1592981250,
])
;
}
/**
@ -18,7 +42,7 @@ class Token extends Model
*/
public function getName():string
{
return 'User';
return 'Token';
}
/**
@ -28,6 +52,6 @@ class Token extends Model
*/
public function getType():string
{
return Response::MODEL_LOCALE;
return Response::MODEL_TOKEN;
}
}

View file

@ -11,49 +11,42 @@ class User extends Model
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'User ID.',
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'User name.',
'example' => 'John Doe',
])
->addRule('registration', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'User registration date in Unix timestamp.',
'example' => 1592981250,
])
->addRule('status', [
'type' => 'integer',
'type' => self::TYPE_INTEGER,
'description' => 'User status. 0 for Unavtivated, 1 for active and 2 is blocked.',
'example' => 0,
])
->addRule('email', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'User email address.',
'example' => 'john@appwrite.io',
])
->addRule('emailVerification', [
'type' => 'boolean',
'type' => self::TYPE_BOOLEAN,
'description' => 'Email verification status.',
'default' => false,
'example' => true,
])
->addRule('prefs', [
'type' => 'json',
'type' => self::TYPE_JSON,
'description' => 'User preferences as a key-value object',
'default' => new \stdClass,
'example' => ['theme' => 'pink', 'timezone' => 'UTC'],
])
->addRule('roles', [
'type' => 'string',
'description' => 'User list of roles',
'default' => [],
'example' => [],
'array' => true,
])
;
}

View file

@ -7,44 +7,49 @@ use Appwrite\Utopia\Response\Model;
class Webhook extends Model
{
/**
* @var bool
*/
protected $public = false;
public function __construct()
{
$this
->addRule('$id', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Webhook ID.',
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Webhook name.',
'example' => 'My Webhook',
])
->addRule('url', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Webhook URL endpoint.',
'example' => 'https://example.com/webhook',
])
->addRule('events', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'Webhook trigger events.',
'default' => [],
'example' => ['database.collections.update', 'database.collections.delete'],
'array' => true,
])
->addRule('security', [
'type' => 'boolean',
'type' => self::TYPE_BOOLEAN,
'description' => 'Indicated if SSL / TLS Certificate verification is enabled.',
'example' => true,
])
->addRule('httpUser', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'HTTP basic authentication username.',
'default' => '',
'example' => 'username',
])
->addRule('httpPass', [
'type' => 'string',
'type' => self::TYPE_STRING,
'description' => 'HTTP basic authentication password.',
'default' => '',
'example' => 'password',

View file

@ -2,10 +2,13 @@
namespace Tests\E2E\Services\Account;
use CURLFile;
use Exception;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectNone;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideNone;
use Utopia\CLI\Console;
class HTTPTest extends Scope
{
@ -92,4 +95,60 @@ class HTTPTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertStringContainsString('# robotstxt.org/', $response['body']);
}
public function testSpecSwagger2()
{
$response = $this->client->call(Client::METHOD_GET, '/specs/swagger2?platform=client', [
'content-type' => 'application/json',
], []);
if(!file_put_contents(__DIR__ . '/../../resources/swagger2.json', json_encode($response['body']))) {
throw new Exception('Failed to save spec file');
}
$client = new Client();
$client->setEndpoint('https://validator.swagger.io');
/**
* Test for SUCCESS
*/
$response = $client->call(Client::METHOD_POST, '/validator/debug', [
'content-type' => 'application/json',
], json_decode(file_get_contents(realpath(__DIR__ . '/../../resources/swagger2.json')), true));
$response['body'] = json_decode($response['body'], true);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertFalse(isset($response['body']['messages']));
unlink(realpath(__DIR__ . '/../../resources/swagger2.json'));
}
public function testSpecOpenAPI3()
{
$response = $this->client->call(Client::METHOD_GET, '/specs/open-api3?platform=client', [
'content-type' => 'application/json',
], []);
if(!file_put_contents(__DIR__ . '/../../resources/open-api3.json', json_encode($response['body']))) {
throw new Exception('Failed to save spec file');
}
$client = new Client();
$client->setEndpoint('https://validator.swagger.io');
/**
* Test for SUCCESS
*/
$response = $client->call(Client::METHOD_POST, '/validator/debug', [
'content-type' => 'application/json',
], json_decode(file_get_contents(realpath(__DIR__ . '/../../resources/open-api3.json')), true));
$response['body'] = json_decode($response['body'], true);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertFalse(isset($response['body']['messages']));
unlink(realpath(__DIR__ . '/../../resources/open-api3.json'));
}
}

View file

@ -81,23 +81,64 @@ trait ProjectCustom
],
]);
$this->assertEquals(201, $project['headers']['status-code']);
$this->assertEquals(201, $key['headers']['status-code']);
$this->assertNotEmpty($key['body']);
$this->assertNotEmpty($key['body']['secret']);
// return [
// 'email' => $this->demoEmail,
// 'password' => $this->demoPassword,
// 'session' => $session,
// 'projectUid' => $project['body']['$id'],
// 'projectAPIKeySecret' => $key['body']['secret'],
// 'projectSession' => $this->client->parseCookie($user['headers']['set-cookie'])['a_session_' . $project['body']['$id']],
// ];
$webhook = $this->client->call(Client::METHOD_POST, '/projects/'.$project['body']['$id'].'/webhooks', [
'origin' => 'http://localhost',
'content-type' => 'application/json',
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
'x-appwrite-project' => 'console',
], [
'name' => 'Webhook Test',
'events' => [
'account.create',
'account.update.email',
'account.update.name',
'account.update.password',
'account.update.prefs',
'account.recovery.create',
'account.recovery.update',
'account.verification.create',
'account.verification.update',
'account.delete',
'account.sessions.create',
'account.sessions.delete',
'database.collections.create',
'database.collections.update',
'database.collections.delete',
'database.documents.create',
'database.documents.update',
'database.documents.delete',
'storage.files.create',
'storage.files.update',
'storage.files.delete',
'users.create',
'users.update.status',
'users.delete',
'users.sessions.delete',
'teams.create',
'teams.update',
'teams.delete',
'teams.memberships.create',
'teams.memberships.update.status',
'teams.memberships.delete',
],
'url' => 'http://request-catcher:5000/webhook',
'security' => false,
'httpUser' => '',
'httpPass' => '',
]);
$this->assertEquals(201, $webhook['headers']['status-code']);
$this->assertNotEmpty($webhook['body']);
self::$project = [
'$id' => $project['body']['$id'],
'name' => $project['body']['name'],
'apiKey' => $key['body']['secret'],
'webhookId' => $webhook['body']['$id'],
];
return self::$project;

View file

@ -46,7 +46,7 @@ abstract class Scope extends TestCase
protected function getLastRequest():array
{
sleep(10);
sleep(5);
$resquest = json_decode(file_get_contents('http://request-catcher:5000/__last_request__'), true);
$resquest['data'] = json_decode($resquest['data'], true);

View file

@ -11,4 +11,12 @@ trait SideClient
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $this->getUser()['session'],
];
}
/**
* @return string
*/
public function getSide()
{
return 'client';
}
}

View file

@ -8,4 +8,12 @@ trait SideNone
{
return [];
}
/**
* @return string
*/
public function getSide()
{
return 'none';
}
}

View file

@ -15,4 +15,12 @@ trait SideServer
'x-appwrite-key' => $this->getProject()['apiKey']
];
}
/**
* @return string
*/
public function getSide()
{
return 'server';
}
}

View file

@ -152,10 +152,6 @@ trait AccountBase
$this->assertIsNumeric($response['body']['registration']);
$this->assertEquals($response['body']['email'], $email);
$this->assertEquals($response['body']['name'], $name);
$this->assertContains('*', $response['body']['roles']);
$this->assertContains('user:'.$response['body']['$id'], $response['body']['roles']);
$this->assertContains('role:1', $response['body']['roles']);
$this->assertCount(3, $response['body']['roles']);
/**
* Test for FAILURE
@ -573,8 +569,8 @@ trait AccountBase
$this->assertIsArray($response['body']);
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']);
$this->assertEquals('prefValue1', $response['body']['prefKey1']);
$this->assertEquals('prefValue2', $response['body']['prefKey2']);
$this->assertEquals('prefValue1', $response['body']['prefs']['prefKey1']);
$this->assertEquals('prefValue2', $response['body']['prefs']['prefKey2']);
/**
* Test for FAILURE
@ -648,7 +644,7 @@ trait AccountBase
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEquals(2, $response['body']['type']);
$this->assertEmpty($response['body']['secret']);
$this->assertIsNumeric($response['body']['expire']);
$lastEmail = $this->getLastEmail();
@ -662,24 +658,24 @@ trait AccountBase
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_POST, '/account/recovery', array_merge([
$response = $this->client->call(Client::METHOD_POST, '/account/verification', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
]), [
'url' => 'localhost/recovery',
'url' => 'localhost/verification',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_POST, '/account/recovery', array_merge([
$response = $this->client->call(Client::METHOD_POST, '/account/verification', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
]), [
'url' => 'http://remotehost/recovery',
'url' => 'http://remotehost/verification',
]);
$this->assertEquals(400, $response['headers']['status-code']);
@ -940,7 +936,7 @@ trait AccountBase
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEquals(3, $response['body']['type']);
$this->assertEmpty($response['body']['secret']);
$this->assertIsNumeric($response['body']['expire']);
$lastEmail = $this->getLastEmail();

View file

@ -5,10 +5,119 @@ namespace Tests\E2E\Services\Database;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
use Tests\E2E\Client;
class DatabaseCustomServerTest extends Scope
{
use DatabaseBase;
use ProjectCustom;
use SideServer;
public function testDeleteCollection()
{
/**
* Test for SUCCESS
*/
// Create collection
$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']
]), [
'name' => 'Actors',
'read' => ['*'],
'write' => ['role:1', 'role:2'],
'rules' => [
[
'label' => 'First Name',
'key' => 'firstName',
'type' => 'text',
'default' => '',
'required' => true,
'array' => false
],
[
'label' => 'Last Name',
'key' => 'lastName',
'type' => 'text',
'default' => '',
'required' => true,
'array' => false
],
],
]);
$this->assertEquals($actors['headers']['status-code'], 201);
$this->assertEquals($actors['body']['$collection'], 0);
$this->assertEquals($actors['body']['name'], 'Actors');
$this->assertIsArray($actors['body']['$permissions']);
$this->assertIsArray($actors['body']['$permissions']['read']);
$this->assertIsArray($actors['body']['$permissions']['write']);
$this->assertCount(1, $actors['body']['$permissions']['read']);
$this->assertCount(2, $actors['body']['$permissions']['write']);
// Add Documents to the collection
$document1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $actors['body']['$id'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'firstName' => 'Tom',
'lastName' => 'Holland',
],
'read' => ['user:'.$this->getUser()['$id']],
'write' => ['user:'.$this->getUser()['$id']],
]);
$document2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $actors['body']['$id'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'data' => [
'firstName' => 'Samuel',
'lastName' => 'Jackson',
],
'read' => ['user:'.$this->getUser()['$id']],
'write' => ['user:'.$this->getUser()['$id']],
]);
$this->assertEquals($document1['headers']['status-code'], 201);
$this->assertEquals($document1['body']['$collection'], $actors['body']['$id']);
$this->assertIsArray($document1['body']['$permissions']);
$this->assertIsArray($document1['body']['$permissions']['read']);
$this->assertIsArray($document1['body']['$permissions']['write']);
$this->assertCount(1, $document1['body']['$permissions']['read']);
$this->assertCount(1, $document1['body']['$permissions']['write']);
$this->assertEquals($document1['body']['firstName'], 'Tom');
$this->assertEquals($document1['body']['lastName'], 'Holland');
$this->assertEquals($document2['headers']['status-code'], 201);
$this->assertEquals($document2['body']['$collection'], $actors['body']['$id']);
$this->assertIsArray($document2['body']['$permissions']);
$this->assertIsArray($document2['body']['$permissions']['read']);
$this->assertIsArray($document2['body']['$permissions']['write']);
$this->assertCount(1, $document2['body']['$permissions']['read']);
$this->assertCount(1, $document2['body']['$permissions']['write']);
$this->assertEquals($document2['body']['firstName'], 'Samuel');
$this->assertEquals($document2['body']['lastName'], 'Jackson');
// Delete the actors collection
$response = $this->client->call(Client::METHOD_DELETE, '/database/collections/'.$actors['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], $this->getHeaders()));
$this->assertEquals($response['headers']['status-code'], 204);
$this->assertEquals($response['body'],"");
// Try to get the collection and check if it has been deleted
$response = $this->client->call(Client::METHOD_GET, '/database/collections/'.$actors['body']['$id'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()));
$this->assertEquals($response['headers']['status-code'], 404);
}
}

Some files were not shown because too many files have changed in this diff Show more