Merge remote-tracking branch 'origin/feat-database-indexing' into feat-db-refactor-api-buxfixes

This commit is contained in:
kodumbeats
2021-10-07 14:40:45 -04:00
277 changed files with 2223 additions and 3602 deletions
+493 -23
View File
@@ -510,8 +510,8 @@ $collections = [
'signed' => true,
'required' => false,
'default' => null,
'array' => true,
'filters' => ['json'],
'array' => false,
'filters' => ['subQueryPlatforms'],
],
[
'$id' => 'webhooks',
@@ -521,8 +521,8 @@ $collections = [
'signed' => true,
'required' => false,
'default' => null,
'array' => true,
'filters' => ['json'],
'array' => false,
'filters' => ['subQueryWebhooks'],
],
[
'$id' => 'keys',
@@ -532,8 +532,8 @@ $collections = [
'signed' => true,
'required' => false,
'default' => null,
'array' => true,
'filters' => ['json'],
'array' => false,
'filters' => ['subQueryKeys'],
],
[
'$id' => 'domains',
@@ -543,16 +543,381 @@ $collections = [
'signed' => true,
'required' => false,
'default' => null,
'array' => true,
'filters' => ['json'],
'array' => false,
'filters' => ['subQueryDomains'],
],
[
'$id' => 'search',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => '_fulltext_name',
'$id' => '_key_search',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['name'],
'lengths' => [1024],
'attributes' => ['search'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
],
],
],
'platforms' => [
'$collection' => Database::METADATA,
'$id' => 'platforms',
'name' => 'platforms',
'attributes' => [
[
'$id' => 'projectId',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'type',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'name',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 256,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'key',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'store',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 256,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'hostname',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 256,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'dateCreated',
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'dateUpdated',
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => '_key_project',
'type' => Database::INDEX_KEY,
'attributes' => ['projectId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
],
],
'domains' => [
'$collection' => Database::METADATA,
'$id' => 'domains',
'name' => 'domains',
'attributes' => [
[
'$id' => 'projectId',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'updated',
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'domain',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'tld',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'registerable',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'verification',
'type' => Database::VAR_BOOLEAN,
'format' => '',
'size' => 0,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'certificateId',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => '_key_project',
'type' => Database::INDEX_KEY,
'attributes' => ['projectId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
],
],
'keys' => [
'$collection' => Database::METADATA,
'$id' => 'keys',
'name' => 'keys',
'attributes' => [
[
'$id' => 'projectId',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'name',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'scopes',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => true,
'filters' => [],
],
[
'$id' => 'secret',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 256, // var_dump of \bin2hex(\random_bytes(128)) => string(256)
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => '_key_project',
'type' => Database::INDEX_KEY,
'attributes' => ['projectId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
],
],
'webhooks' => [
'$collection' => Database::METADATA,
'$id' => 'webhooks',
'name' => 'webhooks',
'attributes' => [
[
'$id' => 'projectId',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'name',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'url',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'httpUser',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'httpPass',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'security',
'type' => Database::VAR_BOOLEAN,
'format' => '',
'size' => 0,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'events',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => true,
'filters' => [],
],
],
'indexes' => [
[
'$id' => '_key_project',
'type' => Database::INDEX_KEY,
'attributes' => ['projectId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
],
@@ -578,7 +943,7 @@ $collections = [
'$id' => 'email',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 1024,
'size' => 320,
'signed' => true,
'required' => false,
'default' => null,
@@ -695,15 +1060,51 @@ $collections = [
'array' => true,
'filters' => ['json'],
],
[
'$id' => 'search',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'deleted',
'type' => Database::VAR_BOOLEAN,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => '_key_email',
'type' => Database::INDEX_UNIQUE,
'attributes' => ['email'],
'lengths' => [1024],
'lengths' => [320],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_search',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_deleted_email',
'type' => Database::INDEX_KEY,
'attributes' => ['deleted', 'email'],
'lengths' => [0, 320],
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
],
],
],
@@ -993,13 +1394,24 @@ $collections = [
'array' => false,
'filters' => [],
],
[
'$id' => 'search',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => '_fulltext_name',
'$id' => '_key_search',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['name'],
'lengths' => [1024],
'attributes' => ['search'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
],
],
@@ -1273,6 +1685,17 @@ $collections = [
'array' => false,
'filters' => [],
],
[
'$id' => 'search',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
@@ -1283,10 +1706,10 @@ $collections = [
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_fulltext_name',
'$id' => '_key_search',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['name'],
'lengths' => [1024],
'attributes' => ['search'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
],
],
@@ -1441,13 +1864,24 @@ $collections = [
'array' => false,
'filters' => [],
],
[
'$id' => 'search',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => '_fulltext_name',
'$id' => '_key_search',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['name'],
'lengths' => [1024],
'attributes' => ['search'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
],
],
@@ -1514,6 +1948,17 @@ $collections = [
'array' => false,
'filters' => [],
],
[
'$id' => 'search',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
@@ -1523,6 +1968,13 @@ $collections = [
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_search',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
],
],
],
@@ -1631,6 +2083,17 @@ $collections = [
'array' => false,
'filters' => [],
],
[
'$id' => 'search',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
@@ -1640,6 +2103,13 @@ $collections = [
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_fulltext_search',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [16384],
'orders' => [Database::ORDER_ASC],
],
],
],
@@ -1815,4 +2285,4 @@ $collections = [
]
];
return $collections;
return $collections;
+4
View File
@@ -5,6 +5,7 @@ return [
'key' => 'homepage',
'name' => 'Homepage',
'subtitle' => '',
'description' => '',
'controller' => 'web/home.php',
'sdk' => false,
'docs' => false,
@@ -16,6 +17,8 @@ return [
'console' => [
'key' => 'console',
'name' => 'Console',
'subtitle' => '',
'description' => '',
'controller' => 'web/console.php',
'sdk' => false,
'docs' => false,
@@ -93,6 +96,7 @@ return [
'key' => 'projects',
'name' => 'Projects',
'subtitle' => 'The Project service allows you to manage all the projects in your Appwrite server.',
'description' => '',
'controller' => 'api/projects.php',
'sdk' => true,
'docs' => true,
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
+40 -19
View File
@@ -78,7 +78,9 @@ App::post('/v1/account')
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
if ($limit !== 0) {
$sum = $dbForInternal->count('users', [], APP_LIMIT_USERS);
$sum = $dbForInternal->count('users', [
new Query('deleted', Query::TYPE_EQUAL, [false]),
], APP_LIMIT_USERS);
if ($sum >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
@@ -105,6 +107,8 @@ App::post('/v1/account')
'sessions' => [],
'tokens' => [],
'memberships' => [],
'search' => implode(' ', [$userId, $email, $name]),
'deleted' => false
]));
} catch (Duplicate $th) {
throw new Exception('Account already exists', 409);
@@ -165,7 +169,7 @@ App::post('/v1/account/sessions')
$email = \strtolower($email);
$protocol = $request->getProtocol();
$profile = $dbForInternal->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
$profile = $dbForInternal->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
if (!$profile || !Auth::passwordVerify($password, $profile->getAttribute('password'))) {
$audits
@@ -202,8 +206,8 @@ App::post('/v1/account/sessions')
Authorization::setRole('user:' . $profile->getId());
$session = $dbForInternal->createDocument('sessions', $session
->setAttribute('$read', ['user:' . $profile->getId()])
->setAttribute('$write', ['user:' . $profile->getId()])
->setAttribute('$read', ['user:' . $profile->getId()])
->setAttribute('$write', ['user:' . $profile->getId()])
);
$profile->setAttribute('sessions', $session, Document::SET_TYPE_APPEND);
@@ -462,13 +466,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$name = $oauth2->getUserName($accessToken);
$email = $oauth2->getUserEmail($accessToken);
$user = $dbForInternal->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
$user = $dbForInternal->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
if ($user === false || $user->isEmpty()) { // Last option -> create the user, generate random password
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
if ($limit !== 0) {
$sum = $dbForInternal->count('users', [], APP_LIMIT_COUNT);
$sum = $dbForInternal->count('users', [ new Query('deleted', Query::TYPE_EQUAL, [false]),], APP_LIMIT_COUNT);
if ($sum >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
@@ -495,6 +499,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'sessions' => [],
'tokens' => [],
'memberships' => [],
'search' => implode(' ', [$userId, $email, $name]),
'deleted' => false
]));
} catch (Duplicate $th) {
throw new Exception('Account already exists', 409);
@@ -544,8 +550,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
Authorization::setRole('user:' . $user->getId());
$session = $dbForInternal->createDocument('sessions', $session
->setAttribute('$read', ['user:' . $user->getId()])
->setAttribute('$write', ['user:' . $user->getId()])
->setAttribute('$read', ['user:' . $user->getId()])
->setAttribute('$write', ['user:' . $user->getId()])
);
$user = $dbForInternal->updateDocument('users', $user->getId(), $user);
@@ -639,7 +645,9 @@ App::post('/v1/account/sessions/anonymous')
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
if ($limit !== 0) {
$sum = $dbForInternal->count('users', [], APP_LIMIT_COUNT);
$sum = $dbForInternal->count('users', [
new Query('deleted', Query::TYPE_EQUAL, [false]),
], APP_LIMIT_COUNT);
if ($sum >= $limit) {
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501);
@@ -665,6 +673,8 @@ App::post('/v1/account/sessions/anonymous')
'sessions' => [],
'tokens' => [],
'memberships' => [],
'search' => $userId,
'deleted' => false
]));
Authorization::reset();
@@ -1032,7 +1042,10 @@ App::patch('/v1/account/name')
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('name', $name));
$user = $dbForInternal->updateDocument('users', $user->getId(), $user
->setAttribute('name', $name)
->setAttribute('search', implode(' ', [$user->getId(), $name, $user->getAttribute('email')]))
);
$audits
->setParam('userId', $user->getId())
@@ -1131,11 +1144,18 @@ App::patch('/v1/account/email')
}
$email = \strtolower($email);
$profile = $dbForInternal->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
if ($profile) {
throw new Exception('User already registered', 409);
}
try {
$user = $dbForInternal->updateDocument('users', $user->getId(), $user
->setAttribute('password', $isAnonymousUser ? Auth::passwordHash($password) : $user->getAttribute('password', ''))
->setAttribute('email', $email)
->setAttribute('emailVerification', false) // After this user needs to confirm mail again
->setAttribute('search', implode(' ', [$user->getId(), $user->getAttribute('name'), $user->getAttribute('email')]))
);
} catch(Duplicate $th) {
throw new Exception('Email already exists', 409);
@@ -1221,6 +1241,8 @@ App::delete('/v1/account')
$protocol = $request->getProtocol();
$user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('status', false));
// TODO Seems to be related to users.php/App::delete('/v1/users/:userId'). Can we share code between these two? Do todos below apply to users.php?
// TODO delete all tokens or only current session?
// TODO delete all user data according to GDPR. Make sure everything is backed up and backups are deleted later
/*
@@ -1463,7 +1485,7 @@ App::post('/v1/account/recovery')
$isAppUser = Auth::isAppUser(Authorization::$roles);
$email = \strtolower($email);
$profile = $dbForInternal->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
$profile = $dbForInternal->findOne('users', [new Query('deleted', Query::TYPE_EQUAL, [false]), new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
if (!$profile) {
throw new Exception('User not found', 404);
@@ -1566,7 +1588,7 @@ App::put('/v1/account/recovery')
$profile = $dbForInternal->getDocument('users', $userId);
if ($profile->isEmpty()) {
if ($profile->isEmpty() || $profile->getAttribute('deleted')) {
throw new Exception('User not found', 404);
}
@@ -1586,10 +1608,9 @@ App::put('/v1/account/recovery')
);
/**
* We act like we're updating and validating
* the recovery token but actually we don't need it anymore.
*/
* We act like we're updating and validating
* the recovery token but actually we don't need it anymore.
*/
foreach ($tokens as $key => $token) {
if ($recovery === $token->getId()) {
$recovery = $token;
@@ -1757,9 +1778,9 @@ App::put('/v1/account/verification')
$profile = $dbForInternal->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true));
/**
* We act like we're updating and validating
* the verification token but actually we don't need it anymore.
*/
* We act like we're updating and validating
* the verification token but actually we don't need it anymore.
*/
foreach ($tokens as $key => $token) {
if ($token->getId() === $verification) {
$verification = $token;
+33 -30
View File
@@ -76,7 +76,8 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $
throw new Exception('Cannot set default value for required attribute', 400);
}
$attribute = new Document([
try {
$attribute = new Document([
'$id' => $collectionId.'_'.$attributeId,
'key' => $attributeId,
'collectionId' => $collectionId,
@@ -89,9 +90,8 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $
'array' => $array,
'format' => $format,
'formatOptions' => $formatOptions,
]);
]);
try {
$dbForInternal->checkAttribute($collection, $attribute);
$attribute = $dbForInternal->createDocument('attributes', $attribute);
}
@@ -102,7 +102,7 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $
throw new Exception('Attribute limit exceeded', 400);
}
$dbForInternal->purgeDocument('collections', $collectionId);
$dbForInternal->deleteCachedDocument('collections', $collectionId);
// Pass clone of $attribute object to workers
// so we can later modify Document to fit response model
@@ -200,33 +200,34 @@ App::get('/v1/database/collections')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 40000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', new UID(), 'ID of the collection used as the starting point for the query, excluding the collection itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursor', '', new UID(), 'ID of the collection used as the starting point for the query, excluding the collection itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->inject('usage')
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal, $usage) {
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
if (!empty($cursor)) {
$cursorCollection = $dbForInternal->getDocument('collections', $cursor);
if ($cursorCollection->isEmpty()) {
throw new Exception("Collection '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$queries = [];
if (!empty($search)) {
$queries[] = new Query('name', Query::TYPE_SEARCH, [$search]);
}
if (!empty($after)) {
$afterCollection = $dbForInternal->getDocument('collections', $after);
if ($afterCollection->isEmpty()) {
throw new Exception("Collection '{$after}' for the 'after' value not found.", 400);
}
}
$usage->setParam('database.collections.read', 1);
$response->dynamic(new Document([
'collections' => $dbForInternal->find('collections', $queries, $limit, $offset, [], [$orderType], $afterCollection ?? null),
'collections' => $dbForInternal->find('collections', $queries, $limit, $offset, [], [$orderType], $cursorCollection ?? null, $cursorDirection),
'sum' => $dbForInternal->count('collections', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_COLLECTION_LIST);
});
@@ -1142,6 +1143,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
}
$dbForInternal->purgeDocument('collections', $collectionId);
$dbForInternal->deleteCachedDocument('collections', $collectionId);
$database
->setParam('type', DATABASE_TYPE_DELETE_ATTRIBUTE)
@@ -1253,7 +1255,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
throw new Exception('Index already exists', 409);
}
$dbForInternal->purgeDocument('collections', $collectionId);
$dbForInternal->deleteCachedDocument('collections', $collectionId);
$database
->setParam('type', DATABASE_TYPE_CREATE_INDEX)
@@ -1402,7 +1404,7 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId')
$index = $dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'deleting'));
}
$dbForInternal->purgeDocument('collections', $collectionId);
$dbForInternal->deleteCachedDocument('collections', $collectionId);
$database
->setParam('type', DATABASE_TYPE_DELETE_INDEX)
@@ -1455,7 +1457,7 @@ App::post('/v1/database/collections/:collectionId/documents')
/** @var Utopia\Database\Document $user */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $usage */
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
if (empty($data)) {
@@ -1465,7 +1467,7 @@ App::post('/v1/database/collections/:collectionId/documents')
if (isset($data['$id'])) {
throw new Exception('$id is not allowed for creating new documents, try update instead', 400);
}
$collection = $dbForInternal->getDocument('collections', $collectionId);
if ($collection->isEmpty()) {
@@ -1532,14 +1534,15 @@ App::get('/v1/database/collections/:collectionId/documents')
->param('queries', [], new ArrayList(new Text(128)), 'Array of query strings.', true)
->param('limit', 25, new Range(0, 100), 'Maximum number of documents to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 900000000), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', new UID(), 'ID of the document used as the starting point for the query, excluding the document itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursor', '', new UID(), 'ID of the document used as the starting point for the query, excluding the document itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderAttributes', [], new ArrayList(new Text(128)), 'Array of attributes used to sort results.', true)
->param('orderTypes', [], new ArrayList(new WhiteList(['DESC', 'ASC'], true)), 'Array of order directions for sorting attribtues. Possible values are DESC for descending order, or ASC for ascending order.', true)
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')
->inject('usage')
->action(function ($collectionId, $queries, $limit, $offset, $after, $orderAttributes, $orderTypes, $response, $dbForInternal, $dbForExternal, $usage) {
->action(function ($collectionId, $queries, $limit, $offset, $cursor, $cursorDirection, $orderAttributes, $orderTypes, $response, $dbForInternal, $dbForExternal, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Utopia\Database\Database $dbForExternal */
@@ -1570,28 +1573,28 @@ App::get('/v1/database/collections/:collectionId/documents')
throw new Exception($validator->getDescription(), 400);
}
if (!empty($after)) {
$afterDocument = $dbForExternal->getDocument($collectionId, $after);
if (!empty($cursor)) {
$cursorDocument = $dbForExternal->getDocument($collectionId, $cursor);
if ($afterDocument->isEmpty()) {
throw new Exception("Document '{$after}' for the 'after' value not found.", 400);
if ($cursorDocument->isEmpty()) {
throw new Exception("Document '{$cursor}' for the 'cursor' value not found.", 400);
}
}
if ($collection->getAttribute('permission') === 'collection') {
/** @var Document[] $documents */
$documents = Authorization::skip(function() use ($dbForExternal, $collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument) {
return $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument ?? null);
$documents = Authorization::skip(function() use ($dbForExternal, $collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument, $cursorDirection) {
return $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection);
});
} else {
$documents = $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument ?? null);
$documents = $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection);
}
$usage
->setParam('database.documents.read', 1)
->setParam('collectionId', $collectionId)
;
;
$response->dynamic(new Document([
'sum' => $dbForExternal->count($collectionId, $queries, APP_LIMIT_COUNT),
'documents' => $documents,
+61 -36
View File
@@ -53,8 +53,9 @@ App::post('/v1/functions')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
$functionId = ($functionId == 'unique()') ? $dbForInternal->getId() : $functionId;
$function = $dbForInternal->createDocument('functions', new Document([
'$id' => $functionId == 'unique()' ? $dbForInternal->getId() : $functionId,
'$id' => $functionId,
'execute' => $execute,
'dateCreated' => time(),
'dateUpdated' => time(),
@@ -68,6 +69,7 @@ App::post('/v1/functions')
'schedulePrevious' => 0,
'scheduleNext' => 0,
'timeout' => $timeout,
'search' => implode(' ', [$functionId, $name, $runtime]),
]));
$response->setStatusCode(Response::STATUS_CODE_CREATED);
@@ -88,26 +90,31 @@ App::get('/v1/functions')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', new UID(), 'ID of the function used as the starting point for the query, excluding the function itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursor', '', new UID(), 'ID of the function used as the starting point for the query, excluding the function itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal) {
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
$queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : [];
if (!empty($cursor)) {
$cursorFunction = $dbForInternal->getDocument('functions', $cursor);
if (!empty($after)) {
$afterFunction = $dbForInternal->getDocument('functions', $after);
if ($afterFunction->isEmpty()) {
throw new Exception("Function '{$after}' for the 'after' value not found.", 400);
if ($cursorFunction->isEmpty()) {
throw new Exception("Function '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$queries = [];
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
}
$response->dynamic(new Document([
'functions' => $dbForInternal->find('functions', $queries, $limit, $offset, [], [$orderType], $afterFunction ?? null),
'functions' => $dbForInternal->find('functions', $queries, $limit, $offset, [], [$orderType], $cursorFunction ?? null, $cursorDirection),
'sum' => $dbForInternal->count('functions', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_FUNCTION_LIST);
});
@@ -271,6 +278,7 @@ App::put('/v1/functions/:functionId')
'schedule' => $schedule,
'scheduleNext' => (int)$next,
'timeout' => $timeout,
'search' => implode(' ', [$functionId, $name, $function->getAttribute('runtime')]),
])));
if ($next && $schedule !== $original) {
@@ -447,8 +455,9 @@ App::post('/v1/functions/:functionId/tags')
throw new Exception('Failed moving file', 500);
}
$tagId = $dbForInternal->getId();
$tag = $dbForInternal->createDocument('tags', new Document([
'$id' => $dbForInternal->getId(),
'$id' => $tagId,
'$read' => [],
'$write' => [],
'functionId' => $function->getId(),
@@ -456,6 +465,7 @@ App::post('/v1/functions/:functionId/tags')
'command' => $command,
'path' => $path,
'size' => $size,
'search' => implode(' ', [$tagId, $command]),
]));
$usage
@@ -481,11 +491,12 @@ App::get('/v1/functions/:functionId/tags')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', new UID(), 'ID of the tag used as the starting point for the query, excluding the tag itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursor', '', new UID(), 'ID of the tag used as the starting point for the query, excluding the tag itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->action(function ($functionId, $search, $limit, $offset, $after, $orderType, $response, $dbForInternal) {
->action(function ($functionId, $search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
@@ -495,17 +506,23 @@ App::get('/v1/functions/:functionId/tags')
throw new Exception('Function not found', 404);
}
$queries[] = new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]);
if (!empty($cursor)) {
$cursorTag = $dbForInternal->getDocument('tags', $cursor);
if (!empty($after)) {
$afterTag = $dbForInternal->getDocument('tags', $after);
if ($afterTag->isEmpty()) {
throw new Exception("Tag '{$after}' for the 'after' value not found.", 400);
if ($cursorTag->isEmpty()) {
throw new Exception("Tag '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$results = $dbForInternal->find('tags', $queries, $limit, $offset, [], [$orderType], $afterTag ?? null);
$queries = [];
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
}
$queries[] = new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]);
$results = $dbForInternal->find('tags', $queries, $limit, $offset, [], [$orderType], $cursorTag ?? null, $cursorDirection);
$sum = $dbForInternal->count('tags', $queries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
@@ -665,8 +682,10 @@ App::post('/v1/functions/:functionId/executions')
Authorization::disable();
$executionId = $dbForInternal->getId();
$execution = $dbForInternal->createDocument('executions', new Document([
'$id' => $dbForInternal->getId(),
'$id' => $executionId,
'$read' => (!$user->isEmpty()) ? ['user:' . $user->getId()] : [],
'$write' => [],
'dateCreated' => time(),
@@ -678,6 +697,7 @@ App::post('/v1/functions/:functionId/executions')
'stdout' => '',
'stderr' => '',
'time' => 0.0,
'search' => implode(' ', [$functionId, $executionId]),
]));
Authorization::reset();
@@ -732,36 +752,41 @@ App::get('/v1/functions/:functionId/executions')
->param('functionId', '', new UID(), 'Function unique ID.')
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', new UID(), 'ID of the execution used as the starting point for the query, excluding the execution itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('cursor', '', new UID(), 'ID of the execution used as the starting point for the query, excluding the execution itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->inject('response')
->inject('dbForInternal')
->action(function ($functionId, $limit, $offset, $after, $response, $dbForInternal) {
->action(function ($functionId, $limit, $offset, $search, $cursor, $cursorDirection, $response, $dbForInternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
Authorization::disable();
$function = $dbForInternal->getDocument('functions', $functionId);
Authorization::reset();
$function = Authorization::skip(function() use ($dbForInternal, $functionId) {
return $dbForInternal->getDocument('functions', $functionId);
});
if ($function->isEmpty()) {
throw new Exception('Function not found', 404);
}
if (!empty($after)) {
$afterExecution = $dbForInternal->getDocument('executions', $after);
if (!empty($cursor)) {
$cursorExecution = $dbForInternal->getDocument('executions', $cursor);
if ($afterExecution->isEmpty()) {
throw new Exception("Execution '{$after}' for the 'after' value not found.", 400);
if ($cursorExecution->isEmpty()) {
throw new Exception("Execution '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$results = $dbForInternal->find('executions', [
new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]),
], $limit, $offset, [], [Database::ORDER_DESC], $afterExecution ?? null);
$queries = [
new Query('functionId', Query::TYPE_EQUAL, [$function->getId()])
];
$sum = $dbForInternal->count('executions', [
new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]),
], APP_LIMIT_COUNT);
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
}
$results = $dbForInternal->find('executions', $queries, $limit, $offset, [], [Database::ORDER_DESC], $cursorExecution ?? null, $cursorDirection);
$sum = $dbForInternal->count('executions', $queries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
'executions' => $results,
+167 -84
View File
@@ -8,6 +8,7 @@ use Appwrite\Network\Validator\URL;
use Appwrite\Utopia\Response;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\App;
use Utopia\CLI\CLI;
use Utopia\Audit\Audit;
use Utopia\Config\Config;
use Utopia\Database\Database;
@@ -78,6 +79,7 @@ App::post('/v1/projects')
$auths[$method['key'] ?? ''] = true;
}
$projectId = ($projectId == 'unique()') ? $dbForConsole->getId() : $projectId;
$project = $dbForConsole->createDocument('projects', new Document([
'$id' => $projectId == 'unique()' ? $dbForConsole->getId() : $projectId,
'$read' => ['team:' . $teamId],
@@ -95,11 +97,13 @@ App::post('/v1/projects')
'legalAddress' => $legalAddress,
'legalTaxId' => $legalTaxId,
'services' => new stdClass(),
'platforms' => [],
'webhooks' => [],
'keys' => [],
'domains' => [],
'platforms' => null,
'providers' => [],
'webhooks' => null,
'keys' => null,
'domains' => null,
'auths' => $auths,
'search' => implode(' ', [$projectId, $name]),
]));
$collections = Config::getParam('collections2', []); /** @var array $collections */
@@ -112,7 +116,7 @@ App::post('/v1/projects')
$audit = new Audit($dbForInternal);
$audit->setup();
$adapter = new TimeLimit("", 0, 1, $dbForInternal);
$adapter = new TimeLimit('', 0, 1, $dbForInternal);
$adapter->setup();
foreach ($collections as $key => $collection) {
@@ -163,25 +167,30 @@ App::get('/v1/projects')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', new UID(), 'ID of the project used as the starting point for the query, excluding the project itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursor', '', new UID(), 'ID of the project used as the starting point for the query, excluding the project itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForConsole')
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForConsole) {
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
$queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : [];
if (!empty($cursor)) {
$cursorProject = $dbForConsole->getDocument('projects', $cursor);
if (!empty($after)) {
$afterProject = $dbForConsole->getDocument('projects', $after);
if ($afterProject->isEmpty()) {
throw new Exception("Project '{$after}' for the 'after' value not found.", 400);
if ($cursorProject->isEmpty()) {
throw new Exception("Project '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$results = $dbForConsole->find('projects', $queries, $limit, $offset, [], [$orderType], $afterProject ?? null);
$queries = [];
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
}
$results = $dbForConsole->find('projects', $queries, $limit, $offset, [], [$orderType], $cursorProject ?? null, $cursorDirection);
$sum = $dbForConsole->count('projects', $queries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
@@ -356,6 +365,7 @@ App::patch('/v1/projects/:projectId')
->setAttribute('legalCity', $legalCity)
->setAttribute('legalAddress', $legalAddress)
->setAttribute('legalTaxId', $legalTaxId)
->setAttribute('search', implode(' ', [$projectId, $name]))
);
$response->dynamic($project, Response::MODEL_PROJECT);
@@ -379,6 +389,7 @@ App::patch('/v1/projects/:projectId/service')
->action(function ($projectId, $service, $status, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
/** @var Boolean $status */
$project = $dbForConsole->getDocument('projects', $projectId);
@@ -457,7 +468,7 @@ App::patch('/v1/projects/:projectId/auth/limit')
$auths['limit'] = $limit;
$dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('auths', $auths)
->setAttribute('auths', $auths)
);
$response->dynamic($project, Response::MODEL_PROJECT);
@@ -581,6 +592,9 @@ App::post('/v1/projects/:projectId/webhooks')
$webhook = new Document([
'$id' => $dbForConsole->getId(),
'$read' => ['role:all'],
'$write' => ['role:all'],
'projectId' => $project->getId(),
'name' => $name,
'events' => $events,
'url' => $url,
@@ -589,9 +603,9 @@ App::post('/v1/projects/:projectId/webhooks')
'httpPass' => $httpPass,
]);
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('webhooks', $webhook, Document::SET_TYPE_APPEND)
);
$webhook = $dbForConsole->createDocument('webhooks', $webhook);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
@@ -620,7 +634,9 @@ App::get('/v1/projects/:projectId/webhooks')
throw new Exception('Project not found', 404);
}
$webhooks = $project->getAttribute('webhooks', []);
$webhooks = $dbForConsole->find('webhooks', [
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
]);
$response->dynamic(new Document([
'webhooks' => $webhooks,
@@ -652,9 +668,12 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
throw new Exception('Project not found', 404);
}
$webhook = $project->find('$id', $webhookId, 'webhooks');
$webhook = $dbForConsole->findOne('webhooks', [
new Query('_uid', Query::TYPE_EQUAL, [$webhookId]),
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
]);
if (empty($webhook) || !$webhook instanceof Document) {
if ($webhook === false || $webhook->isEmpty()) {
throw new Exception('Webhook not found', 404);
}
@@ -693,22 +712,27 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
$security = ($security === '1' || $security === 'true' || $security === 1 || $security === true);
$webhook = $project->find('$id', $webhookId, 'webhooks');
$webhook = $dbForConsole->findOne('webhooks', [
new Query('_uid', Query::TYPE_EQUAL, [$webhookId]),
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
]);
if (empty($webhook) || !$webhook instanceof Document) {
if ($webhook === false || $webhook->isEmpty()) {
throw new Exception('Webhook not found', 404);
}
$project->findAndReplace('$id', $webhook->getId(), $webhook
->setAttribute('name', $name)
->setAttribute('events', $events)
->setAttribute('url', $url)
->setAttribute('security', $security)
->setAttribute('httpUser', $httpUser)
->setAttribute('httpPass', $httpPass)
, 'webhooks');
$webhook
->setAttribute('name', $name)
->setAttribute('events', $events)
->setAttribute('url', $url)
->setAttribute('security', $security)
->setAttribute('httpUser', $httpUser)
->setAttribute('httpPass', $httpPass)
;
$dbForConsole->updateDocument('projects', $project->getId(), $project);
$dbForConsole->updateDocument('webhooks', $webhook->getId(), $webhook);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
});
@@ -736,11 +760,18 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
throw new Exception('Project not found', 404);
}
if (!$project->findAndRemove('$id', $webhookId, 'webhooks')) {
$webhook = $dbForConsole->findOne('webhooks', [
new Query('_uid', Query::TYPE_EQUAL, [$webhookId]),
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
]);
if($webhook === false || $webhook->isEmpty()) {
throw new Exception('Webhook not found', 404);
}
$dbForConsole->updateDocument('projects', $project->getId(), $project);
$dbForConsole->deleteDocument('webhooks', $webhook->getId());
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$response->noContent();
});
@@ -774,14 +805,17 @@ App::post('/v1/projects/:projectId/keys')
$key = new Document([
'$id' => $dbForConsole->getId(),
'$read' => ['role:all'],
'$write' => ['role:all'],
'projectId' => $project->getId(),
'name' => $name,
'scopes' => $scopes,
'secret' => \bin2hex(\random_bytes(128)),
]);
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('keys', $key, Document::SET_TYPE_APPEND)
);
$key = $dbForConsole->createDocument('keys', $key);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($key, Response::MODEL_KEY);
@@ -810,7 +844,9 @@ App::get('/v1/projects/:projectId/keys')
throw new Exception('Project not found', 404);
}
$keys = $project->getAttribute('keys', []);
$keys = $dbForConsole->find('keys', [
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]),
], 5000);
$response->dynamic(new Document([
'keys' => $keys,
@@ -833,15 +869,21 @@ App::get('/v1/projects/:projectId/keys/:keyId')
->inject('response')
->inject('dbForConsole')
->action(function ($projectId, $keyId, $response, $dbForConsole) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForConsole */
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception('Project not found', 404);
}
$key = $project->find('$id', $keyId, 'keys');
$key = $dbForConsole->findOne('keys', [
new Query('_uid', Query::TYPE_EQUAL, [$keyId]),
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
]);
if (empty($key) || !$key instanceof Document) {
if ($key === false || $key->isEmpty()) {
throw new Exception('Key not found', 404);
}
@@ -874,18 +916,23 @@ App::put('/v1/projects/:projectId/keys/:keyId')
throw new Exception('Project not found', 404);
}
$key = $project->find('$id', $keyId, 'keys');
$key = $dbForConsole->findOne('keys', [
new Query('_uid', Query::TYPE_EQUAL, [$keyId]),
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
]);
if (empty($key) || !$key instanceof Document) {
if ($key === false || $key->isEmpty()) {
throw new Exception('Key not found', 404);
}
$project->findAndReplace('$id', $key->getId(), $key
->setAttribute('name', $name)
->setAttribute('scopes', $scopes)
, 'keys');
$key
->setAttribute('name', $name)
->setAttribute('scopes', $scopes)
;
$dbForConsole->updateDocument('projects', $project->getId(), $project);
$dbForConsole->updateDocument('keys', $key->getId(), $key);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$response->dynamic($key, Response::MODEL_KEY);
});
@@ -913,11 +960,18 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
throw new Exception('Project not found', 404);
}
if (!$project->findAndRemove('$id', $keyId, 'keys')) {
$key = $dbForConsole->findOne('keys', [
new Query('_uid', Query::TYPE_EQUAL, [$keyId]),
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
]);
if($key === false || $key->isEmpty()) {
throw new Exception('Key not found', 404);
}
$dbForConsole->updateDocument('projects', $project->getId(), $project);
$dbForConsole->deleteDocument('keys', $key->getId());
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$response->noContent();
});
@@ -954,6 +1008,9 @@ App::post('/v1/projects/:projectId/platforms')
$platform = new Document([
'$id' => $dbForConsole->getId(),
'$read' => ['role:all'],
'$write' => ['role:all'],
'projectId' => $project->getId(),
'type' => $type,
'name' => $name,
'key' => $key,
@@ -963,9 +1020,9 @@ App::post('/v1/projects/:projectId/platforms')
'dateUpdated' => \time(),
]);
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('platforms', $platform, Document::SET_TYPE_APPEND)
);
$platform = $dbForConsole->createDocument('platforms', $platform);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($platform, Response::MODEL_PLATFORM);
@@ -994,7 +1051,9 @@ App::get('/v1/projects/:projectId/platforms')
throw new Exception('Project not found', 404);
}
$platforms = $project->getAttribute('platforms', []);
$platforms = $dbForConsole->find('platforms', [
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
], 5000);
$response->dynamic(new Document([
'platforms' => $platforms,
@@ -1026,9 +1085,12 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
throw new Exception('Project not found', 404);
}
$platform = $project->find('$id', $platformId, 'platforms');
$platform = $dbForConsole->findOne('platforms', [
new Query('_uid', Query::TYPE_EQUAL, [$platformId]),
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
]);
if (empty($platform) || !$platform instanceof Document) {
if ($platform === false || $platform->isEmpty()) {
throw new Exception('Platform not found', 404);
}
@@ -1063,9 +1125,12 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
throw new Exception('Project not found', 404);
}
$platform = $project->find('$id', $platformId, 'platforms');
$platform = $dbForConsole->findOne('platforms', [
new Query('_uid', Query::TYPE_EQUAL, [$platformId]),
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
]);
if (empty($platform) || !$platform instanceof Document) {
if ($platform === false || $platform->isEmpty()) {
throw new Exception('Platform not found', 404);
}
@@ -1077,15 +1142,9 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
->setAttribute('hostname', $hostname)
;
$project->findAndReplace('$id', $platform->getId(), $platform
->setAttribute('name', $name)
->setAttribute('dateUpdated', \time())
->setAttribute('key', $key)
->setAttribute('store', $store)
->setAttribute('hostname', $hostname)
, 'platforms');
$dbForConsole->updateDocument('platforms', $platform->getId(), $platform);
$dbForConsole->updateDocument('projects', $project->getId(), $project);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$response->dynamic($platform, Response::MODEL_PLATFORM);
});
@@ -1113,11 +1172,18 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
throw new Exception('Project not found', 404);
}
if (!$project->findAndRemove('$id', $platformId, 'platforms')) {
$platform = $dbForConsole->findOne('platforms', [
new Query('_uid', Query::TYPE_EQUAL, [$platformId]),
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
]);
if ($platform === false || $platform->isEmpty()) {
throw new Exception('Platform not found', 404);
}
$dbForConsole->updateDocument('projects', $project->getId(), $project);
$dbForConsole->deleteDocument('platforms', $platformId);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$response->noContent();
});
@@ -1148,9 +1214,12 @@ App::post('/v1/projects/:projectId/domains')
throw new Exception('Project not found', 404);
}
$document = $project->find('domain', $domain, 'domains');
$document = $dbForConsole->findOne('domains', [
new Query('domain', Query::TYPE_EQUAL, [$domain]),
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()]),
]);
if ($document) {
if ($document && !$document->isEmpty()) {
throw new Exception('Domain already exists', 409);
}
@@ -1164,6 +1233,9 @@ App::post('/v1/projects/:projectId/domains')
$domain = new Document([
'$id' => $dbForConsole->getId(),
'$read' => ['role:all'],
'$write' => ['role:all'],
'projectId' => $project->getId(),
'updated' => \time(),
'domain' => $domain->get(),
'tld' => $domain->getSuffix(),
@@ -1172,9 +1244,9 @@ App::post('/v1/projects/:projectId/domains')
'certificateId' => null,
]);
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('domains', $domain, Document::SET_TYPE_APPEND)
);
$domain = $dbForConsole->createDocument('domains', $domain);
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($domain, Response::MODEL_DOMAIN);
@@ -1203,7 +1275,9 @@ App::get('/v1/projects/:projectId/domains')
throw new Exception('Project not found', 404);
}
$domains = $project->getAttribute('domains', []);
$domains = $dbForConsole->find('domains', [
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
], 5000);
$response->dynamic(new Document([
'domains' => $domains,
@@ -1235,9 +1309,12 @@ App::get('/v1/projects/:projectId/domains/:domainId')
throw new Exception('Project not found', 404);
}
$domain = $project->find('$id', $domainId, 'domains');
$domain = $dbForConsole->findOne('domains', [
new Query('_uid', Query::TYPE_EQUAL, [$domainId]),
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
]);
if (empty($domain) || !$domain instanceof Document) {
if ($domain === false || $domain->isEmpty()) {
throw new Exception('Domain not found', 404);
}
@@ -1268,9 +1345,12 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
throw new Exception('Project not found', 404);
}
$domain = $project->find('$id', $domainId, 'domains');
$domain = $dbForConsole->findOne('domains', [
new Query('_uid', Query::TYPE_EQUAL, [$domainId]),
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
]);
if (empty($domain) || !$domain instanceof Document) {
if ($domain === false || $domain->isEmpty()) {
throw new Exception('Domain not found', 404);
}
@@ -1290,11 +1370,9 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
throw new Exception('Failed to verify domain', 401);
}
$project->findAndReplace('$id', $domain->getId(), $domain
->setAttribute('verification', true)
, 'domains');
$dbForConsole->updateDocument('projects', $project->getId(), $project);
$dbForConsole->updateDocument('domains', $domain->getId(), $domain->setAttribute('verification', true));
$dbForConsole->deleteCachedDocument('projects', $project->getId());
// Issue a TLS certificate when domain is verified
Resque::enqueue('v1-certificates', 'CertificatesV1', [
@@ -1329,13 +1407,18 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
throw new Exception('Project not found', 404);
}
$domain = $project->find('$id', $domainId, 'domains');
$domain = $dbForConsole->findOne('domains', [
new Query('_uid', Query::TYPE_EQUAL, [$domainId]),
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
]);
if (!$project->findAndRemove('$id', $domainId, 'domains')) {
if ($domain === false || $domain->isEmpty()) {
throw new Exception('Domain not found', 404);
}
$dbForConsole->updateDocument('projects', $project->getId(), $project);
$dbForConsole->deleteDocument('domains', $domain->getId());
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$deletes
->setParam('type', DELETE_TYPE_CERTIFICATES)
+18 -11
View File
@@ -125,13 +125,14 @@ App::post('/v1/storage/files')
$sizeActual = $device->getFileSize($path);
$fileId = ($fileId == 'unique()') ? $dbForInternal->getId() : $fileId;
$file = $dbForInternal->createDocument('files', new Document([
'$id' => $fileId == 'unique()' ? $dbForInternal->getId() : $fileId,
'$id' => $fileId,
'$read' => (is_null($read) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $read ?? [], // By default set read permissions for user
'$write' => (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? [], // By default set write permissions for user
'dateCreated' => \time(),
'bucketId' => '',
'name' => $file['name'],
'name' => $file['name'] ?? '',
'path' => $path,
'signature' => $device->getFileHash($path),
'mimeType' => $mimeType,
@@ -143,6 +144,7 @@ App::post('/v1/storage/files')
'openSSLCipher' => OpenSSL::CIPHER_AES_128_GCM,
'openSSLTag' => \bin2hex($tag),
'openSSLIV' => \bin2hex($iv),
'search' => implode(' ', [$fileId, $file['name'] ?? '',]),
]));
$audits
@@ -175,33 +177,38 @@ App::get('/v1/storage/files')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', new UID(), 'ID of the file used as the starting point for the query, excluding the file itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursor', '', new UID(), 'ID of the file used as the starting point for the query, excluding the file itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->inject('usage')
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal, $usage) {
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Appwrite\Stats\Stats $usage */
$queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, $search)] : [];
if (!empty($cursor)) {
$cursorFile = $dbForInternal->getDocument('files', $cursor);
if (!empty($after)) {
$afterFile = $dbForInternal->getDocument('files', $after);
if ($afterFile->isEmpty()) {
throw new Exception("File '{$after}' for the 'after' value not found.", 400);
if ($cursorFile->isEmpty()) {
throw new Exception("File '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$queries = [];
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
}
$usage
->setParam('storage.files.read', 1)
->setParam('bucketId', 'default')
;
$response->dynamic(new Document([
'files' => $dbForInternal->find('files', $queries, $limit, $offset, [], [$orderType], $afterFile ?? null),
'files' => $dbForInternal->find('files', $queries, $limit, $offset, [], [$orderType], $cursorFile ?? null, $cursorDirection),
'sum' => $dbForInternal->count('files', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_FILE_LIST);
});
+29 -17
View File
@@ -14,6 +14,7 @@ use Utopia\Validator\Text;
use Utopia\Validator\Range;
use Utopia\Validator\ArrayList;
use Utopia\Validator\WhiteList;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Query;
@@ -57,6 +58,7 @@ App::post('/v1/teams')
'name' => $name,
'sum' => ($isPrivilegedUser || $isAppUser) ? 0 : 1,
'dateCreated' => \time(),
'search' => implode(' ', [$teamId, $name]),
]));
Authorization::reset();
@@ -99,25 +101,30 @@ App::get('/v1/teams')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', new UID(), 'ID of the team used as the starting point for the query, excluding the team itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursor', '', new UID(), 'ID of the team used as the starting point for the query, excluding the team itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal) {
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
$queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : [];
if (!empty($cursor)) {
$cursorTeam = $dbForInternal->getDocument('teams', $cursor);
if (!empty($after)) {
$afterTeam = $dbForInternal->getDocument('teams', $after);
if ($afterTeam->isEmpty()) {
throw new Exception("Team '{$after}' for the 'after' value not found.", 400);
if ($cursorTeam->isEmpty()) {
throw new Exception("Team '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$results = $dbForInternal->find('teams', $queries, $limit, $offset, [], [$orderType], $afterTeam ?? null);
$queries = [];
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
}
$results = $dbForInternal->find('teams', $queries, $limit, $offset, [], [$orderType], $cursorTeam ?? null, $cursorDirection);
$sum = $dbForInternal->count('teams', $queries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
@@ -179,7 +186,10 @@ App::put('/v1/teams/:teamId')
throw new Exception('Team not found', 404);
}
$team = $dbForInternal->updateDocument('teams', $team->getId(), $team->setAttribute('name', $name));
$team = $dbForInternal->updateDocument('teams', $team->getId(),$team
->setAttribute('name', $name)
->setAttribute('search', implode(' ', [$teamId, $name]))
);
$response->dynamic($team, Response::MODEL_TEAM);
});
@@ -323,6 +333,7 @@ App::post('/v1/teams/:teamId/memberships')
'sessions' => [],
'tokens' => [],
'memberships' => [],
'search' => implode(' ', [$userId, $email, $name]),
]));
} catch (Duplicate $th) {
throw new Exception('Account already exists', 409);
@@ -424,11 +435,12 @@ App::get('/v1/teams/:teamId/memberships')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', new UID(), 'ID of the membership used as the starting point for the query, excluding the membership itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursor', '', new UID(), 'ID of the membership used as the starting point for the query, excluding the membership itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->action(function ($teamId, $search, $limit, $offset, $after, $orderType, $response, $dbForInternal) {
->action(function ($teamId, $search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
@@ -438,15 +450,15 @@ App::get('/v1/teams/:teamId/memberships')
throw new Exception('Team not found', 404);
}
if (!empty($after)) {
$afterMembership = $dbForInternal->getDocument('memberships', $after);
if (!empty($cursor)) {
$cursorMembership = $dbForInternal->getDocument('memberships', $cursor);
if ($afterMembership->isEmpty()) {
throw new Exception("Membership '{$after}' for the 'after' value not found.", 400);
if ($cursorMembership->isEmpty()) {
throw new Exception("Membership '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$memberships = $dbForInternal->find('memberships', [new Query('teamId', Query::TYPE_EQUAL, [$teamId])], $limit, $offset, [], [$orderType], $afterMembership ?? null);
$memberships = $dbForInternal->find('memberships', [new Query('teamId', Query::TYPE_EQUAL, [$teamId])], $limit, $offset, [], [$orderType], $cursorMembership ?? null, $cursorDirection);
$sum = $dbForInternal->count('memberships', [new Query('teamId', Query::TYPE_EQUAL, [$teamId])], APP_LIMIT_COUNT);
$users = [];
+46 -29
View File
@@ -66,6 +66,8 @@ App::post('/v1/users')
'sessions' => [],
'tokens' => [],
'memberships' => [],
'search' => implode(' ', [$userId, $email, $name]),
'deleted' => false
]));
} catch (Duplicate $th) {
throw new Exception('Account already exists', 409);
@@ -93,34 +95,40 @@ App::get('/v1/users')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('after', '', new UID(), 'ID of the user used as the starting point for the query, excluding the user itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursor', '', new UID(), 'ID of the user used as the starting point for the query, excluding the user itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForInternal')
->inject('usage')
->action(function ($search, $limit, $offset, $after, $orderType, $response, $dbForInternal, $usage) {
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForInternal, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Appwrite\Stats\Stats $usage */
if (!empty($after)) {
$afterUser = $dbForInternal->getDocument('users', $after);
if (!empty($cursor)) {
$cursorUser = $dbForInternal->getDocument('users', $cursor);
if ($afterUser->isEmpty()) {
throw new Exception('User for after not found', 400);
if ($cursorUser->isEmpty()) {
throw new Exception("User '{$cursor}' for the 'after' value not found.", 400);
}
}
$results = $dbForInternal->find('users', [], $limit, $offset, [], [$orderType], $afterUser ?? null);
$sum = $dbForInternal->count('users', [], APP_LIMIT_COUNT);
$queries = [
new Query('deleted', Query::TYPE_EQUAL, [false])
];
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
}
$usage
->setParam('users.read', 1)
;
$response->dynamic(new Document([
'users' => $results,
'sum' => $sum,
'users' => $dbForInternal->find('users', $queries, $limit, $offset, [], [$orderType], $cursorUser ?? null, $cursorDirection),
'sum' => $dbForInternal->count('users', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_USER_LIST);
});
@@ -146,7 +154,7 @@ App::get('/v1/users/:userId')
$user = $dbForInternal->getDocument('users', $userId);
if ($user->isEmpty()) {
if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404);
}
@@ -178,7 +186,7 @@ App::get('/v1/users/:userId/prefs')
$user = $dbForInternal->getDocument('users', $userId);
if ($user->isEmpty()) {
if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404);
}
@@ -214,7 +222,7 @@ App::get('/v1/users/:userId/sessions')
$user = $dbForInternal->getDocument('users', $userId);
if ($user->isEmpty()) {
if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404);
}
@@ -266,7 +274,7 @@ App::get('/v1/users/:userId/logs')
$user = $dbForInternal->getDocument('users', $userId);
if ($user->isEmpty()) {
if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404);
}
@@ -374,7 +382,7 @@ App::patch('/v1/users/:userId/status')
$user = $dbForInternal->getDocument('users', $userId);
if ($user->isEmpty()) {
if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404);
}
@@ -410,7 +418,7 @@ App::patch('/v1/users/:userId/verification')
$user = $dbForInternal->getDocument('users', $userId);
if ($user->isEmpty()) {
if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404);
}
@@ -446,7 +454,7 @@ App::patch('/v1/users/:userId/name')
$user = $dbForInternal->getDocument('users', $userId);
if ($user->isEmpty()) {
if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404);
}
@@ -485,7 +493,7 @@ App::patch('/v1/users/:userId/password')
$user = $dbForInternal->getDocument('users', $userId);
if ($user->isEmpty()) {
if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404);
}
@@ -525,7 +533,7 @@ App::patch('/v1/users/:userId/email')
$user = $dbForInternal->getDocument('users', $userId);
if ($user->isEmpty()) {
if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404);
}
@@ -569,7 +577,7 @@ App::patch('/v1/users/:userId/prefs')
$user = $dbForInternal->getDocument('users', $userId);
if ($user->isEmpty()) {
if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404);
}
@@ -606,7 +614,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
$user = $dbForInternal->getDocument('users', $userId);
if ($user->isEmpty()) {
if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404);
}
@@ -661,7 +669,7 @@ App::delete('/v1/users/:userId/sessions')
$user = $dbForInternal->getDocument('users', $userId);
if ($user->isEmpty()) {
if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404);
}
@@ -710,26 +718,35 @@ App::delete('/v1/users/:userId')
$user = $dbForInternal->getDocument('users', $userId);
if ($user->isEmpty()) {
if ($user->isEmpty() || $user->getAttribute('deleted')) {
throw new Exception('User not found', 404);
}
if (!$dbForInternal->deleteDocument('users', $userId)) {
throw new Exception('Failed to remove user from DB', 500);
}
// clone user object to send to workers
$clone = clone $user;
$user
->setAttribute("name", null)
->setAttribute("email", null)
->setAttribute("password", null)
->setAttribute("deleted", true)
;
$dbForInternal->updateDocument('users', $userId, $user);
$deletes
->setParam('type', DELETE_TYPE_DOCUMENT)
->setParam('document', $user)
->setParam('document', $clone)
;
$events
->setParam('eventData', $response->output($user, Response::MODEL_USER))
->setParam('eventData', $response->output($clone, Response::MODEL_USER))
;
$usage
->setParam('users.delete', 1)
;
$response->noContent();
});
+50 -1
View File
@@ -31,6 +31,7 @@ use Appwrite\Network\Validator\URL;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Stats\Stats;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\View;
use Utopia\Config\Config;
use Utopia\Locale\Locale;
@@ -62,7 +63,7 @@ const APP_PAGING_LIMIT = 12;
const APP_LIMIT_COUNT = 5000;
const APP_LIMIT_USERS = 10000;
const APP_CACHE_BUSTER = 151;
const APP_VERSION_STABLE = '0.9.4';
const APP_VERSION_STABLE = '0.11.0';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
const APP_DATABASE_ATTRIBUTE_URL = 'url';
@@ -240,6 +241,54 @@ Database::addFilter('subQueryIndexes',
return $database
->find('indexes', [
new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()])
], 64, 0, []);
}
);
Database::addFilter('subQueryPlatforms',
function($value) {
return null;
},
function($value, Document $document, Database $database) {
return $database
->find('platforms', [
new Query('projectId', Query::TYPE_EQUAL, [$document->getId()])
], $database->getIndexLimit(), 0, []);
}
);
Database::addFilter('subQueryDomains',
function($value) {
return null;
},
function($value, Document $document, Database $database) {
return $database
->find('domains', [
new Query('projectId', Query::TYPE_EQUAL, [$document->getId()])
], $database->getIndexLimit(), 0, []);
}
);
Database::addFilter('subQueryKeys',
function($value) {
return null;
},
function($value, Document $document, Database $database) {
return $database
->find('keys', [
new Query('projectId', Query::TYPE_EQUAL, [$document->getId()])
], $database->getIndexLimit(), 0, []);
}
);
Database::addFilter('subQueryWebhooks',
function($value) {
return null;
},
function($value, Document $document, Database $database) {
return $database
->find('webhooks', [
new Query('projectId', Query::TYPE_EQUAL, [$document->getId()])
], $database->getIndexLimit(), 0, []);
}
);
+1 -1
View File
@@ -29,7 +29,7 @@ $cli
$production = ($git) ? (Console::confirm('Type "Appwrite" to push code to production git repos') == 'Appwrite') : false;
$message = ($git) ? Console::confirm('Please enter your commit message:') : '';
if(!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x'])) {
if(!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x'])) {
throw new Exception('Unknown version given');
}
+2 -2
View File
@@ -342,7 +342,7 @@ $cli
do { // list projects
try {
$attempts++;
$projects = $dbForConsole->find('projects', [], 100, orderAfter:$latestProject);
$projects = $dbForConsole->find('projects', [], 100, cursor:$latestProject);
break; // leave the do-while if successful
} catch (\Exception $e) {
Console::warning("Console DB not ready yet. Retrying ({$attempts})...");
@@ -472,7 +472,7 @@ $cli
do { // Loop over all the parent collection document for each sub collection
$dbForProject->setNamespace("project_{$projectId}_{$options['namespace']}");
$parents = $dbForProject->find($collection, [], 100, orderAfter:$latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections
$parents = $dbForProject->find($collection, [], 100, cursor:$latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections
if (empty($parents)) {
continue;
+56 -5
View File
@@ -94,7 +94,7 @@ class DatabaseV1 extends Worker
$dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed'));
}
$dbForInternal->purgeDocument('collections', $collectionId);
$dbForInternal->deleteCachedDocument('collections', $collectionId);
}
/**
@@ -120,7 +120,58 @@ class DatabaseV1 extends Worker
$dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'stuck'));
}
$dbForInternal->purgeDocument('collections', $collectionId);
// The underlying database removes/rebuilds indexes when attribute is removed
// Update indexes table with changes
/** @var Document[] $indexes */
$indexes = $collection->getAttribute('indexes', []);
foreach ($indexes as $index) {
/** @var string[] $attributes */
$attributes = $index->getAttribute('attributes');
$lengths = $index->getAttribute('lengths');
$orders = $index->getAttribute('orders');
$found = \array_search($key, $attributes);
if ($found !== false) {
// If found, remove entry from attributes, lengths, and orders
// array_values wraps array_diff to reindex array keys
// when found attribute is removed from array
$attributes = \array_values(\array_diff($attributes, [$attributes[$found]]));
$lengths = \array_values(\array_diff($lengths, [$lengths[$found]]));
$orders = \array_values(\array_diff($orders, [$orders[$found]]));
if (empty($attributes)) {
$dbForInternal->deleteDocument('indexes', $index->getId());
} else {
$index
->setAttribute('attributes', $attributes, Document::SET_TYPE_ASSIGN)
->setAttribute('lengths', $lengths, Document::SET_TYPE_ASSIGN)
->setAttribute('orders', $orders, Document::SET_TYPE_ASSIGN)
;
// Check if an index exists with the same attributes and orders
$exists = false;
foreach ($indexes as $existing) {
if ($existing->getAttribute('key') !== $index->getAttribute('key') // Ignore itself
&& $existing->getAttribute('attributes') === $index->getAttribute('attributes')
&& $existing->getAttribute('orders') === $index->getAttribute('orders')
) {
$exists = true;
break;
}
}
if ($exists) { // Delete the duplicate if created, else update in db
$this->deleteIndex($collection, $index, $projectId);
} else {
$dbForInternal->updateDocument('indexes', $index->getId(), $index);
}
}
}
}
$dbForInternal->deleteCachedDocument('collections', $collectionId);
}
/**
@@ -150,7 +201,7 @@ class DatabaseV1 extends Worker
$dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'failed'));
}
$dbForInternal->purgeDocument('collections', $collectionId);
$dbForInternal->deleteCachedDocument('collections', $collectionId);
}
/**
@@ -168,7 +219,7 @@ class DatabaseV1 extends Worker
try {
if(!$dbForExternal->deleteIndex($collectionId, $key) && $index->getAttribute('status') !== 'failed') {
throw new Exception('Failed to delete Attribute');
throw new Exception('Failed to delete index');
}
$dbForInternal->deleteDocument('indexes', $index->getId());
@@ -177,6 +228,6 @@ class DatabaseV1 extends Worker
$dbForInternal->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'stuck'));
}
$dbForInternal->purgeDocument('collections', $collectionId);
$dbForInternal->deleteCachedDocument('collections', $collectionId);
}
}
+2 -7
View File
@@ -45,7 +45,7 @@
"utopia-php/cache": "0.4.*",
"utopia-php/cli": "0.11.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "dev-feat-get-limit-methods-on-database as 0.10.0",
"utopia-php/database": "0.10.*",
"utopia-php/locale": "0.4.*",
"utopia-php/registry": "0.5.*",
"utopia-php/preloader": "0.2.*",
@@ -63,12 +63,7 @@
"adhocore/jwt": "1.1.2",
"slickdeals/statsd": "3.1.0"
},
"repositories": [
{
"type": "git",
"url": "https://github.com/utopia-php/database"
}
],
"repositories": [],
"require-dev": {
"appwrite/sdk-generator": "0.13.0",
"swoole/ide-helper": "4.6.7",
Generated
+25 -28
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": "ffd14884e3b377752bdb699c5693cb0d",
"content-hash": "59d828599adbf037148a36d34233dea9",
"packages": [
{
"name": "adhocore/jwt",
@@ -1984,11 +1984,17 @@
},
{
"name": "utopia-php/database",
"version": "dev-feat-get-limit-methods-on-database",
"version": "0.10.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database",
"reference": "4cc9d5eb9c6be1a9116a4d9dc2e6bd9db4e8e9c0"
"url": "https://github.com/utopia-php/database.git",
"reference": "b7c60b0ec769a9050dd2b939b78ff1f5d4fa27e8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/b7c60b0ec769a9050dd2b939b78ff1f5d4fa27e8",
"reference": "b7c60b0ec769a9050dd2b939b78ff1f5d4fa27e8",
"shasum": ""
},
"require": {
"ext-mongodb": "*",
@@ -2011,11 +2017,7 @@
"Utopia\\Database\\": "src/Database"
}
},
"autoload-dev": {
"psr-4": {
"Utopia\\Tests\\": "tests/Database"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@@ -2037,7 +2039,11 @@
"upf",
"utopia"
],
"time": "2021-09-17T01:44:11+00:00"
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.10.0"
},
"time": "2021-10-04T17:23:25+00:00"
},
{
"name": "utopia-php/domains",
@@ -3712,16 +3718,16 @@
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.5.0",
"version": "1.5.1",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f"
"reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/30f38bffc6f24293dadd1823936372dfa9e86e2f",
"reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae",
"reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae",
"shasum": ""
},
"require": {
@@ -3756,9 +3762,9 @@
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.0"
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1"
},
"time": "2021-09-17T15:28:14+00:00"
"time": "2021-10-02T14:08:47+00:00"
},
{
"name": "phpspec/prophecy",
@@ -6249,18 +6255,9 @@
"time": "2015-12-17T08:42:14+00:00"
}
],
"aliases": [
{
"package": "utopia-php/database",
"version": "dev-feat-get-limit-methods-on-database",
"alias": "0.10.0",
"alias_normalized": "0.10.0.0"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"utopia-php/database": 20
},
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
@@ -6282,5 +6279,5 @@
"platform-overrides": {
"php": "8.0"
},
"plugin-api-version": "2.1.0"
"plugin-api-version": "2.0.0"
}
+1 -1
View File
@@ -63,7 +63,7 @@ services:
- ./psalm.xml:/usr/src/code/psalm.xml
- ./tests:/usr/src/code/tests
- ./app:/usr/src/code/app
# - ./vendor/utopia-php/database:/usr/src/code/vendor/utopia-php/database
# - ./vendor:/usr/src/code/vendor
- ./docs:/usr/src/code/docs
- ./public:/usr/src/code/public
- ./src:/usr/src/code/src
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.createAnonymousSession();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.createJWT();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,10 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
// Go to OAuth provider login page
sdk.account.createOAuth2Session('amazon');
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.createRecovery('email@example.com', 'https://example.com');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.createSession('email@example.com', 'password');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.createVerification('https://example.com');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.create('', 'email@example.com', 'password');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.deleteSession('[SESSION_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.deleteSessions();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.delete();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.getLogs();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.getPrefs();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.getSession('[SESSION_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.getSessions();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.get();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.updateEmail('email@example.com', 'password');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.updateName('[NAME]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.updatePassword('password');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.updatePrefs({});
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.updateRecovery('[USER_ID]', '[SECRET]', 'password', 'password');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.updateVerification('[USER_ID]', '[SECRET]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,10 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let result = sdk.avatars.getBrowser('aa');
console.log(result); // Resource URL
@@ -1,10 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let result = sdk.avatars.getCreditCard('amex');
console.log(result); // Resource URL
@@ -1,10 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let result = sdk.avatars.getFavicon('https://example.com');
console.log(result); // Resource URL
@@ -1,10 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let result = sdk.avatars.getFlag('af');
console.log(result); // Resource URL
@@ -1,10 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let result = sdk.avatars.getImage('https://example.com');
console.log(result); // Resource URL
@@ -1,10 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let result = sdk.avatars.getInitials();
console.log(result); // Resource URL
@@ -1,10 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let result = sdk.avatars.getQR('[TEXT]');
console.log(result); // Resource URL
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.createAttribute('[COLLECTION_ID]', '', '[TYPE]', null, false);
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.createDocument('[COLLECTION_ID]', '', {});
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.createIndex('[COLLECTION_ID]', '', 'text', []);
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.deleteAttribute('[COLLECTION_ID]', '');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.deleteDocument('[COLLECTION_ID]', '[DOCUMENT_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.deleteIndex('[COLLECTION_ID]', '');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.getAttribute('[COLLECTION_ID]', '');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.getDocument('[COLLECTION_ID]', '[DOCUMENT_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.getIndex('[COLLECTION_ID]', '');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.listAttributes('[COLLECTION_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.listDocuments('[COLLECTION_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.listIndexes('[COLLECTION_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.updateDocument('[COLLECTION_ID]', '[DOCUMENT_ID]', {});
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.functions.createExecution('[FUNCTION_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.functions.getExecution('[FUNCTION_ID]', '[EXECUTION_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.functions.listExecutions('[FUNCTION_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.locale.getContinents();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.locale.getCountriesEU();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.locale.getCountriesPhones();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.locale.getCountries();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.locale.getCurrencies();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.locale.getLanguages();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.locale.get();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.storage.createFile('', document.getElementById('uploader').files[0]);
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.storage.deleteFile('[FILE_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,10 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let result = sdk.storage.getFileDownload('[FILE_ID]');
console.log(result); // Resource URL
@@ -1,10 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let result = sdk.storage.getFilePreview('[FILE_ID]');
console.log(result); // Resource URL
@@ -1,10 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let result = sdk.storage.getFileView('[FILE_ID]');
console.log(result); // Resource URL
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.storage.getFile('[FILE_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.storage.listFiles();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.storage.updateFile('[FILE_ID]', [], []);
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.teams.createMembership('[TEAM_ID]', 'email@example.com', [], 'https://example.com');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.teams.create('', '[NAME]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.teams.deleteMembership('[TEAM_ID]', '[MEMBERSHIP_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.teams.delete('[TEAM_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.teams.getMembership('[TEAM_ID]', '[MEMBERSHIP_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.teams.getMemberships('[TEAM_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.teams.get('[TEAM_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.teams.list();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.teams.updateMembershipRoles('[TEAM_ID]', '[MEMBERSHIP_ID]', []);
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.teams.updateMembershipStatus('[TEAM_ID]', '[MEMBERSHIP_ID]', '[USER_ID]', '[SECRET]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.teams.update('[TEAM_ID]', '[NAME]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.createAnonymousSession();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.createJWT();
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,10 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
// Go to OAuth provider login page
sdk.account.createOAuth2Session('amazon');
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.createRecovery('email@example.com', 'https://example.com');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.createSession('email@example.com', 'password');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.createVerification('https://example.com');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.create('', 'email@example.com', 'password');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
@@ -1,14 +0,0 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.deleteSession('[SESSION_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

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