Merge branch 'feat-database-indexing' of https://github.com/appwrite/appwrite into feat-before-pagination
This commit is contained in:
commit
9941198a7f
|
@ -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;
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -15,6 +15,7 @@ use Utopia\Database\Database;
|
|||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Adapter\MariaDB;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\Permissions;
|
||||
use Utopia\Database\Validator\QueryValidator;
|
||||
|
@ -31,7 +32,6 @@ use Appwrite\Network\Validator\IP;
|
|||
use Appwrite\Network\Validator\URL;
|
||||
use Appwrite\Utopia\Response;
|
||||
use DeviceDetector\DeviceDetector;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
|
||||
/**
|
||||
* Create attribute of varying type
|
||||
|
@ -77,7 +77,7 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $
|
|||
}
|
||||
|
||||
try {
|
||||
$attribute = $dbForInternal->createDocument('attributes', new Document([
|
||||
$attribute = new Document([
|
||||
'$id' => $collectionId.'_'.$attributeId,
|
||||
'key' => $attributeId,
|
||||
'collectionId' => $collectionId,
|
||||
|
@ -90,11 +90,17 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $
|
|||
'array' => $array,
|
||||
'format' => $format,
|
||||
'formatOptions' => $formatOptions,
|
||||
'filters' => $filters,
|
||||
]));
|
||||
} catch (DuplicateException $th) {
|
||||
]);
|
||||
|
||||
$dbForInternal->checkAttribute($collection, $attribute);
|
||||
$attribute = $dbForInternal->createDocument('attributes', $attribute);
|
||||
}
|
||||
catch (DuplicateException $exception) {
|
||||
throw new Exception('Attribute already exists', 409);
|
||||
}
|
||||
catch (LimitException $exception) {
|
||||
throw new Exception('Attribute limit exceeded', 400);
|
||||
}
|
||||
|
||||
$dbForInternal->deleteCachedDocument('collections', $collectionId);
|
||||
|
||||
|
@ -218,6 +224,12 @@ App::get('/v1/database/collections')
|
|||
}
|
||||
}
|
||||
|
||||
$queries = [];
|
||||
|
||||
if (!empty($search)) {
|
||||
$queries[] = new Query('name', Query::TYPE_SEARCH, [$search]);
|
||||
}
|
||||
|
||||
$usage->setParam('database.collections.read', 1);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
|
@ -1454,13 +1466,28 @@ App::post('/v1/database/collections/:collectionId/documents')
|
|||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
// Check collection permissions when enforced
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
$validator = new Authorization('write');
|
||||
if (!$validator->isValid($collection->getWrite())) {
|
||||
throw new Exception('Unauthorized permissions', 401);
|
||||
}
|
||||
}
|
||||
|
||||
$data['$collection'] = $collection->getId(); // Adding this param to make API easier for developers
|
||||
$data['$id'] = $documentId == 'unique()' ? $dbForExternal->getId() : $documentId;
|
||||
$data['$read'] = (is_null($read) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $read ?? []; // By default set read permissions for user
|
||||
$data['$write'] = (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? []; // By default set write permissions for user
|
||||
|
||||
try {
|
||||
$document = $dbForExternal->createDocument($collectionId, new Document($data));
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
/** @var Document $document */
|
||||
$document = Authorization::skip(function() use ($dbForExternal, $collectionId, $data) {
|
||||
return $dbForExternal->createDocument($collectionId, new Document($data));
|
||||
});
|
||||
} else {
|
||||
$document = $dbForExternal->createDocument($collectionId, new Document($data));
|
||||
}
|
||||
}
|
||||
catch (StructureException $exception) {
|
||||
throw new Exception($exception->getMessage(), 400);
|
||||
|
@ -1519,6 +1546,14 @@ App::get('/v1/database/collections/:collectionId/documents')
|
|||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
// Check collection permissions when enforced
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
$validator = new Authorization('read');
|
||||
if (!$validator->isValid($collection->getRead())) {
|
||||
throw new Exception('Unauthorized permissions', 401);
|
||||
}
|
||||
}
|
||||
|
||||
$queries = \array_map(function ($query) {
|
||||
return Query::parse($query);
|
||||
}, $queries);
|
||||
|
@ -1538,6 +1573,15 @@ App::get('/v1/database/collections/:collectionId/documents')
|
|||
}
|
||||
}
|
||||
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
/** @var Document[] $documents */
|
||||
$documents = Authorization::skip(function() use ($dbForExternal, $collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument, $cursorDirection) {
|
||||
return $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection);
|
||||
});
|
||||
} else {
|
||||
$documents = $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection);
|
||||
}
|
||||
|
||||
$usage
|
||||
->setParam('database.documents.read', 1)
|
||||
->setParam('collectionId', $collectionId)
|
||||
|
@ -1545,7 +1589,7 @@ App::get('/v1/database/collections/:collectionId/documents')
|
|||
|
||||
$response->dynamic(new Document([
|
||||
'sum' => $dbForExternal->count($collectionId, $queries, APP_LIMIT_COUNT),
|
||||
'documents' => $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursorDocument ?? null, $cursorDirection),
|
||||
'documents' => $documents,
|
||||
]), Response::MODEL_DOCUMENT_LIST);
|
||||
});
|
||||
|
||||
|
@ -1577,7 +1621,22 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
$document = $dbForExternal->getDocument($collectionId, $documentId);
|
||||
// Check collection permissions when enforced
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
$validator = new Authorization('read');
|
||||
if (!$validator->isValid($collection->getRead())) {
|
||||
throw new Exception('Unauthorized permissions', 401);
|
||||
}
|
||||
}
|
||||
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
/** @var Document $document */
|
||||
$document = Authorization::skip(function() use ($dbForExternal, $collectionId, $documentId) {
|
||||
return $dbForExternal->getDocument($collectionId, $documentId);
|
||||
});
|
||||
} else {
|
||||
$document = $dbForExternal->getDocument($collectionId, $documentId);
|
||||
}
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception('No document found', 404);
|
||||
|
@ -1626,6 +1685,14 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
// Check collection permissions when enforced
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
$validator = new Authorization('write');
|
||||
if (!$validator->isValid($collection->getWrite())) {
|
||||
throw new Exception('Unauthorized permissions', 401);
|
||||
}
|
||||
}
|
||||
|
||||
$document = $dbForExternal->getDocument($collectionId, $documentId);
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
|
@ -1650,7 +1717,14 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
$data['$write'] = (is_null($write)) ? ($document->getWrite() ?? []) : $write; // By default inherit write permissions
|
||||
|
||||
try {
|
||||
$document = $dbForExternal->updateDocument($collection->getId(), $document->getId(), new Document($data));
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
/** @var Document $document */
|
||||
$document = Authorization::skip(function() use ($dbForExternal, $collection, $document, $data) {
|
||||
return $dbForExternal->updateDocument($collection->getId(), $document->getId(), new Document($data));
|
||||
});
|
||||
} else {
|
||||
$document = $dbForExternal->updateDocument($collection->getId(), $document->getId(), new Document($data));
|
||||
}
|
||||
}
|
||||
catch (AuthorizationException $exception) {
|
||||
throw new Exception('Unauthorized permissions', 401);
|
||||
|
@ -1708,7 +1782,22 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
$document = $dbForExternal->getDocument($collectionId, $documentId);
|
||||
// Check collection permissions when enforced
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
$validator = new Authorization('write');
|
||||
if (!$validator->isValid($collection->getWrite())) {
|
||||
throw new Exception('Unauthorized permissions', 401);
|
||||
}
|
||||
}
|
||||
|
||||
if ($collection->getAttribute('permission') === 'collection') {
|
||||
/** @var Document $document */
|
||||
$document = Authorization::skip(function() use ($dbForExternal, $collectionId, $documentId) {
|
||||
return $dbForExternal->getDocument($collectionId, $documentId);
|
||||
});
|
||||
} else {
|
||||
$document = $dbForExternal->getDocument($collectionId, $documentId);
|
||||
}
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception('No document found', 404);
|
||||
|
|
|
@ -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);
|
||||
|
@ -97,8 +99,6 @@ App::get('/v1/functions')
|
|||
/** @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);
|
||||
|
||||
|
@ -107,6 +107,12 @@ App::get('/v1/functions')
|
|||
}
|
||||
}
|
||||
|
||||
$queries = [];
|
||||
|
||||
if (!empty($search)) {
|
||||
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'functions' => $dbForInternal->find('functions', $queries, $limit, $offset, [], [$orderType], $cursorFunction ?? null, $cursorDirection),
|
||||
'sum' => $dbForInternal->count('functions', $queries, APP_LIMIT_COUNT),
|
||||
|
@ -272,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) {
|
||||
|
@ -448,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(),
|
||||
|
@ -457,6 +465,7 @@ App::post('/v1/functions/:functionId/tags')
|
|||
'command' => $command,
|
||||
'path' => $path,
|
||||
'size' => $size,
|
||||
'search' => implode(' ', [$tagId, $command]),
|
||||
]));
|
||||
|
||||
$usage
|
||||
|
@ -497,8 +506,6 @@ 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);
|
||||
|
||||
|
@ -507,6 +514,14 @@ App::get('/v1/functions/:functionId/tags')
|
|||
}
|
||||
}
|
||||
|
||||
$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);
|
||||
|
||||
|
@ -667,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(),
|
||||
|
@ -680,6 +697,7 @@ App::post('/v1/functions/:functionId/executions')
|
|||
'stdout' => '',
|
||||
'stderr' => '',
|
||||
'time' => 0.0,
|
||||
'search' => implode(' ', [$functionId, $executionId]),
|
||||
]));
|
||||
|
||||
Authorization::reset();
|
||||
|
@ -734,17 +752,18 @@ 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('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, $cursor, $cursorDirection, $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);
|
||||
|
@ -758,13 +777,16 @@ App::get('/v1/functions/:functionId/executions')
|
|||
}
|
||||
}
|
||||
|
||||
$results = $dbForInternal->find('executions', [
|
||||
new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]),
|
||||
], $limit, $offset, [], [Database::ORDER_DESC], $cursorExecution ?? null, $cursorDirection);
|
||||
$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,
|
||||
|
|
|
@ -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],
|
||||
|
@ -94,12 +96,14 @@ App::post('/v1/projects')
|
|||
'legalCity' => $legalCity,
|
||||
'legalAddress' => $legalAddress,
|
||||
'legalTaxId' => $legalTaxId,
|
||||
'services' => new \stdClass(),
|
||||
'platforms' => [],
|
||||
'webhooks' => [],
|
||||
'keys' => [],
|
||||
'domains' => [],
|
||||
'services' => new stdClass(),
|
||||
'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) {
|
||||
|
@ -172,8 +176,6 @@ App::get('/v1/projects')
|
|||
/** @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);
|
||||
|
||||
|
@ -182,6 +184,12 @@ App::get('/v1/projects')
|
|||
}
|
||||
}
|
||||
|
||||
$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);
|
||||
|
||||
|
@ -357,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);
|
||||
|
@ -380,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);
|
||||
|
||||
|
@ -458,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);
|
||||
|
@ -582,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,
|
||||
|
@ -590,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);
|
||||
|
@ -621,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,
|
||||
|
@ -653,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);
|
||||
}
|
||||
|
||||
|
@ -694,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);
|
||||
});
|
||||
|
@ -737,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();
|
||||
});
|
||||
|
@ -775,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);
|
||||
|
@ -811,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,
|
||||
|
@ -834,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);
|
||||
}
|
||||
|
||||
|
@ -875,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);
|
||||
});
|
||||
|
@ -914,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();
|
||||
});
|
||||
|
@ -955,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,
|
||||
|
@ -964,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);
|
||||
|
@ -995,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,
|
||||
|
@ -1027,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);
|
||||
}
|
||||
|
||||
|
@ -1064,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);
|
||||
}
|
||||
|
||||
|
@ -1078,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);
|
||||
});
|
||||
|
@ -1114,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();
|
||||
});
|
||||
|
@ -1149,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);
|
||||
}
|
||||
|
||||
|
@ -1165,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(),
|
||||
|
@ -1173,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);
|
||||
|
@ -1204,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,
|
||||
|
@ -1236,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);
|
||||
}
|
||||
|
||||
|
@ -1269,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);
|
||||
}
|
||||
|
||||
|
@ -1291,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', [
|
||||
|
@ -1330,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)
|
||||
|
|
|
@ -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
|
||||
|
@ -186,8 +188,6 @@ App::get('/v1/storage/files')
|
|||
/** @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);
|
||||
|
||||
|
@ -196,6 +196,12 @@ App::get('/v1/storage/files')
|
|||
}
|
||||
}
|
||||
|
||||
$queries = [];
|
||||
|
||||
if (!empty($search)) {
|
||||
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
|
||||
}
|
||||
|
||||
$usage
|
||||
->setParam('storage.files.read', 1)
|
||||
->setParam('bucketId', 'default')
|
||||
|
|
|
@ -58,6 +58,7 @@ App::post('/v1/teams')
|
|||
'name' => $name,
|
||||
'sum' => ($isPrivilegedUser || $isAppUser) ? 0 : 1,
|
||||
'dateCreated' => \time(),
|
||||
'search' => implode(' ', [$teamId, $name]),
|
||||
]));
|
||||
|
||||
Authorization::reset();
|
||||
|
@ -109,8 +110,6 @@ App::get('/v1/teams')
|
|||
/** @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);
|
||||
|
||||
|
@ -119,6 +118,12 @@ App::get('/v1/teams')
|
|||
}
|
||||
}
|
||||
|
||||
$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);
|
||||
|
||||
|
@ -181,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);
|
||||
});
|
||||
|
@ -325,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);
|
||||
|
|
|
@ -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);
|
||||
|
@ -108,20 +110,25 @@ App::get('/v1/users')
|
|||
$cursorUser = $dbForInternal->getDocument('users', $cursor);
|
||||
|
||||
if ($cursorUser->isEmpty()) {
|
||||
throw new Exception('User for after not found', 400);
|
||||
throw new Exception("User '{$cursor}' for the 'after' value not found.", 400);
|
||||
}
|
||||
}
|
||||
|
||||
$results = $dbForInternal->find('users', [], $limit, $offset, [], [$orderType], $cursorUser ?? null, $cursorDirection);
|
||||
$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);
|
||||
});
|
||||
|
||||
|
@ -147,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);
|
||||
}
|
||||
|
||||
|
@ -179,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);
|
||||
}
|
||||
|
||||
|
@ -215,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);
|
||||
}
|
||||
|
||||
|
@ -267,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);
|
||||
}
|
||||
|
||||
|
@ -375,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);
|
||||
}
|
||||
|
||||
|
@ -411,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);
|
||||
}
|
||||
|
||||
|
@ -447,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);
|
||||
}
|
||||
|
||||
|
@ -486,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);
|
||||
}
|
||||
|
||||
|
@ -526,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);
|
||||
}
|
||||
|
||||
|
@ -570,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);
|
||||
}
|
||||
|
||||
|
@ -607,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);
|
||||
}
|
||||
|
||||
|
@ -662,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);
|
||||
}
|
||||
|
||||
|
@ -711,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();
|
||||
});
|
||||
|
||||
|
|
53
app/init.php
53
app/init.php
|
@ -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;
|
||||
|
@ -228,7 +229,7 @@ Database::addFilter('subQueryAttributes',
|
|||
return $database
|
||||
->find('attributes', [
|
||||
new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()])
|
||||
], 100, 0, []);
|
||||
], $database->getAttributeLimit(), 0, []);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -240,7 +241,55 @@ Database::addFilter('subQueryIndexes',
|
|||
return $database
|
||||
->find('indexes', [
|
||||
new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()])
|
||||
], 100, 0, []);
|
||||
], 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, []);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -120,6 +120,57 @@ class DatabaseV1 extends Worker
|
|||
$dbForInternal->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed'));
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -168,7 +219,7 @@ class DatabaseV1 extends Worker
|
|||
|
||||
try {
|
||||
if(!$dbForExternal->deleteIndex($collectionId, $key)) {
|
||||
throw new Exception('Failed to delete Attribute');
|
||||
throw new Exception('Failed to delete index');
|
||||
}
|
||||
|
||||
$dbForInternal->deleteDocument('indexes', $index->getId());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -577,7 +577,7 @@ class Database
|
|||
{
|
||||
if (!isset(self::$filters[$name])) {
|
||||
return $value;
|
||||
throw new Exception('Filter not found');
|
||||
throw new Exception("Filter '{$name}' not found");
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -599,7 +599,7 @@ class Database
|
|||
{
|
||||
if (!isset(self::$filters[$name])) {
|
||||
return $value;
|
||||
throw new Exception('Filter not found');
|
||||
throw new Exception("Filter '{$name}' not found");
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -1276,7 +1276,7 @@ trait DatabaseBase
|
|||
// $this->assertEquals('Minimum value must be lesser than maximum value', $invalidRange['body']['message']);
|
||||
|
||||
// wait for worker to add attributes
|
||||
sleep(2);
|
||||
sleep(3);
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
@ -1612,6 +1612,144 @@ trait DatabaseBase
|
|||
return $data;
|
||||
}
|
||||
|
||||
|
||||
public function testEnforceCollectionPermissions()
|
||||
{
|
||||
$user = 'user:' . $this->getUser()['$id'];
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => 'unique()',
|
||||
'name' => 'enforceCollectionPermissions',
|
||||
'permission' => 'collection',
|
||||
'read' => [$user],
|
||||
'write' => [$user]
|
||||
]);
|
||||
|
||||
$this->assertEquals($collection['headers']['status-code'], 201);
|
||||
$this->assertEquals($collection['body']['name'], 'enforceCollectionPermissions');
|
||||
$this->assertEquals($collection['body']['permission'], 'collection');
|
||||
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
sleep(2);
|
||||
|
||||
$attribute = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/string', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'attribute',
|
||||
'size' => 64,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $attribute['headers']['status-code'], 201);
|
||||
$this->assertEquals('attribute', $attribute['body']['key']);
|
||||
|
||||
// wait for db to add attribute
|
||||
sleep(2);
|
||||
|
||||
$index = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/indexes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'key_attribute',
|
||||
'type' => 'key',
|
||||
'attributes' => [$attribute['body']['key']],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $index['headers']['status-code']);
|
||||
$this->assertEquals('key_attribute', $index['body']['key']);
|
||||
|
||||
// wait for db to add attribute
|
||||
sleep(2);
|
||||
|
||||
$document1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => 'unique()',
|
||||
'data' => [
|
||||
'attribute' => 'one',
|
||||
],
|
||||
'read' => [$user],
|
||||
'write' => [$user],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $document1['headers']['status-code']);
|
||||
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(1, $documents['body']['sum']);
|
||||
$this->assertCount(1, $documents['body']['documents']);
|
||||
|
||||
/*
|
||||
* Test for Failure
|
||||
*/
|
||||
|
||||
// Remove write permission
|
||||
$collection = $this->client->call(Client::METHOD_PUT, '/database/collections/' . $collectionId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'name' => 'enforceCollectionPermissions',
|
||||
'permission' => 'collection',
|
||||
'read' => [$user],
|
||||
'write' => []
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $collection['headers']['status-code']);
|
||||
|
||||
$badDocument = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'documentId' => 'unique()',
|
||||
'data' => [
|
||||
'attribute' => 'bad',
|
||||
],
|
||||
'read' => [$user],
|
||||
'write' => [$user],
|
||||
]);
|
||||
|
||||
if($this->getSide() == 'client') {
|
||||
$this->assertEquals(401, $badDocument['headers']['status-code']);
|
||||
}
|
||||
|
||||
if($this->getSide() == 'server') {
|
||||
$this->assertEquals(201, $badDocument['headers']['status-code']);
|
||||
}
|
||||
|
||||
// Remove read permission
|
||||
$collection = $this->client->call(Client::METHOD_PUT, '/database/collections/' . $collectionId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'name' => 'enforceCollectionPermissions',
|
||||
'permission' => 'collection',
|
||||
'read' => [],
|
||||
'write' => []
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $collection['headers']['status-code']);
|
||||
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]));
|
||||
|
||||
$this->assertEquals(404, $documents['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testDefaultPermissions
|
||||
*/
|
||||
|
|
|
@ -284,6 +284,214 @@ class DatabaseCustomServerTest extends Scope
|
|||
/**
|
||||
* @depends testDeleteIndex
|
||||
*/
|
||||
public function testDeleteIndexOnDeleteAttribute($data)
|
||||
{
|
||||
$attribute1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/attributes/string', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'attribute1',
|
||||
'size' => 16,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$attribute2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/attributes/string', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'attribute2',
|
||||
'size' => 16,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $attribute1['headers']['status-code']);
|
||||
$this->assertEquals(201, $attribute2['headers']['status-code']);
|
||||
$this->assertEquals('attribute1', $attribute1['body']['key']);
|
||||
$this->assertEquals('attribute2', $attribute2['body']['key']);
|
||||
|
||||
sleep(2);
|
||||
|
||||
$index1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/indexes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'index1',
|
||||
'type' => 'key',
|
||||
'attributes' => ['attribute1', 'attribute2'],
|
||||
'orders' => ['ASC', 'ASC'],
|
||||
]);
|
||||
|
||||
$index2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['collectionId'] . '/indexes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'index2',
|
||||
'type' => 'key',
|
||||
'attributes' => ['attribute2'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $index1['headers']['status-code']);
|
||||
$this->assertEquals(201, $index2['headers']['status-code']);
|
||||
$this->assertEquals('index1', $index1['body']['key']);
|
||||
$this->assertEquals('index2', $index2['body']['key']);
|
||||
|
||||
sleep(2);
|
||||
|
||||
// Expected behavior: deleting attribute2 will cause index2 to be dropped, and index1 rebuilt with a single key
|
||||
$deleted = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/attributes/'. $attribute2['body']['key'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals($deleted['headers']['status-code'], 204);
|
||||
|
||||
// wait for database worker to complete
|
||||
sleep(2);
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['collectionId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals(200, $collection['headers']['status-code']);
|
||||
$this->assertIsArray($collection['body']['indexes']);
|
||||
$this->assertCount(1, $collection['body']['indexes']);
|
||||
$this->assertEquals($index1['body']['key'], $collection['body']['indexes'][0]['key']);
|
||||
$this->assertIsArray($collection['body']['indexes'][0]['attributes']);
|
||||
$this->assertCount(1, $collection['body']['indexes'][0]['attributes']);
|
||||
$this->assertEquals($attribute1['body']['key'], $collection['body']['indexes'][0]['attributes'][0]);
|
||||
|
||||
// Delete attribute
|
||||
$deleted = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['collectionId'] . '/attributes/' . $attribute1['body']['key'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals($deleted['headers']['status-code'], 204);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function testCleanupDuplicateIndexOnDeleteAttribute()
|
||||
{
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => 'unique()',
|
||||
'name' => 'TestCleanupDuplicateIndexOnDeleteAttribute',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'document',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $collection['headers']['status-code']);
|
||||
$this->assertNotEmpty($collection['body']['$id']);
|
||||
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
$attribute1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/string', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'attribute1',
|
||||
'size' => 16,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$attribute2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/string', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'attribute2',
|
||||
'size' => 16,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $attribute1['headers']['status-code']);
|
||||
$this->assertEquals(201, $attribute2['headers']['status-code']);
|
||||
$this->assertEquals('attribute1', $attribute1['body']['key']);
|
||||
$this->assertEquals('attribute2', $attribute2['body']['key']);
|
||||
|
||||
sleep(2);
|
||||
|
||||
$index1 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/indexes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'index1',
|
||||
'type' => 'key',
|
||||
'attributes' => ['attribute1', 'attribute2'],
|
||||
'orders' => ['ASC', 'ASC'],
|
||||
]);
|
||||
|
||||
$index2 = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/indexes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'index2',
|
||||
'type' => 'key',
|
||||
'attributes' => ['attribute2'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $index1['headers']['status-code']);
|
||||
$this->assertEquals(201, $index2['headers']['status-code']);
|
||||
$this->assertEquals('index1', $index1['body']['key']);
|
||||
$this->assertEquals('index2', $index2['body']['key']);
|
||||
|
||||
sleep(2);
|
||||
|
||||
// Expected behavior: deleting attribute1 would cause index1 to be a duplicate of index2 and automatically removed
|
||||
$deleted = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $collectionId . '/attributes/'. $attribute1['body']['key'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals($deleted['headers']['status-code'], 204);
|
||||
|
||||
// wait for database worker to complete
|
||||
sleep(2);
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collectionId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals(200, $collection['headers']['status-code']);
|
||||
$this->assertIsArray($collection['body']['indexes']);
|
||||
$this->assertCount(1, $collection['body']['indexes']);
|
||||
$this->assertEquals($index2['body']['key'], $collection['body']['indexes'][0]['key']);
|
||||
$this->assertIsArray($collection['body']['indexes'][0]['attributes']);
|
||||
$this->assertCount(1, $collection['body']['indexes'][0]['attributes']);
|
||||
$this->assertEquals($attribute2['body']['key'], $collection['body']['indexes'][0]['attributes'][0]);
|
||||
|
||||
// Delete attribute
|
||||
$deleted = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $collectionId . '/attributes/' . $attribute2['body']['key'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertEquals($deleted['headers']['status-code'], 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testDeleteIndexOnDeleteAttribute
|
||||
*/
|
||||
public function testDeleteCollection($data)
|
||||
{
|
||||
$collectionId = $data['collectionId'];
|
||||
|
@ -350,6 +558,101 @@ class DatabaseCustomServerTest extends Scope
|
|||
$this->assertEquals($response['headers']['status-code'], 404);
|
||||
}
|
||||
|
||||
public function testAttributeCountLimit()
|
||||
{
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => 'unique()',
|
||||
'name' => 'attributeCountLimit',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'document',
|
||||
]);
|
||||
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
// load the collection up to the limit
|
||||
for ($i=0; $i < 1012; $i++) {
|
||||
$attribute = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => "attribute{$i}",
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $attribute['headers']['status-code']);
|
||||
}
|
||||
|
||||
sleep(5);
|
||||
|
||||
$tooMany = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/integer', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => "tooMany",
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $tooMany['headers']['status-code']);
|
||||
$this->assertEquals('Attribute limit exceeded', $tooMany['body']['message']);
|
||||
}
|
||||
|
||||
public function testAttributeRowWidthLimit()
|
||||
{
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => 'attributeRowWidthLimit',
|
||||
'name' => 'attributeRowWidthLimit',
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
'permission' => 'document',
|
||||
]);
|
||||
|
||||
$this->assertEquals($collection['headers']['status-code'], 201);
|
||||
$this->assertEquals($collection['body']['name'], 'attributeRowWidthLimit');
|
||||
|
||||
$collectionId = $collection['body']['$id'];
|
||||
|
||||
// Add wide string attributes to approach row width limit
|
||||
for ($i=0; $i < 15; $i++) {
|
||||
$attribute = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/string', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => "attribute{$i}",
|
||||
'size' => 1024,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals($attribute['headers']['status-code'], 201);
|
||||
}
|
||||
|
||||
sleep(5);
|
||||
|
||||
$tooWide = $this->client->call(Client::METHOD_POST, '/database/collections/' . $collectionId . '/attributes/string', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'attributeId' => 'tooWide',
|
||||
'size' => 1024,
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $tooWide['headers']['status-code']);
|
||||
$this->assertEquals('Attribute limit exceeded', $tooWide['body']['message']);
|
||||
}
|
||||
|
||||
public function testIndexLimitException()
|
||||
{
|
||||
$collection = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
|
||||
|
|
|
@ -79,6 +79,45 @@ class FunctionsCustomServerTest extends Scope
|
|||
* Test for SUCCESS
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test search queries
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => $data['functionId']
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertCount(1, $response['body']['functions']);
|
||||
$this->assertEquals($response['body']['functions'][0]['name'], 'Test');
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'Test'
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertCount(1, $response['body']['functions']);
|
||||
$this->assertEquals($response['body']['functions'][0]['$id'], $data['functionId']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/functions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'php-8.0'
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertCount(1, $response['body']['functions']);
|
||||
$this->assertEquals($response['body']['functions'][0]['$id'], $data['functionId']);
|
||||
|
||||
/**
|
||||
* Test pagination
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
@ -307,6 +346,48 @@ class FunctionsCustomServerTest extends Scope
|
|||
$this->assertIsArray($function['body']['tags']);
|
||||
$this->assertCount(1, $function['body']['tags']);
|
||||
|
||||
/**
|
||||
* Test search queries
|
||||
*/
|
||||
$function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/tags', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders(), [
|
||||
'search' => $data['functionId']
|
||||
]));
|
||||
|
||||
$this->assertEquals($function['headers']['status-code'], 200);
|
||||
$this->assertEquals($function['body']['sum'], 1);
|
||||
$this->assertIsArray($function['body']['tags']);
|
||||
$this->assertCount(1, $function['body']['tags']);
|
||||
$this->assertEquals($function['body']['tags'][0]['$id'], $data['tagId']);
|
||||
|
||||
$function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/tags', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders(), [
|
||||
'search' => 'Test'
|
||||
]));
|
||||
|
||||
$this->assertEquals($function['headers']['status-code'], 200);
|
||||
$this->assertEquals($function['body']['sum'], 1);
|
||||
$this->assertIsArray($function['body']['tags']);
|
||||
$this->assertCount(1, $function['body']['tags']);
|
||||
$this->assertEquals($function['body']['tags'][0]['$id'], $data['tagId']);
|
||||
|
||||
$function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/tags', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders(), [
|
||||
'search' => 'php-8.0'
|
||||
]));
|
||||
|
||||
$this->assertEquals($function['headers']['status-code'], 200);
|
||||
$this->assertEquals($function['body']['sum'], 1);
|
||||
$this->assertIsArray($function['body']['tags']);
|
||||
$this->assertCount(1, $function['body']['tags']);
|
||||
$this->assertEquals($function['body']['tags'][0]['$id'], $data['tagId']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@ -418,6 +499,36 @@ class FunctionsCustomServerTest extends Scope
|
|||
$this->assertCount(1, $function['body']['executions']);
|
||||
$this->assertEquals($function['body']['executions'][0]['$id'], $data['executionId']);
|
||||
|
||||
/**
|
||||
* Test search queries
|
||||
*/
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/executions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => $data['executionId'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(1, $response['body']['sum']);
|
||||
$this->assertIsInt($response['body']['sum']);
|
||||
$this->assertCount(1, $response['body']['executions']);
|
||||
$this->assertEquals($data['functionId'], $response['body']['executions'][0]['functionId']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/executions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => $data['functionId'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(1, $response['body']['sum']);
|
||||
$this->assertIsInt($response['body']['sum']);
|
||||
$this->assertCount(1, $response['body']['executions']);
|
||||
$this->assertEquals($data['executionId'], $response['body']['executions'][0]['$id']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
|
|
@ -88,6 +88,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
@ -98,6 +99,35 @@ class ProjectsConsoleClientTest extends Scope
|
|||
$this->assertEquals($id, $response['body']['projects'][0]['$id']);
|
||||
$this->assertEquals('Project Test', $response['body']['projects'][0]['name']);
|
||||
|
||||
/**
|
||||
* Test search queries
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders(), [
|
||||
'search' => $id
|
||||
]));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertEquals($response['body']['sum'], 1);
|
||||
$this->assertIsArray($response['body']['projects']);
|
||||
$this->assertCount(1, $response['body']['projects']);
|
||||
$this->assertEquals($response['body']['projects'][0]['name'], 'Project Test');
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders(), [
|
||||
'search' => 'Project Test'
|
||||
]));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertEquals($response['body']['sum'], 1);
|
||||
$this->assertIsArray($response['body']['projects']);
|
||||
$this->assertCount(1, $response['body']['projects']);
|
||||
$this->assertEquals($response['body']['projects'][0]['$id'], $data['projectId']);
|
||||
|
||||
/**
|
||||
* Test after pagination
|
||||
*/
|
||||
|
@ -1445,6 +1475,17 @@ class ProjectsConsoleClientTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_PUT, '/projects/'.$id.'/platforms/error', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Flutter App (Android) 2',
|
||||
'key' => 'com.example.android2',
|
||||
'store' => '',
|
||||
'hostname' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(404, $response['headers']['status-code']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
|
|
@ -164,7 +164,7 @@ trait StorageBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'fileId' => 'unique()',
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'logo.png'),
|
||||
'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/file.png'), 'image/png', 'file.png'),
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
]);
|
||||
|
@ -172,9 +172,9 @@ trait StorageBase
|
|||
$this->assertEquals($file['headers']['status-code'], 201);
|
||||
$this->assertNotEmpty($file['body']['$id']);
|
||||
$this->assertIsInt($file['body']['dateCreated']);
|
||||
$this->assertEquals('logo.png', $file['body']['name']);
|
||||
$this->assertEquals('image/png', $file['body']['mimeType']);
|
||||
$this->assertEquals(47218, $file['body']['sizeOriginal']);
|
||||
$this->assertEquals('file.png', $file['body']['name']);
|
||||
$this->assertEquals('image/jpeg', $file['body']['mimeType']);
|
||||
$this->assertEquals(16804, $file['body']['sizeOriginal']);
|
||||
|
||||
$files = $this->client->call(Client::METHOD_GET, '/storage/files', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
@ -211,6 +211,32 @@ trait StorageBase
|
|||
$this->assertEquals($files['body']['files'][0]['$id'], $response['body']['files'][0]['$id']);
|
||||
$this->assertCount(1, $response['body']['files']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/storage/files', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => $data['fileId'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(1, $response['body']['sum']);
|
||||
$this->assertIsInt($response['body']['sum']);
|
||||
$this->assertCount(1, $response['body']['files']);
|
||||
$this->assertEquals('logo.png', $response['body']['files'][0]['name']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/storage/files', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'logo',
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals(1, $response['body']['sum']);
|
||||
$this->assertIsInt($response['body']['sum']);
|
||||
$this->assertCount(1, $response['body']['files']);
|
||||
$this->assertEquals($data['fileId'], $response['body']['files'][0]['$id']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
|
|
@ -173,6 +173,19 @@ trait TeamsBase
|
|||
$this->assertCount(1, $response['body']['teams']);
|
||||
$this->assertEquals('Manchester United', $response['body']['teams'][0]['name']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => $data['teamUid'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertGreaterThan(0, $response['body']['sum']);
|
||||
$this->assertIsInt($response['body']['sum']);
|
||||
$this->assertCount(1, $response['body']['teams']);
|
||||
$this->assertEquals('Arsenal', $response['body']['teams'][0]['name']);
|
||||
|
||||
$teams = $this->client->call(Client::METHOD_GET, '/teams', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
|
|
@ -17,14 +17,14 @@ trait UsersBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => 'unique()',
|
||||
'email' => 'users.service@example.com',
|
||||
'email' => 'cristiano.ronaldo@manchester-united.co.uk',
|
||||
'password' => 'password',
|
||||
'name' => 'Project User',
|
||||
'name' => 'Cristiano Ronaldo',
|
||||
]);
|
||||
|
||||
$this->assertEquals($user['headers']['status-code'], 201);
|
||||
$this->assertEquals($user['body']['name'], 'Project User');
|
||||
$this->assertEquals($user['body']['email'], 'users.service@example.com');
|
||||
$this->assertEquals($user['body']['name'], 'Cristiano Ronaldo');
|
||||
$this->assertEquals($user['body']['email'], 'cristiano.ronaldo@manchester-united.co.uk');
|
||||
$this->assertEquals($user['body']['status'], true);
|
||||
$this->assertGreaterThan(0, $user['body']['registration']);
|
||||
|
||||
|
@ -36,15 +36,15 @@ trait UsersBase
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => 'user1',
|
||||
'email' => 'users.service1@example.com',
|
||||
'email' => 'lionel.messi@psg.fr',
|
||||
'password' => 'password',
|
||||
'name' => 'Project User',
|
||||
'name' => 'Lionel Messi',
|
||||
]);
|
||||
|
||||
$this->assertEquals($res['headers']['status-code'], 201);
|
||||
$this->assertEquals($res['body']['$id'], 'user1');
|
||||
$this->assertEquals($res['body']['name'], 'Project User');
|
||||
$this->assertEquals($res['body']['email'], 'users.service1@example.com');
|
||||
$this->assertEquals($res['body']['name'], 'Lionel Messi');
|
||||
$this->assertEquals($res['body']['email'], 'lionel.messi@psg.fr');
|
||||
$this->assertEquals(true, $res['body']['status']);
|
||||
$this->assertGreaterThan(0, $res['body']['registration']);
|
||||
|
||||
|
@ -57,7 +57,7 @@ trait UsersBase
|
|||
public function testListUsers(array $data): void
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
* Test for SUCCESS listUsers
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
@ -83,7 +83,6 @@ trait UsersBase
|
|||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['users']);
|
||||
$this->assertCount(1, $response['body']['users']);
|
||||
|
||||
$this->assertEquals($response['body']['users'][0]['$id'], 'user1');
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
|
@ -100,9 +99,6 @@ trait UsersBase
|
|||
|
||||
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
@ -110,7 +106,70 @@ trait UsersBase
|
|||
'cursor' => 'unknown'
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
/**
|
||||
* Test for SUCCESS searchUsers
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'Ronaldo'
|
||||
]);
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['users']);
|
||||
$this->assertCount(1, $response['body']['users']);
|
||||
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'cristiano.ronaldo'
|
||||
]);
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['users']);
|
||||
$this->assertCount(1, $response['body']['users']);
|
||||
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'manchester'
|
||||
]);
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['users']);
|
||||
$this->assertCount(1, $response['body']['users']);
|
||||
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'manchester-united.co.uk'
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertIsArray($response['body']);
|
||||
$this->assertIsArray($response['body']['users']);
|
||||
$this->assertIsInt($response['body']['sum']);
|
||||
$this->assertEquals(1, $response['body']['sum']);
|
||||
$this->assertCount(1, $response['body']['users']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => $data['userId']
|
||||
]);
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertNotEmpty($response['body']);
|
||||
$this->assertNotEmpty($response['body']['users']);
|
||||
$this->assertCount(1, $response['body']['users']);
|
||||
$this->assertEquals($response['body']['users'][0]['$id'], $data['userId']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,8 +186,8 @@ trait UsersBase
|
|||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals($user['headers']['status-code'], 200);
|
||||
$this->assertEquals($user['body']['name'], 'Project User');
|
||||
$this->assertEquals($user['body']['email'], 'users.service@example.com');
|
||||
$this->assertEquals($user['body']['name'], 'Cristiano Ronaldo');
|
||||
$this->assertEquals($user['body']['email'], 'cristiano.ronaldo@manchester-united.co.uk');
|
||||
$this->assertEquals($user['body']['status'], true);
|
||||
$this->assertGreaterThan(0, $user['body']['registration']);
|
||||
|
||||
|
@ -159,21 +218,6 @@ trait UsersBase
|
|||
$this->assertIsInt($users['body']['sum']);
|
||||
$this->assertGreaterThan(0, $users['body']['sum']);
|
||||
|
||||
$users = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'search' => 'example.com'
|
||||
]);
|
||||
|
||||
$this->assertEquals($users['headers']['status-code'], 200);
|
||||
$this->assertIsArray($users['body']);
|
||||
$this->assertIsArray($users['body']['users']);
|
||||
$this->assertIsInt($users['body']['sum']);
|
||||
$this->assertEquals(2, $users['body']['sum']);
|
||||
$this->assertGreaterThan(0, $users['body']['sum']);
|
||||
$this->assertCount(2, $users['body']['users']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Tests\E2E\Services\Users;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
|
@ -11,4 +12,49 @@ class UsersCustomServerTest extends Scope
|
|||
use UsersBase;
|
||||
use ProjectCustom;
|
||||
use SideServer;
|
||||
|
||||
public function testDeprecatedUsers():array
|
||||
{
|
||||
/**
|
||||
* Test for FAILURE (don't allow recreating account with same custom ID)
|
||||
*/
|
||||
|
||||
// Create user with custom ID 'meldiron'
|
||||
$response = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => 'meldiron',
|
||||
'email' => 'matej@appwrite.io',
|
||||
'password' => 'my-superstr0ng-password',
|
||||
'name' => 'Matej Bačo'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
// Delete user with custom ID 'meldiron'
|
||||
$response = $this->client->call(Client::METHOD_DELETE, '/users/meldiron', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
|
||||
// Try to create user with custom ID 'meldiron' again, but now it should fail
|
||||
$response1 = $this->client->call(Client::METHOD_POST, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => 'meldiron',
|
||||
'email' => 'matej2@appwrite.io',
|
||||
'password' => 'someones-superstr0ng-password',
|
||||
'name' => 'Matej Bačo Second'
|
||||
]);
|
||||
|
||||
$this->assertEquals(409, $response1['headers']['status-code']);
|
||||
$this->assertEquals('Account already exists', $response1['body']['message']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue