diff --git a/composer.json b/composer.json
index d6c0a7ff2d..e7bd6646a9 100644
--- a/composer.json
+++ b/composer.json
@@ -46,7 +46,7 @@
"utopia-php/cache": "0.4.*",
"utopia-php/cli": "0.11.*",
"utopia-php/config": "0.2.*",
- "utopia-php/database": "0.13.*",
+ "utopia-php/database": "0.14.*",
"utopia-php/locale": "0.4.*",
"utopia-php/orchestration": "0.2.*",
"utopia-php/registry": "0.5.*",
diff --git a/composer.lock b/composer.lock
index 62a9f81bd6..91440e4a7a 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "f022f43cc2d6023c3dad3805b7c4455a",
+ "content-hash": "ab493f0a7f01a1105f8bc5caaf9b928b",
"packages": [
{
"name": "adhocore/jwt",
@@ -355,16 +355,16 @@
},
{
"name": "composer/package-versions-deprecated",
- "version": "1.11.99.4",
+ "version": "1.11.99.5",
"source": {
"type": "git",
"url": "https://github.com/composer/package-versions-deprecated.git",
- "reference": "b174585d1fe49ceed21928a945138948cb394600"
+ "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600",
- "reference": "b174585d1fe49ceed21928a945138948cb394600",
+ "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d",
+ "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d",
"shasum": ""
},
"require": {
@@ -408,7 +408,7 @@
"description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
"support": {
"issues": "https://github.com/composer/package-versions-deprecated/issues",
- "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4"
+ "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5"
},
"funding": [
{
@@ -424,7 +424,7 @@
"type": "tidelift"
}
],
- "time": "2021-09-13T08:41:34+00:00"
+ "time": "2022-01-17T14:14:24+00:00"
},
{
"name": "dragonmantank/cron-expression",
@@ -2141,16 +2141,16 @@
},
{
"name": "utopia-php/database",
- "version": "0.13.2",
+ "version": "0.14.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
- "reference": "bf92279b707b3a10ee5ec5df5c065023b2221357"
+ "reference": "2f2527bb080cf578fba327ea2ec637064561d403"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/utopia-php/database/zipball/bf92279b707b3a10ee5ec5df5c065023b2221357",
- "reference": "bf92279b707b3a10ee5ec5df5c065023b2221357",
+ "url": "https://api.github.com/repos/utopia-php/database/zipball/2f2527bb080cf578fba327ea2ec637064561d403",
+ "reference": "2f2527bb080cf578fba327ea2ec637064561d403",
"shasum": ""
},
"require": {
@@ -2198,9 +2198,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
- "source": "https://github.com/utopia-php/database/tree/0.13.2"
+ "source": "https://github.com/utopia-php/database/tree/0.14.0"
},
- "time": "2022-01-04T10:51:22+00:00"
+ "time": "2022-01-21T16:34:34+00:00"
},
{
"name": "utopia-php/domains",
@@ -3703,9 +3703,6 @@
"require": {
"php": "^7.1 || ^8.0"
},
- "replace": {
- "myclabs/deep-copy": "self.version"
- },
"require-dev": {
"doctrine/collections": "^1.0",
"doctrine/common": "^2.6",
@@ -6669,5 +6666,5 @@
"platform-overrides": {
"php": "8.0"
},
- "plugin-api-version": "2.1.0"
+ "plugin-api-version": "2.2.0"
}
diff --git a/docs/references/account/delete-session.md b/docs/references/account/delete-session.md
index e0ca6d29ac..cd1f22f627 100644
--- a/docs/references/account/delete-session.md
+++ b/docs/references/account/delete-session.md
@@ -1 +1 @@
-Use this endpoint to log out the currently logged in user from all their account sessions across all of their different devices. When using the option id argument, only the session unique ID provider will be deleted.
\ No newline at end of file
+Use this endpoint to log out the currently logged in user from all their account sessions across all of their different devices. When using the Session ID argument, only the unique session ID provided is deleted.
diff --git a/package-lock.json b/package-lock.json
index 4d6acc54bc..8f1340f9b7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,8 +9,8 @@
"version": "0.1.0",
"license": "BSD-3-Clause",
"dependencies": {
- "chart.js": "^3.6.2",
- "markdown-it": "^12.3.0",
+ "chart.js": "^3.7.0",
+ "markdown-it": "^12.3.2",
"pell": "^1.0.6",
"prismjs": "^1.25.0",
"turndown": "^7.1.1"
@@ -549,9 +549,9 @@
}
},
"node_modules/chart.js": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.6.2.tgz",
- "integrity": "sha512-Xz7f/fgtVltfQYWq0zL1Xbv7N2inpG+B54p3D5FSvpCdy3sM+oZhbqa42eNuYXltaVvajgX5UpKCU2GeeJIgxg=="
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.0.tgz",
+ "integrity": "sha512-31gVuqqKp3lDIFmzpKIrBeum4OpZsQjSIAqlOpgjosHDJZlULtvwLEZKtEhIAZc7JMPaHlYMys40Qy9Mf+1AAg=="
},
"node_modules/chokidar": {
"version": "2.1.8",
@@ -2862,9 +2862,9 @@
}
},
"node_modules/markdown-it": {
- "version": "12.3.0",
- "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.0.tgz",
- "integrity": "sha512-T345UZZ6ejQWTjG6PSEHplzNy5m4kF6zvUpHVDv8Snl/pEU0OxIK0jGg8YLVNwJvT8E0YJC7/2UvssJDk/wQCQ==",
+ "version": "12.3.2",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz",
+ "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==",
"dependencies": {
"argparse": "^2.0.1",
"entities": "~2.1.0",
@@ -5484,9 +5484,9 @@
"dev": true
},
"chart.js": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.6.2.tgz",
- "integrity": "sha512-Xz7f/fgtVltfQYWq0zL1Xbv7N2inpG+B54p3D5FSvpCdy3sM+oZhbqa42eNuYXltaVvajgX5UpKCU2GeeJIgxg=="
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.0.tgz",
+ "integrity": "sha512-31gVuqqKp3lDIFmzpKIrBeum4OpZsQjSIAqlOpgjosHDJZlULtvwLEZKtEhIAZc7JMPaHlYMys40Qy9Mf+1AAg=="
},
"chokidar": {
"version": "2.1.8",
@@ -7413,9 +7413,9 @@
}
},
"markdown-it": {
- "version": "12.3.0",
- "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.0.tgz",
- "integrity": "sha512-T345UZZ6ejQWTjG6PSEHplzNy5m4kF6zvUpHVDv8Snl/pEU0OxIK0jGg8YLVNwJvT8E0YJC7/2UvssJDk/wQCQ==",
+ "version": "12.3.2",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz",
+ "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==",
"requires": {
"argparse": "^2.0.1",
"entities": "~2.1.0",
diff --git a/package.json b/package.json
index 9b0ba21e17..2a95fbed23 100644
--- a/package.json
+++ b/package.json
@@ -17,8 +17,8 @@
"gulp-less": "^5.0.0"
},
"dependencies": {
- "chart.js": "^3.6.2",
- "markdown-it": "^12.3.0",
+ "chart.js": "^3.7.0",
+ "markdown-it": "^12.3.2",
"pell": "^1.0.6",
"prismjs": "^1.25.0",
"turndown": "^7.1.1"
diff --git a/src/Appwrite/Resque/Worker.php b/src/Appwrite/Resque/Worker.php
index 1444c90663..b9ed5af275 100644
--- a/src/Appwrite/Resque/Worker.php
+++ b/src/Appwrite/Resque/Worker.php
@@ -70,9 +70,6 @@ abstract class Worker
throw new Exception("Please implement getName method in worker");
}
- const MAX_ATTEMPTS = 10;
- const SLEEP_TIME = 2;
-
const DATABASE_PROJECT = 'project';
const DATABASE_CONSOLE = 'console';
@@ -174,7 +171,7 @@ abstract class Worker
global $register;
$namespace = '';
- $sleep = self::SLEEP_TIME; // overwritten when necessary
+ $sleep = DATABASE_RECONNECT_SLEEP; // overwritten when necessary
switch ($type) {
case self::DATABASE_PROJECT:
@@ -201,18 +198,24 @@ abstract class Worker
$database = new Database(new MariaDB($register->get('db')), $cache);
$database->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$database->setNamespace($namespace); // Main DB
+
if (!empty($projectId) && !$database->getDocument('projects', $projectId)->isEmpty()) {
throw new \Exception("Project does not exist: {$projectId}");
}
+
+ if ($type === self::DATABASE_CONSOLE && !$database->exists($database->getDefaultDatabase(), 'realtime')) {
+ throw new \Exception('Console project not ready');
+ }
+
break; // leave loop if successful
} catch(\Exception $e) {
Console::warning("Database not ready. Retrying connection ({$attempts})...");
- if ($attempts >= self::MAX_ATTEMPTS) {
+ if ($attempts >= DATABASE_RECONNECT_MAX_ATTEMPTS) {
throw new \Exception('Failed to connect to database: '. $e->getMessage());
}
sleep($sleep);
}
- } while ($attempts < self::MAX_ATTEMPTS);
+ } while ($attempts < DATABASE_RECONNECT_MAX_ATTEMPTS);
return $database;
}
diff --git a/tests/e2e/Scopes/Scope.php b/tests/e2e/Scopes/Scope.php
index b5b5e0e775..079777be4c 100644
--- a/tests/e2e/Scopes/Scope.php
+++ b/tests/e2e/Scopes/Scope.php
@@ -33,7 +33,7 @@ abstract class Scope extends TestCase
protected function getLastEmail():array
{
- sleep(5);
+ sleep(3);
$emails = json_decode(file_get_contents('http://maildev:1080/email'), true);
@@ -46,7 +46,7 @@ abstract class Scope extends TestCase
protected function getLastRequest():array
{
- sleep(5);
+ sleep(2);
$resquest = json_decode(file_get_contents('http://request-catcher:5000/__last_request__'), true);
$resquest['data'] = json_decode($resquest['data'], true);
@@ -167,4 +167,4 @@ abstract class Scope extends TestCase
return self::$user[$this->getProject()['$id']];
}
-}
\ No newline at end of file
+}
diff --git a/tests/e2e/Services/Database/DatabaseCustomServerTest.php b/tests/e2e/Services/Database/DatabaseCustomServerTest.php
index 659839debe..18542961db 100644
--- a/tests/e2e/Services/Database/DatabaseCustomServerTest.php
+++ b/tests/e2e/Services/Database/DatabaseCustomServerTest.php
@@ -125,6 +125,39 @@ class DatabaseCustomServerTest extends Scope
$this->assertCount(0, $collections['body']['collections']);
$this->assertEmpty($collections['body']['collections']);
+ /**
+ * Test for Search
+ */
+ $collections = $this->client->call(Client::METHOD_GET, '/database/collections', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'search' => 'first'
+ ]);
+
+ $this->assertEquals(1, $collections['body']['sum']);
+ $this->assertEquals('first', $collections['body']['collections'][0]['$id']);
+
+ $collections = $this->client->call(Client::METHOD_GET, '/database/collections', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'search' => 'Test'
+ ]);
+
+ $this->assertEquals(2, $collections['body']['sum']);
+ $this->assertEquals('Test 1', $collections['body']['collections'][0]['name']);
+ $this->assertEquals('Test 2', $collections['body']['collections'][1]['name']);
+
+ $collections = $this->client->call(Client::METHOD_GET, '/database/collections', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'search' => 'Nonexistent'
+ ]);
+
+ $this->assertEquals(0, $collections['body']['sum']);
+
/**
* Test for FAILURE
*/
@@ -136,6 +169,21 @@ class DatabaseCustomServerTest extends Scope
]);
$this->assertEquals($response['headers']['status-code'], 400);
+
+ // This collection already exists
+ $response = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey']
+ ]), [
+ 'name' => 'Test 1',
+ 'collectionId' => 'first',
+ 'read' => ['role:all'],
+ 'write' => ['role:all'],
+ 'permission' => 'document'
+ ]);
+
+ $this->assertEquals($response['headers']['status-code'], 409);
}
public function testDeleteAttribute(): array
diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
index 264fae0735..8a55a0ded4 100644
--- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
+++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
@@ -218,6 +218,32 @@ class FunctionsCustomClientTest extends Scope
];
}
+ public function testCreateExecutionUnauthorized():array
+ {
+ $function = $this->client->call(Client::METHOD_POST, '/functions', [
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey'],
+ ], [
+ 'functionId' => 'unique()',
+ 'name' => 'Test',
+ 'execute' => [],
+ 'runtime' => 'php-8.0',
+ 'timeout' => 10,
+ ]);
+
+ $execution = $this->client->call(Client::METHOD_POST, '/functions/'.$function['body']['$id'].'/executions', [
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], [
+ 'async' => 1,
+ ]);
+
+ $this->assertEquals(401, $execution['headers']['status-code']);
+
+ return [];
+ }
+
/**
* @depends testCreateCustomExecution
*/
diff --git a/tests/e2e/Services/Realtime/RealtimeBase.php b/tests/e2e/Services/Realtime/RealtimeBase.php
index 277537d407..4357bec383 100644
--- a/tests/e2e/Services/Realtime/RealtimeBase.php
+++ b/tests/e2e/Services/Realtime/RealtimeBase.php
@@ -66,6 +66,7 @@ trait RealtimeBase
$this->assertEquals('error', $payload['type']);
$this->assertEquals(1008, $payload['data']['code']);
$this->assertEquals('Missing or unknown project ID', $payload['data']['message']);
+ \usleep(250000); // 250ms
$this->expectException(ConnectionException::class); // Check if server disconnnected client
$client->close();
}
diff --git a/tests/e2e/Services/Teams/TeamsBaseClient.php b/tests/e2e/Services/Teams/TeamsBaseClient.php
index 89ac02da74..e03ce89a9c 100644
--- a/tests/e2e/Services/Teams/TeamsBaseClient.php
+++ b/tests/e2e/Services/Teams/TeamsBaseClient.php
@@ -391,11 +391,75 @@ trait TeamsBaseClient
{
$teamUid = $data['teamUid'] ?? '';
$membershipUid = $data['membershipUid'] ?? '';
+ $session = $data['session'] ?? '';
+
+ $response = $this->client->call(Client::METHOD_GET, '/teams/'.$teamUid.'/memberships', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()));
+
+ $this->assertEquals(200, $response['headers']['status-code']);
+ $this->assertEquals(2, $response['body']['sum']);
+ $ownerMembershipUid = $response['body']['memberships'][0]['$id'];
+
+ /**
+ * Test for FAILURE
+ */
+
+ /**
+ * Test deleting a membership that does not exists
+ */
+ $response = $this->client->call(Client::METHOD_DELETE, '/teams/'.$teamUid.'/memberships/dne', [
+ 'origin' => 'http://localhost',
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'cookie' => 'a_session_'.$this->getProject()['$id'].'='.$session,
+ ]);
+
+ $this->assertEquals(404, $response['headers']['status-code']);
+
+ /**
+ * Test deleting another user's membership
+ */
+ $response = $this->client->call(Client::METHOD_DELETE, '/teams/'.$teamUid.'/memberships/'.$ownerMembershipUid, [
+ 'origin' => 'http://localhost',
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'cookie' => 'a_session_'.$this->getProject()['$id'].'='.$session,
+ ]);
+
+ $this->assertEquals(401, $response['headers']['status-code']);
+
/**
* Test for SUCCESS
*/
- $response = $this->client->call(Client::METHOD_DELETE, '/teams/'.$teamUid.'/memberships/'.$membershipUid, array_merge([
+
+ /**
+ * Test for when a user other than the owner tries to delete their membership
+ */
+ $response = $this->client->call(Client::METHOD_DELETE, '/teams/'.$teamUid.'/memberships/'.$membershipUid, [
+ 'origin' => 'http://localhost',
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'cookie' => 'a_session_'.$this->getProject()['$id'].'='.$session,
+ ]);
+
+ $this->assertEquals(204, $response['headers']['status-code']);
+ $this->assertEmpty($response['body']);
+
+ $response = $this->client->call(Client::METHOD_GET, '/teams/'.$teamUid.'/memberships', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()));
+
+ $this->assertEquals(200, $response['headers']['status-code']);
+ $this->assertEquals(1, $response['body']['sum']);
+
+ /**
+ * Test for when the owner tries to delete their membership
+ */
+ $response = $this->client->call(Client::METHOD_DELETE, '/teams/'.$teamUid.'/memberships/'.$ownerMembershipUid, array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@@ -404,10 +468,7 @@ trait TeamsBaseClient
$this->assertEquals(204, $response['headers']['status-code']);
$this->assertEmpty($response['body']);
- /**
- * Test for FAILURE
- */
- $response = $this->client->call(Client::METHOD_GET, '/teams/'.$teamUid.'/memberships/'.$membershipUid, array_merge([
+ $response = $this->client->call(Client::METHOD_GET, '/teams/'.$teamUid.'/memberships/'.$ownerMembershipUid, array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],