From e1cc6bc6249195f96a1f8f1092d650b2a8eee91c Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Mon, 15 Aug 2022 16:09:09 +0100 Subject: [PATCH 01/30] Start work on response filters for 0.16 --- app/controllers/general.php | 4 + src/Appwrite/Utopia/Response/Filters/V15.php | 125 +++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 src/Appwrite/Utopia/Response/Filters/V15.php diff --git a/app/controllers/general.php b/app/controllers/general.php index 9c2c5d3b81..c3ecfb8737 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -20,6 +20,7 @@ use Appwrite\Utopia\Response\Filters\V11 as ResponseV11; use Appwrite\Utopia\Response\Filters\V12 as ResponseV12; use Appwrite\Utopia\Response\Filters\V13 as ResponseV13; use Appwrite\Utopia\Response\Filters\V14 as ResponseV14; +use Appwrite\Utopia\Response\Filters\V15 as ResponseV15; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Document; @@ -190,6 +191,9 @@ App::init() case version_compare($responseFormat, '0.14.0', '<='): Response::setFilter(new ResponseV14()); break; + case version_compare($responseFormat, '0.15.0', '<='): + Response::setFilter(new ResponseV15()); + break; default: Response::setFilter(null); } diff --git a/src/Appwrite/Utopia/Response/Filters/V15.php b/src/Appwrite/Utopia/Response/Filters/V15.php new file mode 100644 index 0000000000..0a6412ab9f --- /dev/null +++ b/src/Appwrite/Utopia/Response/Filters/V15.php @@ -0,0 +1,125 @@ +handleMetricAttributes($content); + } + + return $parsedResponse; + } + + protected function handleMetricAttributes(array $content) + { + $content['timestamp'] = $content['date']; + unset($content['date']); + } + + protected function parseRemoveAttributes(array $content, array $attributes) + { + foreach ($attributes as $attribute) { + unset($content[$attribute]); + } + + return $content; + } + + protected function parseRemoveAttributesList(array $content, string $property, array $attributes) + { + $documents = $content[$property]; + $parsedResponse = []; + foreach ($documents as $document) { + $parsedResponse[] = $this->parseRemoveAttributes($document, $attributes); + } + $content[$property] = $parsedResponse; + + return $content; + } + + protected function parseCreatedAt(array $content) + { + $content['dateCreated'] = $content['$createdAt']; + unset($content['$createdAt']); + unset($content['$updatedAt']); + + return $content; + } + + protected function parseCreatedAtList(array $content, string $property) + { + $documents = $content[$property]; + $parsedResponse = []; + foreach ($documents as $document) { + $parsedResponse[] = $this->parseCreatedAt($document); + } + $content[$property] = $parsedResponse; + + return $content; + } + + protected function parseCreatedAtAndUpdatedAt(array $content) + { + $content['dateCreated'] = $content['$createdAt']; + $content['dateUpdated'] = $content['$updatedAt']; + unset($content['$createdAt']); + unset($content['$updatedAt']); + + return $content; + } + + protected function parseCreatedAtAndUpdatedAtList(array $content, string $property) + { + $documents = $content[$property]; + $parsedResponse = []; + foreach ($documents as $document) { + $parsedResponse[] = $this->parseCreatedAtAndUpdatedAt($document); + } + $content[$property] = $parsedResponse; + + return $content; + } +} From 9f3ebe995648d5f2398a11529e81cd782132309c Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Wed, 17 Aug 2022 10:33:35 +0100 Subject: [PATCH 02/30] Continue working on permission backporting --- src/Appwrite/Utopia/Response/Filters/V15.php | 97 ++++++++++---------- 1 file changed, 46 insertions(+), 51 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Filters/V15.php b/src/Appwrite/Utopia/Response/Filters/V15.php index 0a6412ab9f..3bf5f9c567 100644 --- a/src/Appwrite/Utopia/Response/Filters/V15.php +++ b/src/Appwrite/Utopia/Response/Filters/V15.php @@ -59,67 +59,62 @@ class V15 extends Filter unset($content['date']); } - protected function parseRemoveAttributes(array $content, array $attributes) + protected function downgradePermissionSelector(string $permSelector) { - foreach ($attributes as $attribute) { - unset($content[$attribute]); + switch ($permSelector) + { + case 'any': + return 'role:all'; + case 'users': + return 'role:user'; + case 'guests': + return 'role:guest'; } - return $content; + return $permSelector; } - protected function parseRemoveAttributesList(array $content, string $property, array $attributes) + protected function downgradePermissions(array $permissions) { - $documents = $content[$property]; - $parsedResponse = []; - foreach ($documents as $document) { - $parsedResponse[] = $this->parseRemoveAttributes($document, $attributes); + $result = [ + 'read' => [], + 'write' => [] + ]; + + $splitPermissions = []; + + // split up the permisisons + foreach ($permissions as $permission) { + $permission_type = explode('(', $permission)[0]; + $permission_value = explode(')', explode('(', $permission)[1])[0]; + $splitPermissions[$permission_type][] = $permission_value; } - $content[$property] = $parsedResponse; - return $content; - } + // downgrade the permissions + foreach ($permissions as $permission) { + // permission = "read('any')" = ["read" => "role:all"] + $permission_type = explode('(', $permission)[0]; + $permission_value = explode(')', explode('(', $permission)[1])[0]; - protected function parseCreatedAt(array $content) - { - $content['dateCreated'] = $content['$createdAt']; - unset($content['$createdAt']); - unset($content['$updatedAt']); - - return $content; - } - - protected function parseCreatedAtList(array $content, string $property) - { - $documents = $content[$property]; - $parsedResponse = []; - foreach ($documents as $document) { - $parsedResponse[] = $this->parseCreatedAt($document); + // Old type permissions meant that 'write' is equivalent to 'create', 'update' and 'delete' + switch ($permission_type) + { + case 'update': + case 'delete': + case 'write': + case 'create': + if (!in_array(downgradePermissionSelector($permission_value), $result['write'])) { + $result['write'][] = downgradePermissionSelector($permission_value); + } + break; + case 'read': + if (!in_array(downgradePermissionSelector($permission_value), $result['read'])) { + $result['read'][] = downgradePermissionSelector($permission_value); + } + break; + } } - $content[$property] = $parsedResponse; - return $content; - } - - protected function parseCreatedAtAndUpdatedAt(array $content) - { - $content['dateCreated'] = $content['$createdAt']; - $content['dateUpdated'] = $content['$updatedAt']; - unset($content['$createdAt']); - unset($content['$updatedAt']); - - return $content; - } - - protected function parseCreatedAtAndUpdatedAtList(array $content, string $property) - { - $documents = $content[$property]; - $parsedResponse = []; - foreach ($documents as $document) { - $parsedResponse[] = $this->parseCreatedAtAndUpdatedAt($document); - } - $content[$property] = $parsedResponse; - - return $content; + return $result; } } From 80ff661aebbdbc1bb9233174e414d38a04f78f5f Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Wed, 17 Aug 2022 14:08:09 +0100 Subject: [PATCH 03/30] Continue working on response filters --- app/controllers/general.php | 4 + src/Appwrite/Utopia/Request/Filters/V15.php | 18 +++ src/Appwrite/Utopia/Response/Filters/V15.php | 113 ++++++++++++------- 3 files changed, 96 insertions(+), 39 deletions(-) create mode 100644 src/Appwrite/Utopia/Request/Filters/V15.php diff --git a/app/controllers/general.php b/app/controllers/general.php index c3ecfb8737..a59aee0e63 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -30,6 +30,7 @@ use Utopia\Validator\Hostname; use Appwrite\Utopia\Request\Filters\V12 as RequestV12; use Appwrite\Utopia\Request\Filters\V13 as RequestV13; use Appwrite\Utopia\Request\Filters\V14 as RequestV14; +use Appwrite\Utopia\Request\Filters\V15 as RequestV15; use Utopia\Validator\Text; Config::setParam('domainVerification', false); @@ -65,6 +66,9 @@ App::init() case version_compare($requestFormat, '0.14.0', '<'): Request::setFilter(new RequestV14()); break; + case version_compare($requestFormat, '0.15.0', '<'): + Request::setFilter(new RequestV15()); + break; default: Request::setFilter(null); } diff --git a/src/Appwrite/Utopia/Request/Filters/V15.php b/src/Appwrite/Utopia/Request/Filters/V15.php new file mode 100644 index 0000000000..8e93d91e2a --- /dev/null +++ b/src/Appwrite/Utopia/Request/Filters/V15.php @@ -0,0 +1,18 @@ +handleDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'registration', 'passwordUpdate']); + $parsedResponse = $this->handleUser($parsedResponse); + break; case Response::MODEL_METRIC: $parsedResponse = $this->handleMetricAttributes($content); + break; + case Response::MODEL_BUILD: + $parsedResponse = $this->handleDatetimeAttributes($content, ['startTime', 'endTime']); + break; + case Response::MODEL_BUCKET: + case Response::MODEL_COLLECTION: + case Response::MODEL_DEPLOYMENT: + case Response::MODEL_DOCUMENT: + case Response::MODEL_EXECUTION: + case Response::MODEL_PLATFORM: + case Response::MODEL_PROJECT: + case Response::MODEL_TEAM: + case Response::MODEL_FILE: + case Response::MODEL_WEBHOOK: + $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt']); + break; + case Response::MODEL_FUNCTION: + $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'scheduleNext', 'schedulePrevious']); + break; + case Response::MODEL_KEY: + $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'expire']); + break; + case Response::MODEL_LOG: + $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'time']); + break; + case Response::MODEL_MEMBERSHIP: + $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'invited', 'joined']); + break; + case Response::MODEL_SESSION: + $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', 'expire', 'providerAccessTokenExpiry']); + break; + case Response::MODEL_TOKEN: + $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', 'expire']); + break; } + // Downgrade Permissions for all models + $parsedResponse = $this->handleDowngradePermissions($parsedResponse); + return $parsedResponse; } + protected function handleDatetimeAttributes(array $content, array $attributes): array + { + foreach ($attributes as $attribute) { + if (isset($content[$attribute])) { + $content[$attribute] = strtotime($content[$attribute]); + } + } + return $content; + } + + protected function handleUser(array $content): array + { + unset($content['password']); + return $content; + } + protected function handleMetricAttributes(array $content) { $content['timestamp'] = $content['date']; unset($content['date']); } + protected function handleDowngradePermissions(array $content) + { + if (!isset($content['$permissions'])) { + return $content; + } + $content = array_merge($content, $this->downgradePermissions($content['permissions'])); + unset($content['permissions']); + return $content; + } + protected function downgradePermissionSelector(string $permSelector) { switch ($permSelector) @@ -77,8 +111,8 @@ class V15 extends Filter protected function downgradePermissions(array $permissions) { $result = [ - 'read' => [], - 'write' => [] + '$read' => [], + '$write' => [] ]; $splitPermissions = []; @@ -92,7 +126,8 @@ class V15 extends Filter // downgrade the permissions foreach ($permissions as $permission) { - // permission = "read('any')" = ["read" => "role:all"] + //TODO: Replace with Permission class + // permission = "read('any')" = ["$read" => "role:all"] $permission_type = explode('(', $permission)[0]; $permission_value = explode(')', explode('(', $permission)[1])[0]; @@ -103,13 +138,13 @@ class V15 extends Filter case 'delete': case 'write': case 'create': - if (!in_array(downgradePermissionSelector($permission_value), $result['write'])) { - $result['write'][] = downgradePermissionSelector($permission_value); + if (!in_array($this->downgradePermissionSelector($permission_value), $result['write'])) { + $result['$write'][] = $this->downgradePermissionSelector($permission_value); } break; case 'read': - if (!in_array(downgradePermissionSelector($permission_value), $result['read'])) { - $result['read'][] = downgradePermissionSelector($permission_value); + if (!in_array($this->downgradePermissionSelector($permission_value), $result['read'])) { + $result['$read'][] = $this->downgradePermissionSelector($permission_value); } break; } From 5294f86595dbc368b5fea75f62e6f4068e3b17d1 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Mon, 5 Sep 2022 13:43:36 +0100 Subject: [PATCH 04/30] Run Linter --- src/Appwrite/Utopia/Request/Filters/V15.php | 3 +-- src/Appwrite/Utopia/Response/Filters/V15.php | 10 ++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Appwrite/Utopia/Request/Filters/V15.php b/src/Appwrite/Utopia/Request/Filters/V15.php index 8e93d91e2a..300750f95e 100644 --- a/src/Appwrite/Utopia/Request/Filters/V15.php +++ b/src/Appwrite/Utopia/Request/Filters/V15.php @@ -10,9 +10,8 @@ class V15 extends Filter public function parse(array $content, string $model): array { switch ($model) { - } return $content; } -} \ No newline at end of file +} diff --git a/src/Appwrite/Utopia/Response/Filters/V15.php b/src/Appwrite/Utopia/Response/Filters/V15.php index e168e9954a..1ba37ebd20 100644 --- a/src/Appwrite/Utopia/Response/Filters/V15.php +++ b/src/Appwrite/Utopia/Response/Filters/V15.php @@ -77,7 +77,7 @@ class V15 extends Filter return $content; } - protected function handleMetricAttributes(array $content) + protected function handleMetricAttributes(array $content) { $content['timestamp'] = $content['date']; unset($content['date']); @@ -95,8 +95,7 @@ class V15 extends Filter protected function downgradePermissionSelector(string $permSelector) { - switch ($permSelector) - { + switch ($permSelector) { case 'any': return 'role:all'; case 'users': @@ -132,8 +131,7 @@ class V15 extends Filter $permission_value = explode(')', explode('(', $permission)[1])[0]; // Old type permissions meant that 'write' is equivalent to 'create', 'update' and 'delete' - switch ($permission_type) - { + switch ($permission_type) { case 'update': case 'delete': case 'write': @@ -146,7 +144,7 @@ class V15 extends Filter if (!in_array($this->downgradePermissionSelector($permission_value), $result['read'])) { $result['$read'][] = $this->downgradePermissionSelector($permission_value); } - break; + break; } } From a04a968def96673143a55092ccdd30f513fcb9cd Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Mon, 5 Sep 2022 16:48:10 +0100 Subject: [PATCH 05/30] Update permission to use permission class --- src/Appwrite/Utopia/Response/Filters/V15.php | 29 ++++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Filters/V15.php b/src/Appwrite/Utopia/Response/Filters/V15.php index 1ba37ebd20..19000c9567 100644 --- a/src/Appwrite/Utopia/Response/Filters/V15.php +++ b/src/Appwrite/Utopia/Response/Filters/V15.php @@ -4,6 +4,7 @@ namespace Appwrite\Utopia\Response\Filters; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Filter; +use Utopia\Database\Permission; class V15 extends Filter { @@ -33,6 +34,7 @@ class V15 extends Filter case Response::MODEL_TEAM: case Response::MODEL_FILE: case Response::MODEL_WEBHOOK: + case Response::MODEL_DOMAIN: $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt']); break; case Response::MODEL_FUNCTION: @@ -107,31 +109,27 @@ class V15 extends Filter return $permSelector; } - protected function downgradePermissions(array $permissions) + protected function downgradePermissions(array $model) { + if (!isset($model['$permissions'])) { + return $model; + } + + $permissions = $model['$permissions']; + $result = [ '$read' => [], '$write' => [] ]; - $splitPermissions = []; - - // split up the permisisons - foreach ($permissions as $permission) { - $permission_type = explode('(', $permission)[0]; - $permission_value = explode(')', explode('(', $permission)[1])[0]; - $splitPermissions[$permission_type][] = $permission_value; - } - // downgrade the permissions foreach ($permissions as $permission) { - //TODO: Replace with Permission class + $permission = Permission::parse($permission); // permission = "read('any')" = ["$read" => "role:all"] - $permission_type = explode('(', $permission)[0]; - $permission_value = explode(')', explode('(', $permission)[1])[0]; // Old type permissions meant that 'write' is equivalent to 'create', 'update' and 'delete' - switch ($permission_type) { + + switch ($permission->getPermission()) { case 'update': case 'delete': case 'write': @@ -148,6 +146,7 @@ class V15 extends Filter } } - return $result; + unset($model['$permissions']); + return array_merge($model, $result); } } From eb0b33bfcd0041b3869dbd12da1f33cac2f8ab95 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 7 Sep 2022 02:12:04 +0000 Subject: [PATCH 06/30] Fix code examples in SDK getting started --- docs/sdks/android/GETTING_STARTED.md | 11 ++++++----- docs/sdks/dart/GETTING_STARTED.md | 8 ++++---- docs/sdks/deno/GETTING_STARTED.md | 10 +++++----- docs/sdks/dotnet/GETTING_STARTED.md | 10 ++++------ docs/sdks/flutter/GETTING_STARTED.md | 16 ++++++++-------- docs/sdks/kotlin/GETTING_STARTED.md | 18 ++++++++---------- docs/sdks/nodejs/GETTING_STARTED.md | 6 +++--- docs/sdks/php/GETTING_STARTED.md | 7 ++++--- docs/sdks/python/GETTING_STARTED.md | 5 +++-- docs/sdks/ruby/GETTING_STARTED.md | 6 +++--- docs/sdks/web/GETTING_STARTED.md | 4 ++-- 11 files changed, 50 insertions(+), 51 deletions(-) diff --git a/docs/sdks/android/GETTING_STARTED.md b/docs/sdks/android/GETTING_STARTED.md index 0c00bf6019..4549d7204b 100644 --- a/docs/sdks/android/GETTING_STARTED.md +++ b/docs/sdks/android/GETTING_STARTED.md @@ -51,7 +51,7 @@ When trying to connect to Appwrite from an emulator or a mobile device, localhos // Register User val account = Account(client) val response = account.create( - "[USER_ID]", + ID.unique(), "email@example.com", "password" ) @@ -62,6 +62,7 @@ val response = account.create( ```kotlin import io.appwrite.Client import io.appwrite.services.Account +import io.appwrite.ID val client = Client(context) .setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint @@ -69,8 +70,8 @@ val client = Client(context) .setSelfSigned(true) // Remove in production val account = Account(client) -val response = account.create( - "[USER_ID]", +val user = account.create( + ID.unique(), "email@example.com", "password" ) @@ -81,8 +82,8 @@ The Appwrite Android SDK raises an `AppwriteException` object with `message`, `c ```kotlin try { - var response = account.create("[USER_ID]", "email@example.com", "password") - Log.d("Appwrite response", response.body?.string()) + var user = account.create(ID.unique(), "email@example.com", "password") + Log.d("Appwrite user", account.toMap()) } catch(e : AppwriteException) { Log.e("AppwriteException",e.message.toString()) } diff --git a/docs/sdks/dart/GETTING_STARTED.md b/docs/sdks/dart/GETTING_STARTED.md index e6c4ecf677..559c0894a3 100644 --- a/docs/sdks/dart/GETTING_STARTED.md +++ b/docs/sdks/dart/GETTING_STARTED.md @@ -16,8 +16,8 @@ void main() async { Users users = Users(client); try { - final response = await users.create(userId: '[USER_ID]', email: ‘email@example.com’,password: ‘password’, name: ‘name’); - print(response.data); + final user = await users.create(userId: ID.unique(), email: ‘email@example.com’,password: ‘password’, name: ‘name’); + print(user.toMap()); } on AppwriteException catch(e) { print(e.message); } @@ -31,8 +31,8 @@ The Appwrite Dart SDK raises `AppwriteException` object with `message`, `code` a Users users = Users(client); try { - final response = await users.create(userId: '[USER_ID]', email: ‘email@example.com’,password: ‘password’, name: ‘name’); - print(response.data); + final user = await users.create(userId: ID.unique(), email: ‘email@example.com’,password: ‘password’, name: ‘name’); + print(user.toMap()); } on AppwriteException catch(e) { //show message to user or do other operation based on error as required print(e.message); diff --git a/docs/sdks/deno/GETTING_STARTED.md b/docs/sdks/deno/GETTING_STARTED.md index 08c9eb461e..6546719a61 100644 --- a/docs/sdks/deno/GETTING_STARTED.md +++ b/docs/sdks/deno/GETTING_STARTED.md @@ -21,8 +21,8 @@ Once your SDK object is set, create any of the Appwrite service objects and choo ```typescript let users = new sdk.Users(client); -let response = await users.create('[USER_ID]', 'email@example.com', 'password'); -console.log(response); +let user = await users.create(ID.unique(), 'email@example.com', 'password'); +console.log(user); ``` ### Full Example @@ -39,8 +39,8 @@ client .setSelfSigned() // Use only on dev mode with a self-signed SSL cert ; -let response = await users.create('[USER_ID]', 'email@example.com', 'password'); -console.log(response); +let user = await users.create(ID.unique(), 'email@example.com', 'password'); +console.log(user); ``` ### Error Handling @@ -50,7 +50,7 @@ The Appwrite Deno SDK raises `AppwriteException` object with `message`, `code` a let users = new sdk.Users(client); try { - let response = await users.create('[USER_ID]', 'email@example.com', 'password'); + let user = await users.create(ID.unique(), 'email@example.com', 'password'); } catch(e) { console.log(e.message); } diff --git a/docs/sdks/dotnet/GETTING_STARTED.md b/docs/sdks/dotnet/GETTING_STARTED.md index cffbce2078..fe8e81499a 100644 --- a/docs/sdks/dotnet/GETTING_STARTED.md +++ b/docs/sdks/dotnet/GETTING_STARTED.md @@ -20,9 +20,8 @@ static async Task Main(string[] args) var users = Users(client); try { - var request = await users.create('[USER_ID]', 'email@example.com', 'password', 'name'); - var response = await request.Content.ReadAsStringAsync(); - Console.WriteLine(response); + var user = await users.create(ID.unique(), 'email@example.com', 'password', 'name'); + Console.WriteLine(user.ToMap()); } catch (AppwriteException e) { Console.WriteLine(e.Message); } @@ -36,9 +35,8 @@ The Appwrite .NET SDK raises `AppwriteException` object with `message`, `code` a var users = Users(client); try { - var request = await users.create('[USER_ID]', 'email@example.com', 'password', 'name'); - var response = await request.Content.ReadAsStringAsync(); - Console.WriteLine(response); + var user = await users.create(ID.unique(), 'email@example.com', 'password', 'name'); + Console.WriteLine(user.ToMap()); } catch (AppwriteException e) { Console.WriteLine(e.Message); } diff --git a/docs/sdks/flutter/GETTING_STARTED.md b/docs/sdks/flutter/GETTING_STARTED.md index a9c121d2b9..78a334e9f7 100644 --- a/docs/sdks/flutter/GETTING_STARTED.md +++ b/docs/sdks/flutter/GETTING_STARTED.md @@ -101,9 +101,9 @@ When trying to connect to Appwrite from an emulator or a mobile device, localhos ```dart // Register User Account account = Account(client); -Response user = await account +final user = await account .create( - userId: '[USER_ID]', + userId: ID.unique(), email: 'me@appwrite.io', password: 'password', name: 'My Name' @@ -129,9 +129,9 @@ void main() { // Register User Account account = Account(client); - Response user = await account + final user = await account .create( - userId: '[USER_ID]', + userId: ID.unique(), email: 'me@appwrite.io', password: 'password', name: 'My Name' @@ -140,14 +140,14 @@ void main() { ``` ### Error Handling -The Appwrite Flutter SDK raises `AppwriteException` object with `message`, `code` and `response` properties. You can handle any errors by catching `AppwriteException` and present the `message` to the user or handle it yourself based on the provided error information. Below is an example. +The Appwrite Flutter SDK raises `AppwriteException` object with `message`, `type`, `code` and `response` properties. You can handle any errors by catching `AppwriteException` and present the `message` to the user or handle it yourself based on the provided error information. Below is an example. ```dart -Users users = Users(client); +Account account = Account(client); try { - final response = await users.create(userId: '[USER_ID]', email: ‘email@example.com’,password: ‘password’, name: ‘name’); - print(response.data); + final user = await account.create(userId: ID.unique(), email: ‘email@example.com’,password: ‘password’, name: ‘name’); + print(user.toMap()); } on AppwriteException catch(e) { //show message to user or do other operation based on error as required print(e.message); diff --git a/docs/sdks/kotlin/GETTING_STARTED.md b/docs/sdks/kotlin/GETTING_STARTED.md index be4da1a090..a419d36cb1 100644 --- a/docs/sdks/kotlin/GETTING_STARTED.md +++ b/docs/sdks/kotlin/GETTING_STARTED.md @@ -23,12 +23,11 @@ Once your SDK object is set, create any of the Appwrite service objects and choo ```kotlin val users = Users(client) -val response = users.create( - user = "[USER_ID]", +val user = users.create( + user = ID.unique(), email = "email@example.com", password = "password", ) -val json = response.body?.string() ``` ### Full Example @@ -36,6 +35,7 @@ val json = response.body?.string() ```kotlin import io.appwrite.Client import io.appwrite.services.Users +import io.appwrite.ID suspend fun main() { val client = Client(context) @@ -45,12 +45,11 @@ suspend fun main() { .setSelfSigned(true) // Use only on dev mode with a self-signed SSL cert val users = Users(client) - val response = users.create( - user = "[USER_ID]", + val user = users.create( + user = ID.unique(), email = "email@example.com", password = "password", ) - val json = response.body?.string() } ``` @@ -60,18 +59,17 @@ The Appwrite Kotlin SDK raises `AppwriteException` object with `message`, `code` ```kotlin import io.appwrite.Client +import io.appwrite.ID import io.appwrite.services.Users suspend fun main() { val users = Users(client) try { - val response = users.create( - user = "[USER_ID]", + val user = users.create( + user = ID.unique(), email = "email@example.com", password = "password", ) - var jsonString = response.body?.string() ?: "" - } catch (e: AppwriteException) { println(e) } diff --git a/docs/sdks/nodejs/GETTING_STARTED.md b/docs/sdks/nodejs/GETTING_STARTED.md index 7db3288479..70767e159b 100644 --- a/docs/sdks/nodejs/GETTING_STARTED.md +++ b/docs/sdks/nodejs/GETTING_STARTED.md @@ -22,7 +22,7 @@ Once your SDK object is set, create any of the Appwrite service objects and choo ```js let users = new sdk.Users(client); -let promise = users.create('[USER_ID]', 'email@example.com', 'password'); +let promise = users.create(ID.unique(), 'email@example.com', 'password'); promise.then(function (response) { console.log(response); @@ -45,7 +45,7 @@ client ; let users = new sdk.Users(client); -let promise = users.create('[USER_ID]', 'email@example.com', 'password'); +let promise = users.create(ID.unique(), 'email@example.com', 'password'); promise.then(function (response) { console.log(response); @@ -61,7 +61,7 @@ The Appwrite Node SDK raises `AppwriteException` object with `message`, `code` a let users = new sdk.Users(client); try { - let res = await users.create('[USER_ID]', 'email@example.com', 'password'); + let res = await users.create(ID.unique(), 'email@example.com', 'password'); } catch(e) { console.log(e.message); } diff --git a/docs/sdks/php/GETTING_STARTED.md b/docs/sdks/php/GETTING_STARTED.md index 0dcb7f2475..faa3dcf654 100644 --- a/docs/sdks/php/GETTING_STARTED.md +++ b/docs/sdks/php/GETTING_STARTED.md @@ -20,12 +20,13 @@ Once your SDK object is set, create any of the Appwrite service objects and choo ```php $users = new Users($client); -$result = $users->create('email@example.com', 'password'); +$user = $users->create(ID::unique(), 'email@example.com', 'password'); ``` ### Full Example ```php use Appwrite\Client; +use Appwrite\ID; use Appwrite\Services\Users; $client = new Client(); @@ -39,7 +40,7 @@ $client $users = new Users($client); -$result = $users->create('[USER_ID]', 'email@example.com', 'password'); +$user = $users->create(ID::unique(), 'email@example.com', 'password'); ``` ### Error Handling @@ -48,7 +49,7 @@ The Appwrite PHP SDK raises `AppwriteException` object with `message`, `code` an ```php $users = new Users($client); try { - $result = $users->create('[USER_ID]', 'email@example.com', 'password'); + $user = $users->create(ID::unique(), 'email@example.com', 'password'); } catch(AppwriteException $error) { echo $error->message; } diff --git a/docs/sdks/python/GETTING_STARTED.md b/docs/sdks/python/GETTING_STARTED.md index 46a3001ab9..a7772c9f5d 100644 --- a/docs/sdks/python/GETTING_STARTED.md +++ b/docs/sdks/python/GETTING_STARTED.md @@ -30,6 +30,7 @@ result = users.create('[USER_ID]', 'email@example.com', 'password') ```python from appwrite.client import Client from appwrite.services.users import Users +from appwrite.client import ID client = Client() @@ -42,7 +43,7 @@ client = Client() users = Users(client) -result = users.create('[USER_ID]', 'email@example.com', 'password') +result = users.create(ID.unique(), 'email@example.com', 'password') ``` ### Error Handling @@ -51,7 +52,7 @@ The Appwrite Python SDK raises `AppwriteException` object with `message`, `code` ```python users = Users(client) try: - result = users.create('[USER_ID]', 'email@example.com', 'password') + result = users.create(ID.unique(), 'email@example.com', 'password') except AppwriteException as e: print(e.message) ``` diff --git a/docs/sdks/ruby/GETTING_STARTED.md b/docs/sdks/ruby/GETTING_STARTED.md index ff714c103c..da10e1aebc 100644 --- a/docs/sdks/ruby/GETTING_STARTED.md +++ b/docs/sdks/ruby/GETTING_STARTED.md @@ -22,7 +22,7 @@ Once your SDK object is set, create any of the Appwrite service objects and choo ```ruby users = Appwrite::Users.new(client); -result = users.create(userId: '[USER_ID]', email: 'email@example.com', password: 'password'); +user = users.create(userId: Appwrite::ID::unique(), email: 'email@example.com', password: 'password'); ``` ### Full Example @@ -40,7 +40,7 @@ client users = Appwrite::Users.new(client); -result = users.create(userId: '[USER_ID]', email: 'email@example.com', password: 'password'); +user = users.create(userId: Appwrite::ID::unique(), email: 'email@example.com', password: 'password'); ``` ### Error Handling @@ -50,7 +50,7 @@ The Appwrite Ruby SDK raises `Appwrite::Exception` object with `message`, `code` users = Appwrite::Users.new(client); begin - result = users.create(userId: '[USER_ID]', email: 'email@example.com', password: 'password'); + user = users.create(userId: Appwrite::ID::unique(), email: 'email@example.com', password: 'password'); rescue Appwrite::Exception => error puts error.message end diff --git a/docs/sdks/web/GETTING_STARTED.md b/docs/sdks/web/GETTING_STARTED.md index a1d387d7d4..2c09704af2 100644 --- a/docs/sdks/web/GETTING_STARTED.md +++ b/docs/sdks/web/GETTING_STARTED.md @@ -25,7 +25,7 @@ Once your SDK object is set, access any of the Appwrite services and choose any const account = new Account(client); // Register User -account.create('[USER_ID]', 'me@example.com', 'password', 'Jane Doe') +account.create(ID.unique(), 'me@example.com', 'password', 'Jane Doe') .then(function (response) { console.log(response); }, function (error) { @@ -47,7 +47,7 @@ client const account = new Account(client); // Register User -account.create('[USER_ID]', 'me@example.com', 'password', 'Jane Doe') +account.create(ID.unique(), 'me@example.com', 'password', 'Jane Doe') .then(function (response) { console.log(response); }, function (error) { From cb8688a8c3da3a0ccc791224a73c87976a19cbb0 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 7 Sep 2022 14:43:50 +1200 Subject: [PATCH 07/30] Update and move swift getting started docs to right location --- .../sdks/{swift-client => apple}/CHANGELOG.md | 0 .../GETTING_STARTED.md | 66 ++++++++------ docs/sdks/swift-server/GETTING_STARTED.md | 83 ----------------- .../sdks/{swift-server => swift}/CHANGELOG.md | 0 docs/sdks/swift/GETTING_STARTED.md | 91 +++++++++++++++++++ 5 files changed, 128 insertions(+), 112 deletions(-) rename docs/sdks/{swift-client => apple}/CHANGELOG.md (100%) rename docs/sdks/{swift-client => apple}/GETTING_STARTED.md (62%) delete mode 100644 docs/sdks/swift-server/GETTING_STARTED.md rename docs/sdks/{swift-server => swift}/CHANGELOG.md (100%) create mode 100644 docs/sdks/swift/GETTING_STARTED.md diff --git a/docs/sdks/swift-client/CHANGELOG.md b/docs/sdks/apple/CHANGELOG.md similarity index 100% rename from docs/sdks/swift-client/CHANGELOG.md rename to docs/sdks/apple/CHANGELOG.md diff --git a/docs/sdks/swift-client/GETTING_STARTED.md b/docs/sdks/apple/GETTING_STARTED.md similarity index 62% rename from docs/sdks/swift-client/GETTING_STARTED.md rename to docs/sdks/apple/GETTING_STARTED.md index 51e5a5bc83..213ab764ac 100644 --- a/docs/sdks/swift-client/GETTING_STARTED.md +++ b/docs/sdks/apple/GETTING_STARTED.md @@ -53,30 +53,35 @@ For UIKit, you need to add the following function to your `SceneDelegate.swift`. ### Init your SDK -Initialize your SDK with your Appwrite server API endpoint and project ID which can be found in your project settings page and your new API secret Key project API keys section. +Initialize your SDK with your Appwrite server API endpoint and project ID which can be found in your project settings page. ```swift import Appwrite func main() { let client = Client() - .setEndpoint("http://[HOSTNAME_OR_IP]/v1") // Your API Endpoint - .setProject("5df5acd0d48c2") // Your project ID - .setSelfSigned() // Use only on dev mode with a self-signed SSL cert + .setEndpoint("http://[HOSTNAME_OR_IP]/v1") // Your API Endpoint + .setProject("5df5acd0d48c2") // Your project ID + .setSelfSigned() // Use only on dev mode with a self-signed SSL cert } ``` ### Make Your First Request -Once your SDK object is set, create any of the Appwrite service objects and choose any request to send. Full documentation for any service method you would like to use can be found in your SDK documentation or in the [API References](https://appwrite.io/docs) section. +Once your SDK object is initialized, create any of the Appwrite service objects and choose any request to send. Full documentation for any service method you would like to use can be found in your SDK documentation or in the [API References](https://appwrite.io/docs) section. ```swift -let users = Users(client: client) -users.create(userId: "[USER_ID]", email: "email@example.com", password: "password") { result in - switch result { - case .failure(let error): print(error.message) - case .success(let user): print(String(describing: user)) - } +let account = Account(client) + +do { + let account = try await account.create( + userId: ID.unique(), + email: "email@example.com", + password: "password" + ) + print(String(describing: account.toMap())) +} catch { + print(error.localizedDescription) } ``` @@ -87,37 +92,40 @@ import Appwrite func main() { let client = Client() - .setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint - .setProject("5df5acd0d48c2") // Your project ID - .setSelfSigned() // Use only on dev mode with a self-signed SSL cert + .setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint + .setProject("5df5acd0d48c2") // Your project ID + .setSelfSigned() // Use only on dev mode with a self-signed SSL cert - let users = Users(client: client) - users.create(userId: "[USER_ID]", email: "email@example.com", password: "password") { result in - switch result { - case .failure(let error): print(error.message) - case .success(let user): print(String(describing: user)) - } + let account = Account(client) + + do { + let account = try await account.create( + userId: ID.unique(), + email: "email@example.com", + password: "password" + ) + print(String(describing: account.toMap())) + } catch { + print(error.localizedDescription) } } ``` ### Error Handling -When an error occurs, the Appwrite Swift SDK responds with a result wrapping an `AppwriteError` object with `message` and `code` properties. You can handle any errors in the result's `.failure` case and present the `message` to the user or handle it yourself based on the provided error information. Below is an example. +When an error occurs, the Appwrite Apple SDK throws an `AppwriteError` object with `message` and `code` properties. You can handle any errors in a catch block and present the `message` or `localizedDescription` to the user or handle it yourself based on the provided error information. Below is an example. ```swift import Appwrite func main() { - let users = Users(client: client) + let account = Account(client) - users.create(userId: "[USER_ID]", email: "email@example.com", password: "password") { result in - switch result { - case .failure(let error): - print(error.message) - case .success(var response): - ... - } + do { + let account = try await account.get() + print(String(describing: account.toMap())) + } catch { + print(error.localizedDescription) } } ``` diff --git a/docs/sdks/swift-server/GETTING_STARTED.md b/docs/sdks/swift-server/GETTING_STARTED.md deleted file mode 100644 index c27fbb34a0..0000000000 --- a/docs/sdks/swift-server/GETTING_STARTED.md +++ /dev/null @@ -1,83 +0,0 @@ -## Getting Started - -### Init your SDK - -Initialize your SDK with your Appwrite server API endpoint and project ID which can be found in your project settings page and your new API secret Key project API keys section. - -```swift -import Appwrite - -func main() { - let client = Client() - .setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint - .setProject("5df5acd0d48c2") // Your project ID - .setKey("919c2d18fb5d4...a2ae413da83346ad2") // Your secret API key - .setSelfSigned() // Use only on dev mode with a self-signed SSL cert -} -``` - -### Make Your First Request - -Once your SDK object is set, create any of the Appwrite service objects and choose any request to send. Full documentation for any service method you would like to use can be found in your SDK documentation or in the [API References](https://appwrite.io/docs) section. - -```swift -let users = Users(client: client) -users.create(userId: "[USER_ID]", email: "email@example.com", password: "password") { result in - switch result { - case .failure(let error): print(error.message) - case .success(let user): print(String(describing: user)) - } -} -``` - -### Full Example - -```swift -import Appwrite - -func main() { - let client = Client() - .setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint - .setProject("5df5acd0d48c2") // Your project ID - .setKey("919c2d18fb5d4...a2ae413da83346ad2") // Your secret API key - .setSelfSigned() // Use only on dev mode with a self-signed SSL cert - - let users = Users(client: client) - users.create(userId: "[USER_ID]", email: "email@example.com", password: "password") { result in - switch result { - case .failure(let error): print(error.message) - case .success(let user): print(String(describing: user)) - } - } -} -``` - -### Error Handling - -When an error occurs, the Appwrite Swift SDK responds with a result wrapping an `AppwriteError` object with `message` and `code` properties. You can handle any errors in the result's `.failure` case and present the `message` to the user or handle it yourself based on the provided error information. Below is an example. - -```swift -import Appwrite - -func main() { - let users = Users(client: client) - - users.create(userId: "[USER_ID]", email: "email@example.com", password: "password") { result in - switch result { - case .failure(let error): - print(error.message) - case .success(var response): - ... - } - } -} -``` - -### Learn more - -You can use the following resources to learn more and get help - -- 🚀 [Getting Started Tutorial](https://appwrite.io/docs/getting-started-for-server) -- 📜 [Appwrite Docs](https://appwrite.io/docs) -- 💬 [Discord Community](https://appwrite.io/discord) -- 🚂 [Appwrite Swift Playground](https://github.com/appwrite/playground-for-swift-server) diff --git a/docs/sdks/swift-server/CHANGELOG.md b/docs/sdks/swift/CHANGELOG.md similarity index 100% rename from docs/sdks/swift-server/CHANGELOG.md rename to docs/sdks/swift/CHANGELOG.md diff --git a/docs/sdks/swift/GETTING_STARTED.md b/docs/sdks/swift/GETTING_STARTED.md new file mode 100644 index 0000000000..e0fb45dd7d --- /dev/null +++ b/docs/sdks/swift/GETTING_STARTED.md @@ -0,0 +1,91 @@ +## Getting Started + +### Init your SDK + +Initialize your SDK with your Appwrite server API endpoint and project ID which can be found in your project settings page and your new API secret Key project API keys section. + +```swift +import Appwrite + +func main() { + let client = Client() + .setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint + .setProject("5df5acd0d48c2") // Your project ID + .setKey("919c2d184...a2ae413dad2") // Your secret API key + .setSelfSigned() // Use only on dev mode with a self-signed SSL cert +} +``` + +### Make Your First Request + +Once your SDK object is initialized, create any of the Appwrite service objects and choose any request to send. Full documentation for any service method you would like to use can be found in your SDK documentation or in the [API References](https://appwrite.io/docs) section. + +```swift +let users = Users(client) + +do { + let user = try await users.create( + userId: ID.unique(), + email: "email@example.com", + password: "password" + ) + print(String(describing: user.toMap())) +} catch { + print(error.localizedDescription) +} +``` + +### Full Example + +```swift +import Appwrite + +func main() { + let client = Client() + .setEndpoint("https://[HOSTNAME_OR_IP]/v1") // Your API Endpoint + .setProject("5df5acd0d48c2") // Your project ID + .setKey("919c2d18fb5d4...a2ae413da83346ad2") // Your secret API key + .setSelfSigned() // Use only on dev mode with a self-signed SSL cert + + let users = Users(client) + + do { + let user = try await users.create( + userId: ID.unique(), + email: "email@example.com", + password: "password" + ) + print(String(describing: user.toMap())) + } catch { + print(error.localizedDescription) + } +} +``` + +### Error Handling + +When an error occurs, the Appwrite Swift SDK throws an `AppwriteError` object with `message` and `code` properties. You can handle any errors in a catch block and present the `message` or `localizedDescription` to the user or handle it yourself based on the provided error information. Below is an example. + +```swift +import Appwrite + +func main() { + let users = Users(client) + + do { + let users = try await users.list() + print(String(describing: users.toMap())) + } catch { + print(error.localizedDescription) + } +} +``` + +### Learn more + +You can use the following resources to learn more and get help + +- 🚀 [Getting Started Tutorial](https://appwrite.io/docs/getting-started-for-server) +- 📜 [Appwrite Docs](https://appwrite.io/docs) +- 💬 [Discord Community](https://appwrite.io/discord) +- 🚂 [Appwrite Swift Playground](https://github.com/appwrite/playground-for-swift-server) From fb3ba9a47512a98901041603bc55096c73beea8a Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 7 Sep 2022 11:05:38 +0545 Subject: [PATCH 08/30] Update docs/sdks/android/GETTING_STARTED.md Co-authored-by: Jake Barnby --- docs/sdks/android/GETTING_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdks/android/GETTING_STARTED.md b/docs/sdks/android/GETTING_STARTED.md index 4549d7204b..bb312390fc 100644 --- a/docs/sdks/android/GETTING_STARTED.md +++ b/docs/sdks/android/GETTING_STARTED.md @@ -85,7 +85,7 @@ try { var user = account.create(ID.unique(), "email@example.com", "password") Log.d("Appwrite user", account.toMap()) } catch(e : AppwriteException) { - Log.e("AppwriteException",e.message.toString()) + e.printStackTrace() } ``` From e24d789510fc54ced6d1e3b3e51f1783f4bcd622 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 7 Sep 2022 11:05:44 +0545 Subject: [PATCH 09/30] Update docs/sdks/apple/GETTING_STARTED.md Co-authored-by: Jake Barnby --- docs/sdks/apple/GETTING_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdks/apple/GETTING_STARTED.md b/docs/sdks/apple/GETTING_STARTED.md index 213ab764ac..755fa4f93b 100644 --- a/docs/sdks/apple/GETTING_STARTED.md +++ b/docs/sdks/apple/GETTING_STARTED.md @@ -74,7 +74,7 @@ Once your SDK object is initialized, create any of the Appwrite service objects let account = Account(client) do { - let account = try await account.create( + let user = try await account.create( userId: ID.unique(), email: "email@example.com", password: "password" From 50d20f7e840b82698c6e1e71a5fc3a2107541f57 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 7 Sep 2022 11:05:50 +0545 Subject: [PATCH 10/30] Update docs/sdks/android/GETTING_STARTED.md Co-authored-by: Jake Barnby --- docs/sdks/android/GETTING_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdks/android/GETTING_STARTED.md b/docs/sdks/android/GETTING_STARTED.md index bb312390fc..684f98e0f6 100644 --- a/docs/sdks/android/GETTING_STARTED.md +++ b/docs/sdks/android/GETTING_STARTED.md @@ -83,7 +83,7 @@ The Appwrite Android SDK raises an `AppwriteException` object with `message`, `c ```kotlin try { var user = account.create(ID.unique(), "email@example.com", "password") - Log.d("Appwrite user", account.toMap()) + Log.d("Appwrite user", user.toMap()) } catch(e : AppwriteException) { e.printStackTrace() } From a342a77caf720261571efbb7a31823dd2a4a04ef Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 7 Sep 2022 11:05:56 +0545 Subject: [PATCH 11/30] Update docs/sdks/apple/GETTING_STARTED.md Co-authored-by: Jake Barnby --- docs/sdks/apple/GETTING_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdks/apple/GETTING_STARTED.md b/docs/sdks/apple/GETTING_STARTED.md index 755fa4f93b..129947ad24 100644 --- a/docs/sdks/apple/GETTING_STARTED.md +++ b/docs/sdks/apple/GETTING_STARTED.md @@ -79,7 +79,7 @@ do { email: "email@example.com", password: "password" ) - print(String(describing: account.toMap())) + print(String(describing: user.toMap())) } catch { print(error.localizedDescription) } From 4acd4a03d32f55d9033c321a24fd1720cac5652c Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 7 Sep 2022 11:06:02 +0545 Subject: [PATCH 12/30] Update docs/sdks/apple/GETTING_STARTED.md Co-authored-by: Jake Barnby --- docs/sdks/apple/GETTING_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdks/apple/GETTING_STARTED.md b/docs/sdks/apple/GETTING_STARTED.md index 129947ad24..bbc9b085f3 100644 --- a/docs/sdks/apple/GETTING_STARTED.md +++ b/docs/sdks/apple/GETTING_STARTED.md @@ -123,7 +123,7 @@ func main() { do { let account = try await account.get() - print(String(describing: account.toMap())) + print(String(describing: user.toMap())) } catch { print(error.localizedDescription) } From 0fd027b957f2cddaa991a4ab28136baba569affd Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 7 Sep 2022 11:06:33 +0545 Subject: [PATCH 13/30] Update docs/sdks/dotnet/GETTING_STARTED.md Co-authored-by: Jake Barnby --- docs/sdks/dotnet/GETTING_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdks/dotnet/GETTING_STARTED.md b/docs/sdks/dotnet/GETTING_STARTED.md index fe8e81499a..46396c106c 100644 --- a/docs/sdks/dotnet/GETTING_STARTED.md +++ b/docs/sdks/dotnet/GETTING_STARTED.md @@ -20,7 +20,7 @@ static async Task Main(string[] args) var users = Users(client); try { - var user = await users.create(ID.unique(), 'email@example.com', 'password', 'name'); + var user = await users.Create(ID.Unique(), 'email@example.com', 'password', 'name'); Console.WriteLine(user.ToMap()); } catch (AppwriteException e) { Console.WriteLine(e.Message); From 90e05fec8785320e4cd5a91a2b376e26cf281d12 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 7 Sep 2022 11:06:38 +0545 Subject: [PATCH 14/30] Update docs/sdks/dotnet/GETTING_STARTED.md Co-authored-by: Jake Barnby --- docs/sdks/dotnet/GETTING_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdks/dotnet/GETTING_STARTED.md b/docs/sdks/dotnet/GETTING_STARTED.md index 46396c106c..23bab6f70a 100644 --- a/docs/sdks/dotnet/GETTING_STARTED.md +++ b/docs/sdks/dotnet/GETTING_STARTED.md @@ -35,7 +35,7 @@ The Appwrite .NET SDK raises `AppwriteException` object with `message`, `code` a var users = Users(client); try { - var user = await users.create(ID.unique(), 'email@example.com', 'password', 'name'); + var user = await users.Create(ID.Unique(), 'email@example.com', 'password', 'name'); Console.WriteLine(user.ToMap()); } catch (AppwriteException e) { Console.WriteLine(e.Message); From b7e9d4894edf8e177d1f7fa9abe3ee6b54b945a5 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 7 Sep 2022 11:06:43 +0545 Subject: [PATCH 15/30] Update docs/sdks/kotlin/GETTING_STARTED.md Co-authored-by: Jake Barnby --- docs/sdks/kotlin/GETTING_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdks/kotlin/GETTING_STARTED.md b/docs/sdks/kotlin/GETTING_STARTED.md index a419d36cb1..99d5e719af 100644 --- a/docs/sdks/kotlin/GETTING_STARTED.md +++ b/docs/sdks/kotlin/GETTING_STARTED.md @@ -71,7 +71,7 @@ suspend fun main() { password = "password", ) } catch (e: AppwriteException) { - println(e) + e.printStackTrace() } } ``` From f18707c3a03b388069cfac1cba366f98d9d8e284 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 7 Sep 2022 11:06:51 +0545 Subject: [PATCH 16/30] Update docs/sdks/python/GETTING_STARTED.md Co-authored-by: Jake Barnby --- docs/sdks/python/GETTING_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdks/python/GETTING_STARTED.md b/docs/sdks/python/GETTING_STARTED.md index a7772c9f5d..9f693b65c6 100644 --- a/docs/sdks/python/GETTING_STARTED.md +++ b/docs/sdks/python/GETTING_STARTED.md @@ -30,7 +30,7 @@ result = users.create('[USER_ID]', 'email@example.com', 'password') ```python from appwrite.client import Client from appwrite.services.users import Users -from appwrite.client import ID +from appwrite.id import ID client = Client() From af1d1dd7a27eeca05f3227594501b8968eeda40b Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Wed, 7 Sep 2022 11:06:59 +0545 Subject: [PATCH 17/30] Update docs/sdks/apple/GETTING_STARTED.md Co-authored-by: Jake Barnby --- docs/sdks/apple/GETTING_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdks/apple/GETTING_STARTED.md b/docs/sdks/apple/GETTING_STARTED.md index bbc9b085f3..de2f233189 100644 --- a/docs/sdks/apple/GETTING_STARTED.md +++ b/docs/sdks/apple/GETTING_STARTED.md @@ -99,7 +99,7 @@ func main() { let account = Account(client) do { - let account = try await account.create( + let user = try await account.create( userId: ID.unique(), email: "email@example.com", password: "password" From 80e60d9d95bc896555fe59c8aaa233d5880b72fe Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Wed, 7 Sep 2022 11:26:22 +0100 Subject: [PATCH 18/30] Update V15.php --- src/Appwrite/Utopia/Response/Filters/V15.php | 86 +++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Response/Filters/V15.php b/src/Appwrite/Utopia/Response/Filters/V15.php index 19000c9567..57c8bce4ca 100644 --- a/src/Appwrite/Utopia/Response/Filters/V15.php +++ b/src/Appwrite/Utopia/Response/Filters/V15.php @@ -25,20 +25,27 @@ class V15 extends Filter $parsedResponse = $this->handleDatetimeAttributes($content, ['startTime', 'endTime']); break; case Response::MODEL_BUCKET: + $parsedResponse = $this->handleBucketAttributes($content); + break; case Response::MODEL_COLLECTION: + $parsedResponse = $this->handleCollectionAttributes($content); + break; case Response::MODEL_DEPLOYMENT: case Response::MODEL_DOCUMENT: case Response::MODEL_EXECUTION: + $parsedResponse = $this->handleExecutionAttributes($content); + break; case Response::MODEL_PLATFORM: case Response::MODEL_PROJECT: case Response::MODEL_TEAM: case Response::MODEL_FILE: case Response::MODEL_WEBHOOK: case Response::MODEL_DOMAIN: + case Response::MODEL_DATABASE: $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt']); break; case Response::MODEL_FUNCTION: - $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'scheduleNext', 'schedulePrevious']); + $parsedResponse = $this->handleFunctionAttribtues($content); break; case Response::MODEL_KEY: $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'expire']); @@ -55,6 +62,8 @@ class V15 extends Filter case Response::MODEL_TOKEN: $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', 'expire']); break; + case Response::MODEL_USAGE_FUNCTIONS: + $parsedResponse = $this->handleModelUsageFuncAttributes($content); } // Downgrade Permissions for all models @@ -63,6 +72,20 @@ class V15 extends Filter return $parsedResponse; } + protected function handleBucketAttributes(array $content) + { + if ($content['fileSecurity']) { + $content['permssion'] = 'file'; + } else { + $content['permssion'] = 'bucket'; + } + + unset($content['fileSecurity']); + unset($content['compression']); + + $content = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt']); + } + protected function handleDatetimeAttributes(array $content, array $attributes): array { foreach ($attributes as $attribute) { @@ -76,6 +99,10 @@ class V15 extends Filter protected function handleUser(array $content): array { unset($content['password']); + unset($content['hash']); + unset($content['hashOptions']); + + $content = $this->handleDatetimeAttributes($content, ['registration', 'passwordUpdate', '$createdAt', '$updatedAt']); return $content; } @@ -149,4 +176,61 @@ class V15 extends Filter unset($model['$permissions']); return array_merge($model, $result); } + + protected function handleCollectionAttributes(array $content) + { + $content['permission'] = $content['documentSecurity']; + + unset($content['documentSecurity']); + $content = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt']); + return $content; + } + + private function handleExecutionAttributes($content) + { + unset($content['stdout']); + + $content = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt']); + return $content; + } + + private function handleFunctionAttribtues($content) + { + $content['execute'] = array_map($this->downgradePermissionSelector, $content['execute']); + + $content = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'scheduleNext', 'schedulePrevious']); + return $content; + } + + private function handleModelUsageFuncAttributes($content) + { + $content['functiosnExecutions'] = $content['executionsTotal']; + $content['functionsFailures'] = $content['executionsFailure']; + $content['functionsCompute'] = $content['executionsTime']; + unset($content['functionExecutions']); + unset($content['functionFailure']); + unset($content['executionsTime']); + unset($content['buildsTotal']); + unset($content['executionsSuccess']); + unset($content['buildsFailure']); + unset($content['buildsSuccess']); + unset($content['buildsTime']); + + return $content; + } + + private function handleUsageProjectAttributes($content) + { + $content['functions'] = $content['executions']; + unset($content['executions']); + + return $content; + } + + private function handleUsageStorageAttribtues($content) + { + $content['filesStorage'] = $content['storage']; + $content['tagsStorage'] = []; + unset($content['storage']); + } } From 7d3829e35ff27e4ad3140f7be040b93d08372b95 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Thu, 8 Sep 2022 12:33:20 +0100 Subject: [PATCH 19/30] Update V15.php --- src/Appwrite/Utopia/Request/Filters/V15.php | 74 +++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/Appwrite/Utopia/Request/Filters/V15.php b/src/Appwrite/Utopia/Request/Filters/V15.php index 300750f95e..9170386280 100644 --- a/src/Appwrite/Utopia/Request/Filters/V15.php +++ b/src/Appwrite/Utopia/Request/Filters/V15.php @@ -3,6 +3,7 @@ namespace Appwrite\Utopia\Request\Filters; use Appwrite\Utopia\Request\Filter; +use Utopia\Database\Query; class V15 extends Filter { @@ -10,8 +11,81 @@ class V15 extends Filter public function parse(array $content, string $model): array { switch ($model) { + // Old Query -> New Query + case "account.logs": + $content = $this->handleAccountLogs($content); + break; + case "account.initials": + $content = $this->handleInitials($content); } return $content; } + + protected function handleAccountLogs($content) + { + // Translate Old Query System to New Query System + + if (!empty($content['limit'])) { + $content['queries'][] = 'Query.limit('.$content['limit'].')'; + } + + if (!empty($content['offset'])) { + $content['queries'][] = 'Query.offset('.$content['offset'].')'; + } + + unset($content['limit']); + unset($content['offset']); + + return $content; + } + + protected function handleInitials($content) + { + unset($content[' color']); + + return $content; + } + + protected function handleQueryTranslation($content) { + $content['queries'] = []; + + if (isset($content['limit'])) { + $content['queries'][] = Query::limit($content['limit']); + } + + if (isset($content['offset'])) { + $content['queries'][] = Query::offset($content['offset']); + } + + if (isset($content['cursor'])) { + $direction = $content['cursorDirection'] ?? 'after'; + + if ($direction === 'after') { + $content['queries'][] = Query::cursorAfter($content['cursor']); + } else { + $content['queries'][] = Query::cursorBefore($content['cursor']); + } + } + + if (isset($content['orderAttributes'])) { + foreach ($content['orderAttributes'] as $i=>$attribute) { + if ($content['orderTypes'][$i] === 'ASC') { + $content['queries'][] = Query::orderAsc($attribute); + } else if ($content['orderTypes'][$i] === 'DESC') { + $content['queries'][] = Query::orderDesc($attribute); + } else { + continue; + } + } + } + + unset($content['limit']); + unset($content['offset']); + unset($content['cursor']); + unset($content['orderAttributes']); + unset($content['orderTypes']); + + return $content; + } } From ddd15921a3795869d0e0528ae53f0d6dfd070580 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Thu, 8 Sep 2022 23:55:29 +0000 Subject: [PATCH 20/30] Update V15 request filters Request filters are in place for the following services: * account * databases * functions * projects --- src/Appwrite/Utopia/Request/Filters/V15.php | 244 ++++++-- tests/unit/Utopia/Request/Filters/V15Test.php | 570 ++++++++++++++++++ 2 files changed, 770 insertions(+), 44 deletions(-) create mode 100644 tests/unit/Utopia/Request/Filters/V15Test.php diff --git a/src/Appwrite/Utopia/Request/Filters/V15.php b/src/Appwrite/Utopia/Request/Filters/V15.php index 9170386280..b6b2497fba 100644 --- a/src/Appwrite/Utopia/Request/Filters/V15.php +++ b/src/Appwrite/Utopia/Request/Filters/V15.php @@ -3,7 +3,7 @@ namespace Appwrite\Utopia\Request\Filters; use Appwrite\Utopia\Request\Filter; -use Utopia\Database\Query; +use Utopia\Database\Database; class V15 extends Filter { @@ -11,27 +11,64 @@ class V15 extends Filter public function parse(array $content, string $model): array { switch ($model) { - // Old Query -> New Query - case "account.logs": - $content = $this->handleAccountLogs($content); + case 'account.logs': + case 'databases.listLogs': + case 'databases.listCollectionLogs': + case 'databases.listDocumentLogs': + $content = $this->convertLimitAndOffset($content); + break; + case 'account.initials': + unset($content['color']); + break; + case 'databases.list': + case 'databases.listCollections': + case 'functions.list': + case 'functions.listDeployments': + case 'projects.list': + $content = $this->convertLimitAndOffset($content); + $content = $this->convertCursor($content); + $content = $this->convertOrderType($content); + break; + case 'databases.createCollection': + case 'databases.updateCollection': + $content = $this->convertCollectionPermission($content); + $content = $this->convertReadWrite($content); + break; + case 'databases.createDocument': + case 'databases.updateDocument': + $content = $this->convertReadWrite($content); + break; + case 'databases.listDocuments': + $content = $this->convertFilters($content); + $content = $this->convertLimitAndOffset($content); + $content = $this->convertCursor($content); + $content = $this->convertOrders($content); + break; + case 'functions.create': + case 'functions.update': + $content = $this->convertExecute($content); + break; + case 'functions.listExecutions': + $content = $this->convertLimitAndOffset($content); + $content = $this->convertCursor($content); + break; + case 'projects.createKey': + case 'projects.updateKey': + $content = $this->convertExpire($content); break; - case "account.initials": - $content = $this->handleInitials($content); } return $content; } - protected function handleAccountLogs($content) + protected function convertLimitAndOffset($content) { - // Translate Old Query System to New Query System + if (isset($content['limit'])) { + $content['queries'][] = 'limit(' . $content['limit'] . ')'; + } - if (!empty($content['limit'])) { - $content['queries'][] = 'Query.limit('.$content['limit'].')'; - } - - if (!empty($content['offset'])) { - $content['queries'][] = 'Query.offset('.$content['offset'].')'; + if (isset($content['offset'])) { + $content['queries'][] = 'offset(' . $content['offset'] . ')'; } unset($content['limit']); @@ -40,52 +77,171 @@ class V15 extends Filter return $content; } - protected function handleInitials($content) + protected function convertCursor($content) { - unset($content[' color']); + if (isset($content['cursor'])) { + $cursorDirection = $content['cursorDirection'] ?? Database::CURSOR_AFTER; + + if ($cursorDirection === Database::CURSOR_BEFORE) { + $content['queries'][] = 'cursorBefore("' . $content["cursor"] . '")'; + } else { + $content['queries'][] = 'cursorAfter("' . $content["cursor"] . '")'; + } + } + + unset($content['cursor']); + unset($content['cursorDirection']); return $content; } - protected function handleQueryTranslation($content) { - $content['queries'] = []; - - if (isset($content['limit'])) { - $content['queries'][] = Query::limit($content['limit']); - } - - if (isset($content['offset'])) { - $content['queries'][] = Query::offset($content['offset']); - } - - if (isset($content['cursor'])) { - $direction = $content['cursorDirection'] ?? 'after'; - - if ($direction === 'after') { - $content['queries'][] = Query::cursorAfter($content['cursor']); + protected function convertOrderType($content) + { + if (isset($content['orderType'])) { + if ($content['orderType'] === Database::ORDER_DESC) { + $content['queries'][] = 'orderDesc("")'; } else { - $content['queries'][] = Query::cursorBefore($content['cursor']); + $content['queries'][] = 'orderAsc("")'; } } + unset($content['orderType']); - if (isset($content['orderAttributes'])) { - foreach ($content['orderAttributes'] as $i=>$attribute) { - if ($content['orderTypes'][$i] === 'ASC') { - $content['queries'][] = Query::orderAsc($attribute); - } else if ($content['orderTypes'][$i] === 'DESC') { - $content['queries'][] = Query::orderDesc($attribute); + return $content; + } + + protected function convertOrders($content) + { + if (isset($content['orderTypes'])) { + foreach ($content['orderTypes'] as $i => $type) { + $attribute = $content['orderAttributes'][$i] ?? ''; + + if ($type === Database::ORDER_DESC) { + $content['queries'][] = 'orderDesc("' . $attribute . '")'; } else { - continue; + $content['queries'][] = 'orderAsc("' . $attribute . '")'; } } } - - unset($content['limit']); - unset($content['offset']); - unset($content['cursor']); + unset($content['orderAttributes']); unset($content['orderTypes']); return $content; } + + protected function convertCollectionPermission($content) + { + if (isset($content['permission'])) { + $content['documentSecurity'] = $content['permission'] === 'document'; + } + + unset($content['permission']); + + return $content; + } + + protected function convertReadWrite($content) + { + if (isset($content['read'])) { + foreach ($content['read'] as $read) { + if ($read === 'role:all') { + $content['permissions'][] = 'read("any")'; + } elseif ($read === 'role:guest') { + $content['permissions'][] = 'read("guests")'; + } elseif ($read === 'role:member') { + $content['permissions'][] = 'read("users")'; + } elseif (str_contains($read, ':')) { + $content['permissions'][] = 'read("' . $read . '")'; + } + } + } + + if (isset($content['write'])) { + foreach ($content['write'] as $write) { + if ($write === 'role:all' || $write === 'role:member') { + $content['permissions'][] = 'write("users")'; + } elseif ($write === 'role:guest') { + // don't add because, historically, + // role:guest for write did nothing + } elseif (str_contains($write, ':')) { + $content['permissions'][] = 'write("' . $write . '")'; + } + } + } + + unset($content['read']); + unset($content['write']); + + return $content; + } + + protected function convertFilters($content) + { + if (!isset($content['queries'])) { + return $content; + } + + $operations = [ + 'equal' => 'equal', + 'notEqual' => 'notEqual', + 'lesser' => 'lessThan', + 'lesserEqual' => 'lessThanEqual', + 'greater' => 'greaterThan', + 'greaterEqual' => 'greaterThanEqual', + 'search' => 'search', + ]; + foreach ($content['queries'] as $i => $query) { + foreach ($operations as $oldOperation => $newOperation) { + $middle = ".$oldOperation("; + if (str_contains($query, $middle)) { + $parts = explode($middle, $query); + if (count($parts) > 1) { + $attribute = $parts[0]; + $value = rtrim($parts[1], ")"); + $content['queries'][$i] = $newOperation . '("' . $attribute . '", [' . $value . '])'; + } + } + } + } + return $content; + } + + protected function convertExecute($content) + { + if (!isset($content['execute'])) { + return $content; + } + + $execute = []; + foreach ($content['execute'] as $role) { + if ($role === 'role:all' || $role === 'role:member') { + $execute[] = 'users'; + } elseif ($role === 'role:guest') { + // don't add because, historically, + // role:guest for write did nothing + } elseif (str_contains($role, ':')) { + $execute[] = $role; + } + } + $content['execute'] = $execute; + + return $content; + } + + protected function convertExpire($content) + { + if (!isset($content['expire'])) { + return $content; + } + + $expire = (int) $content['expire']; + + if ($expire === 0) { + $content['expire'] = null; + } else { + $content['expire'] = date(\DateTime::RFC3339_EXTENDED, $expire); + } + + return $content; + } } diff --git a/tests/unit/Utopia/Request/Filters/V15Test.php b/tests/unit/Utopia/Request/Filters/V15Test.php new file mode 100644 index 0000000000..1c8552d73d --- /dev/null +++ b/tests/unit/Utopia/Request/Filters/V15Test.php @@ -0,0 +1,570 @@ +filter = new V15(); + } + + public function tearDown(): void + { + } + + public function limitOffsetProvider(): array + { + return [ + 'basic test' => [ + ['limit' => '12', 'offset' => '0'], + ['queries' => ['limit(12)', 'offset(0)']] + ], + ]; + } + + /** + * @dataProvider limitOffsetProvider + */ + public function testGetAccountLogs(array $content, array $expected): void + { + $model = 'account.logs'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function testGetAccountInitials(): void + { + $model = 'account.initials'; + + $content = ['color' => 'deadbeef']; + $expected = []; + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function limitOffsetCursorOrderTypeProvider(): array + { + return [ + 'basic test' => [ + [ + 'limit' => '12', + 'offset' => '0', + 'cursor' => 'abcd', + 'cursorDirection' => 'before', + 'orderType' => 'asc', + ], + [ + 'queries' => [ + 'limit(12)', + 'offset(0)', + 'cursorBefore("abcd")', + 'orderAsc("")' + ] + ], + ], + ]; + } + + public function cursorProvider(): array + { + return [ + 'cursorDirection after' => [ + [ + 'cursor' => 'abcd', + 'cursorDirection' => 'after', + ], + [ + 'queries' => [ + 'cursorAfter("abcd")', + ] + ], + ], + 'cursorDirection invalid' => [ + [ + 'cursor' => 'abcd', + 'cursorDirection' => 'invalid', + ], + [ + 'queries' => [ + 'cursorAfter("abcd")', + ] + ], + ], + ]; + } + + public function orderTypeProvider(): array + { + return [ + 'orderType desc' => [ + [ + 'orderType' => 'DESC', + ], + [ + 'queries' => [ + 'orderDesc("")', + ] + ], + ], + 'orderType invalid' => [ + [ + 'orderType' => 'invalid', + ], + [ + 'queries' => [ + 'orderAsc("")', + ] + ], + ], + ]; + } + + /** + * @dataProvider limitOffsetCursorOrderTypeProvider + * @dataProvider limitOffsetProvider + * @dataProvider cursorProvider + * @dataProvider orderTypeProvider + */ + public function testListDatabases(array $content, array $expected): void + { + $model = 'databases.list'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider limitOffsetProvider + */ + public function testListDatabaseLogs(array $content, array $expected): void + { + $model = 'databases.listLogs'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function permissionProvider(): array + { + return [ + 'permission collection' => [ + ['permission' => 'collection'], + ['documentSecurity' => false], + ], + 'permission document' => [ + ['permission' => 'document'], + ['documentSecurity' => true], + ], + 'permission empty' => [ + [], + [], + ], + 'permission invalid' => [ + ['permission' => 'invalid'], + ['documentSecurity' => false], + ], + ]; + } + + public function readWriteProvider(): array + { + return [ + 'read all types' => [ + [ + 'read' => [ + 'role:all', + 'role:guest', + 'role:member', + 'user:a', + 'team:b', + 'team:c/member', + 'member:z', + ], + ], + [ + 'permissions' => [ + 'read("any")', + 'read("guests")', + 'read("users")', + 'read("user:a")', + 'read("team:b")', + 'read("team:c/member")', + 'read("member:z")', + ], + ], + ], + 'read invalid' => [ + ['read' => ['invalid', 'invalid:a']], + ['permissions' => ['read("invalid:a")']], + ], + 'write all types' => [ + [ + 'write' => [ + 'role:all', + 'role:guest', + 'role:member', + 'user:a', + 'team:b', + 'team:c/member', + 'member:z', + ], + ], + [ + 'permissions' => [ + 'write("users")', + 'write("users")', + 'write("user:a")', + 'write("team:b")', + 'write("team:c/member")', + 'write("member:z")', + ], + ], + ], + 'write invalid' => [ + ['write' => ['invalid', 'invalid:a']], + ['permissions' => ['write("invalid:a")']], + ] + ]; + } + + /** + * @dataProvider permissionProvider + * @dataProvider readWriteProvider + */ + public function testCreateCollection(array $content, array $expected): void + { + $model = 'databases.createCollection'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider limitOffsetCursorOrderTypeProvider + * @dataProvider limitOffsetProvider + * @dataProvider cursorProvider + * @dataProvider orderTypeProvider + */ + public function testListCollections(array $content, array $expected): void + { + $model = 'databases.listCollections'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider limitOffsetProvider + */ + public function testListCollectionLogs(array $content, array $expected): void + { + $model = 'databases.listCollectionLogs'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider permissionProvider + * @dataProvider readWriteProvider + */ + public function testUpdateCollection(array $content, array $expected): void + { + $model = 'databases.updateCollection'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider readWriteProvider + */ + public function testCreateDocument(array $content, array $expected): void + { + $model = 'databases.createDocument'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function ordersProvider(): array + { + return [ + 'basic test' => [ + [ + 'orderAttributes' => ['lastName', 'firstName'], + 'orderTypes' => ['DESC', 'ASC'], + ], + [ + 'queries' => [ + 'orderDesc("lastName")', + 'orderAsc("firstName")', + ] + ], + ], + 'orderType only' => [ + [ + 'orderTypes' => ['DESC'], + ], + [ + 'queries' => [ + 'orderDesc("")', + ] + ], + ], + 'orderType invalid' => [ + [ + 'orderAttributes' => ['lastName'], + 'orderTypes' => ['invalid'], + ], + [ + 'queries' => [ + 'orderAsc("lastName")', + ] + ], + ], + ]; + } + + public function filtersProvider(): array + { + return [ + 'all filters' => [ + [ + 'queries' => [ + 'lastName.equal("Smith", "Jackson")', + 'firstName.notEqual("John")', + 'age.lesser(50)', + 'age.lesserEqual(51)', + 'age.greater(20)', + 'age.greaterEqual(21)', + 'address.search("pla")', + ], + ], + [ + 'queries' => [ + 'equal("lastName", ["Smith", "Jackson"])', + 'notEqual("firstName", ["John"])', + 'lessThan("age", [50])', + 'lessThanEqual("age", [51])', + 'greaterThan("age", [20])', + 'greaterThanEqual("age", [21])', + 'search("address", ["pla"])', + ] + ], + ], + ]; + } + + /** + * @dataProvider limitOffsetProvider + * @dataProvider cursorProvider + * @dataProvider ordersProvider + * @dataProvider filtersProvider + */ + public function testListDocuments(array $content, array $expected): void + { + $model = 'databases.listDocuments'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result, 'fail'); + } + + /** + * @dataProvider limitOffsetProvider + */ + public function testListDocumentLogs(array $content, array $expected): void + { + $model = 'databases.listDocumentLogs'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider readWriteProvider + */ + public function testUpdateDocument(array $content, array $expected): void + { + $model = 'databases.updateDocument'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function executeProvider() : array { + return [ + 'all roles' => [ + [ + 'execute' => [ + 'role:all', + 'role:guest', + 'role:member', + 'user:a', + 'team:b', + 'team:c/member', + 'member:z', + ], + ], + [ + 'execute' => [ + 'users', + 'users', + 'user:a', + 'team:b', + 'team:c/member', + 'member:z', + ] + ], + ], + ]; + } + + /** + * @dataProvider executeProvider + */ + public function testCreateFunction(array $content, array $expected): void + { + $model = 'functions.create'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider limitOffsetCursorOrderTypeProvider + * @dataProvider limitOffsetProvider + * @dataProvider cursorProvider + * @dataProvider orderTypeProvider + */ + public function testListFunctions(array $content, array $expected): void + { + $model = 'functions.list'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider executeProvider + */ + public function testUpdateFunction(array $content, array $expected): void + { + $model = 'functions.update'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider limitOffsetCursorOrderTypeProvider + * @dataProvider limitOffsetProvider + * @dataProvider cursorProvider + * @dataProvider orderTypeProvider + */ + public function testListDeployments(array $content, array $expected): void + { + $model = 'functions.listDeployments'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider limitOffsetProvider + * @dataProvider cursorProvider + */ + public function testListExecutions(array $content, array $expected): void + { + $model = 'functions.listExecutions'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider limitOffsetCursorOrderTypeProvider + * @dataProvider limitOffsetProvider + * @dataProvider cursorProvider + * @dataProvider orderTypeProvider + */ + public function testListProjects(array $content, array $expected): void + { + $model = 'projects.list'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function expireProvider() : array + { + return [ + 'empty' => [ + [], + [], + ], + 'zero' => [ + ['expire' => '0'], + ['expire' => null], + ], + 'value' => [ + ['expire' => '1602743880'], + ['expire' => Model::TYPE_DATETIME_EXAMPLE], + ], + ]; + } + + /** + * @dataProvider expireProvider + */ + public function testCreateKey(array $content, array $expected) + { + $model = 'projects.createKey'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider expireProvider + */ + public function testUpdateKey(array $content, array $expected) + { + $model = 'projects.updateKey'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } +} From 668ebe4f4192c6392db35382adb055361105e522 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Fri, 9 Sep 2022 17:44:57 +0000 Subject: [PATCH 21/30] Update V15 request filters for storage, teams, and users --- composer.json | 8 +- composer.lock | 92 +++++---- src/Appwrite/Utopia/Request/Filters/V15.php | 56 ++++-- tests/unit/Utopia/Request/Filters/V15Test.php | 186 +++++++++++++++++- 4 files changed, 274 insertions(+), 68 deletions(-) diff --git a/composer.json b/composer.json index 8fe79a7ace..4598a51ea5 100644 --- a/composer.json +++ b/composer.json @@ -45,13 +45,13 @@ "appwrite/php-runtimes": "0.11.*", "utopia-php/framework": "0.21.*", "utopia-php/logger": "0.3.*", - "utopia-php/abuse": "0.12.*", + "utopia-php/abuse": "0.13.*", "utopia-php/analytics": "0.2.*", - "utopia-php/audit": "0.13.*", + "utopia-php/audit": "0.14.*", "utopia-php/cache": "0.6.*", "utopia-php/cli": "0.13.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.24.*", + "utopia-php/database": "0.25.*", "utopia-php/locale": "0.4.*", "utopia-php/registry": "0.5.*", "utopia-php/preloader": "0.2.*", @@ -77,7 +77,7 @@ } ], "require-dev": { - "appwrite/sdk-generator": "0.23.0", + "appwrite/sdk-generator": "0.25.0", "ext-fileinfo": "*", "phpunit/phpunit": "9.5.20", "squizlabs/php_codesniffer": "^3.6", diff --git a/composer.lock b/composer.lock index 5d2cef66f0..58bc100ead 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": "39c0ee0169b4681e5c07889d2a285d01", + "content-hash": "6194919aa35d896dc3dfdb846936cba4", "packages": [ { "name": "adhocore/jwt", @@ -1591,25 +1591,25 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.1.1", + "version": "v2.5.2", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918" + "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", - "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.1-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -1638,7 +1638,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2" }, "funding": [ { @@ -1654,7 +1654,7 @@ "type": "tidelift" } ], - "time": "2022-02-25T11:15:52+00:00" + "time": "2022-01-02T09:53:40+00:00" }, { "name": "symfony/polyfill-php80", @@ -1741,23 +1741,23 @@ }, { "name": "utopia-php/abuse", - "version": "0.12.0", + "version": "0.13.1", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "aa1e1aae163ecf8ea81d48857ff55c241dcb695f" + "reference": "4c1b8fe742f17158c59550cdfd9074a94bf474ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/aa1e1aae163ecf8ea81d48857ff55c241dcb695f", - "reference": "aa1e1aae163ecf8ea81d48857ff55c241dcb695f", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/4c1b8fe742f17158c59550cdfd9074a94bf474ac", + "reference": "4c1b8fe742f17158c59550cdfd9074a94bf474ac", "shasum": "" }, "require": { "ext-curl": "*", "ext-pdo": "*", "php": ">=8.0", - "utopia-php/database": "0.24.0" + "utopia-php/database": "0.25.*" }, "require-dev": { "phpunit/phpunit": "^9.4", @@ -1789,9 +1789,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.12.0" + "source": "https://github.com/utopia-php/abuse/tree/0.13.1" }, - "time": "2022-08-27T09:50:09+00:00" + "time": "2022-09-07T16:02:58+00:00" }, { "name": "utopia-php/analytics", @@ -1850,22 +1850,22 @@ }, { "name": "utopia-php/audit", - "version": "0.13.0", + "version": "0.14.1", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "a2f30ccfba7a61b1718b9ebd4557ed0d8a4dcb5b" + "reference": "b011224ed9bfef7e5c849938e65619af28f7cf41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/a2f30ccfba7a61b1718b9ebd4557ed0d8a4dcb5b", - "reference": "a2f30ccfba7a61b1718b9ebd4557ed0d8a4dcb5b", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/b011224ed9bfef7e5c849938e65619af28f7cf41", + "reference": "b011224ed9bfef7e5c849938e65619af28f7cf41", "shasum": "" }, "require": { "ext-pdo": "*", "php": ">=8.0", - "utopia-php/database": "0.24.0" + "utopia-php/database": "0.25.*" }, "require-dev": { "phpunit/phpunit": "^9.3", @@ -1897,9 +1897,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.13.0" + "source": "https://github.com/utopia-php/audit/tree/0.14.1" }, - "time": "2022-08-27T09:18:57+00:00" + "time": "2022-09-07T16:03:16+00:00" }, { "name": "utopia-php/cache", @@ -2060,16 +2060,16 @@ }, { "name": "utopia-php/database", - "version": "0.24.0", + "version": "0.25.2", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "7da841d65d87e9f2c242589e58c38880def44dd8" + "reference": "140bbedf1c4d622990fb94d26681fcca235cd5b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/7da841d65d87e9f2c242589e58c38880def44dd8", - "reference": "7da841d65d87e9f2c242589e58c38880def44dd8", + "url": "https://api.github.com/repos/utopia-php/database/zipball/140bbedf1c4d622990fb94d26681fcca235cd5b9", + "reference": "140bbedf1c4d622990fb94d26681fcca235cd5b9", "shasum": "" }, "require": { @@ -2118,9 +2118,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.24.0" + "source": "https://github.com/utopia-php/database/tree/0.25.2" }, - "time": "2022-08-27T09:16:05+00:00" + "time": "2022-09-09T03:58:01+00:00" }, { "name": "utopia-php/domains", @@ -2178,16 +2178,16 @@ }, { "name": "utopia-php/framework", - "version": "0.21.0", + "version": "0.21.1", "source": { "type": "git", "url": "https://github.com/utopia-php/framework.git", - "reference": "5aa5431788460a782065e42b0e8a35e7f139af2f" + "reference": "c81789b87a917da2daf336738170ebe01f50ea18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/5aa5431788460a782065e42b0e8a35e7f139af2f", - "reference": "5aa5431788460a782065e42b0e8a35e7f139af2f", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/c81789b87a917da2daf336738170ebe01f50ea18", + "reference": "c81789b87a917da2daf336738170ebe01f50ea18", "shasum": "" }, "require": { @@ -2221,9 +2221,9 @@ ], "support": { "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.21.0" + "source": "https://github.com/utopia-php/framework/tree/0.21.1" }, - "time": "2022-08-12T11:37:21+00:00" + "time": "2022-09-07T09:56:28+00:00" }, { "name": "utopia-php/image", @@ -2840,16 +2840,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.23.0", + "version": "0.25.0", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "efadccb9abd6263d045ef157881143d3a59dc710" + "reference": "91e2b9bfb06521faabd610935cbfc1825110666a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/efadccb9abd6263d045ef157881143d3a59dc710", - "reference": "efadccb9abd6263d045ef157881143d3a59dc710", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/91e2b9bfb06521faabd610935cbfc1825110666a", + "reference": "91e2b9bfb06521faabd610935cbfc1825110666a", "shasum": "" }, "require": { @@ -2884,9 +2884,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/0.23.0" + "source": "https://github.com/appwrite/sdk-generator/tree/0.25.0" }, - "time": "2022-09-04T17:29:33+00:00" + "time": "2022-09-06T16:32:52+00:00" }, { "name": "doctrine/instantiator", @@ -5382,7 +5382,13 @@ "ext-fileinfo": "*" }, "platform-overrides": { - "php": "8.0" + "php": "8.0", + "ext-imagick": "3.7.0", + "ext-yaml": "2.2.2", + "ext-redis": "5.3.7", + "ext-swoole": "4.8.10", + "ext-zstd": "0.11.0", + "ext-mongodb": "1.13.0" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.2.0" } diff --git a/src/Appwrite/Utopia/Request/Filters/V15.php b/src/Appwrite/Utopia/Request/Filters/V15.php index b6b2497fba..d9e6df8be3 100644 --- a/src/Appwrite/Utopia/Request/Filters/V15.php +++ b/src/Appwrite/Utopia/Request/Filters/V15.php @@ -4,6 +4,9 @@ namespace Appwrite\Utopia\Request\Filters; use Appwrite\Utopia\Request\Filter; use Utopia\Database\Database; +use Utopia\Database\Permission; +use Utopia\Database\Query; +use Utopia\Database\Role; class V15 extends Filter { @@ -15,6 +18,8 @@ class V15 extends Filter case 'databases.listLogs': case 'databases.listCollectionLogs': case 'databases.listDocumentLogs': + case 'teams.listLogs': + case 'users.getLogs': $content = $this->convertLimitAndOffset($content); break; case 'account.initials': @@ -25,6 +30,11 @@ class V15 extends Filter case 'functions.list': case 'functions.listDeployments': case 'projects.list': + case 'storage.listBuckets': + case 'storage.listFiles': + case 'teams.list': + case 'teams.getMemberships': + case 'users.list': $content = $this->convertLimitAndOffset($content); $content = $this->convertCursor($content); $content = $this->convertOrderType($content); @@ -36,6 +46,8 @@ class V15 extends Filter break; case 'databases.createDocument': case 'databases.updateDocument': + case 'storage.createFile': + case 'storage.updateFile': $content = $this->convertReadWrite($content); break; case 'databases.listDocuments': @@ -56,6 +68,11 @@ class V15 extends Filter case 'projects.updateKey': $content = $this->convertExpire($content); break; + case 'storage.createBucket': + case 'storage.updateBucket': + $content = $this->convertBucketPermission($content); + $content = $this->convertReadWrite($content); + break; } return $content; @@ -145,13 +162,13 @@ class V15 extends Filter if (isset($content['read'])) { foreach ($content['read'] as $read) { if ($read === 'role:all') { - $content['permissions'][] = 'read("any")'; + $content['permissions'][] = Permission::read(Role::any()); } elseif ($read === 'role:guest') { - $content['permissions'][] = 'read("guests")'; + $content['permissions'][] = Permission::read(Role::guests()); } elseif ($read === 'role:member') { - $content['permissions'][] = 'read("users")'; + $content['permissions'][] = Permission::read(Role::users()); } elseif (str_contains($read, ':')) { - $content['permissions'][] = 'read("' . $read . '")'; + $content['permissions'][] = Permission::read(Role::parse($read)); } } } @@ -159,12 +176,12 @@ class V15 extends Filter if (isset($content['write'])) { foreach ($content['write'] as $write) { if ($write === 'role:all' || $write === 'role:member') { - $content['permissions'][] = 'write("users")'; + $content['permissions'][] = Permission::write(Role::users()); } elseif ($write === 'role:guest') { // don't add because, historically, // role:guest for write did nothing } elseif (str_contains($write, ':')) { - $content['permissions'][] = 'write("' . $write . '")'; + $content['permissions'][] = Permission::write(Role::parse($write)); } } } @@ -182,13 +199,13 @@ class V15 extends Filter } $operations = [ - 'equal' => 'equal', - 'notEqual' => 'notEqual', - 'lesser' => 'lessThan', - 'lesserEqual' => 'lessThanEqual', - 'greater' => 'greaterThan', - 'greaterEqual' => 'greaterThanEqual', - 'search' => 'search', + 'equal' => Query::TYPE_EQUAL, + 'notEqual' => Query::TYPE_NOTEQUAL, + 'lesser' => Query::TYPE_LESSER, + 'lesserEqual' => Query::TYPE_LESSEREQUAL, + 'greater' => Query::TYPE_GREATER, + 'greaterEqual' => Query::TYPE_GREATEREQUAL, + 'search' => Query::TYPE_SEARCH, ]; foreach ($content['queries'] as $i => $query) { foreach ($operations as $oldOperation => $newOperation) { @@ -215,7 +232,7 @@ class V15 extends Filter $execute = []; foreach ($content['execute'] as $role) { if ($role === 'role:all' || $role === 'role:member') { - $execute[] = 'users'; + $execute[] = Role::users()->toString(); } elseif ($role === 'role:guest') { // don't add because, historically, // role:guest for write did nothing @@ -244,4 +261,15 @@ class V15 extends Filter return $content; } + + protected function convertBucketPermission($content) + { + if (isset($content['permission'])) { + $content['fileSecurity'] = $content['permission'] === 'file'; + } + + unset($content['permission']); + + return $content; + } } diff --git a/tests/unit/Utopia/Request/Filters/V15Test.php b/tests/unit/Utopia/Request/Filters/V15Test.php index 1c8552d73d..28bdda3911 100644 --- a/tests/unit/Utopia/Request/Filters/V15Test.php +++ b/tests/unit/Utopia/Request/Filters/V15Test.php @@ -1,6 +1,6 @@ assertEquals($expected, $result); } - public function permissionProvider(): array + public function collectionPermissionProvider(): array { return [ 'permission collection' => [ @@ -244,7 +244,7 @@ class V15Test extends TestCase } /** - * @dataProvider permissionProvider + * @dataProvider collectionPermissionProvider * @dataProvider readWriteProvider */ public function testCreateCollection(array $content, array $expected): void @@ -284,7 +284,7 @@ class V15Test extends TestCase } /** - * @dataProvider permissionProvider + * @dataProvider collectionPermissionProvider * @dataProvider readWriteProvider */ public function testUpdateCollection(array $content, array $expected): void @@ -389,7 +389,7 @@ class V15Test extends TestCase $result = $this->filter->parse($content, $model); - $this->assertEquals($expected, $result, 'fail'); + $this->assertEquals($expected, $result); } /** @@ -416,7 +416,8 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } - public function executeProvider() : array { + public function executeProvider(): array + { return [ 'all roles' => [ [ @@ -526,7 +527,7 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } - public function expireProvider() : array + public function expireProvider(): array { return [ 'empty' => [ @@ -567,4 +568,175 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + + public function bucketPermissionProvider(): array + { + return [ + 'permission bucket' => [ + ['permission' => 'bucket'], + ['fileSecurity' => false], + ], + 'permission document' => [ + ['permission' => 'file'], + ['fileSecurity' => true], + ], + 'permission empty' => [ + [], + [], + ], + 'permission invalid' => [ + ['permission' => 'invalid'], + ['fileSecurity' => false], + ], + ]; + } + + /** + * @dataProvider bucketPermissionProvider + * @dataProvider readWriteProvider + */ + public function testCreateBucket(array $content, array $expected) + { + $model = 'storage.createBucket'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider limitOffsetCursorOrderTypeProvider + * @dataProvider limitOffsetProvider + * @dataProvider cursorProvider + * @dataProvider orderTypeProvider + */ + public function testListBuckets(array $content, array $expected): void + { + $model = 'storage.listBuckets'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider bucketPermissionProvider + * @dataProvider readWriteProvider + */ + public function testUpdateBucket(array $content, array $expected) + { + $model = 'storage.updateBucket'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider readWriteProvider + */ + public function testCreateFile(array $content, array $expected) + { + $model = 'storage.createFile'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider limitOffsetCursorOrderTypeProvider + * @dataProvider limitOffsetProvider + * @dataProvider cursorProvider + * @dataProvider orderTypeProvider + */ + public function testListFiles(array $content, array $expected): void + { + $model = 'storage.listFiles'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider readWriteProvider + */ + public function testUpdateFile(array $content, array $expected) + { + $model = 'storage.updateFile'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider limitOffsetCursorOrderTypeProvider + * @dataProvider limitOffsetProvider + * @dataProvider cursorProvider + * @dataProvider orderTypeProvider + */ + public function testListTeams(array $content, array $expected): void + { + $model = 'teams.list'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider limitOffsetCursorOrderTypeProvider + * @dataProvider limitOffsetProvider + * @dataProvider cursorProvider + * @dataProvider orderTypeProvider + */ + public function testGetTeamMemberships(array $content, array $expected): void + { + $model = 'teams.getMemberships'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider limitOffsetProvider + */ + public function testListTeamLogs(array $content, array $expected): void + { + $model = 'teams.listLogs'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider limitOffsetCursorOrderTypeProvider + * @dataProvider limitOffsetProvider + * @dataProvider cursorProvider + * @dataProvider orderTypeProvider + */ + public function testListUsers(array $content, array $expected): void + { + $model = 'users.list'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider limitOffsetProvider + */ + public function testGetUserLogs(array $content, array $expected): void + { + $model = 'users.getLogs'; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } } From 71206a98864fd95349c0e5024fb0428a9c4891d3 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Sat, 10 Sep 2022 01:00:20 +0000 Subject: [PATCH 22/30] Update V15 response filter and add tests --- src/Appwrite/Utopia/Response/Filters/V15.php | 262 +++-- src/Appwrite/Utopia/Response/Model/Metric.php | 11 +- tests/unit/Utopia/Request/Filters/V15Test.php | 2 +- .../unit/Utopia/Response/Filters/V15Test.php | 912 ++++++++++++++++++ 4 files changed, 1089 insertions(+), 98 deletions(-) create mode 100644 tests/unit/Utopia/Response/Filters/V15Test.php diff --git a/src/Appwrite/Utopia/Response/Filters/V15.php b/src/Appwrite/Utopia/Response/Filters/V15.php index 57c8bce4ca..f28200d388 100644 --- a/src/Appwrite/Utopia/Response/Filters/V15.php +++ b/src/Appwrite/Utopia/Response/Filters/V15.php @@ -4,7 +4,9 @@ namespace Appwrite\Utopia\Response\Filters; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Filter; +use Utopia\Database\Database; use Utopia\Database\Permission; +use Utopia\Database\Role; class V15 extends Filter { @@ -14,26 +16,28 @@ class V15 extends Filter $parsedResponse = $content; switch ($model) { + case Response::MODEL_ACCOUNT: case Response::MODEL_USER: - $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'registration', 'passwordUpdate']); - $parsedResponse = $this->handleUser($parsedResponse); + $parsedResponse = $this->parseUser($parsedResponse); break; case Response::MODEL_METRIC: - $parsedResponse = $this->handleMetricAttributes($content); + $parsedResponse = $this->parseMetric($parsedResponse); break; case Response::MODEL_BUILD: - $parsedResponse = $this->handleDatetimeAttributes($content, ['startTime', 'endTime']); + $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['startTime', 'endTime']); break; case Response::MODEL_BUCKET: - $parsedResponse = $this->handleBucketAttributes($content); + $parsedResponse = $this->parseBucket($parsedResponse); break; case Response::MODEL_COLLECTION: - $parsedResponse = $this->handleCollectionAttributes($content); + $parsedResponse = $this->parseCollection($parsedResponse); break; case Response::MODEL_DEPLOYMENT: case Response::MODEL_DOCUMENT: + $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['$createdAt', '$updatedAt']); + break; case Response::MODEL_EXECUTION: - $parsedResponse = $this->handleExecutionAttributes($content); + $parsedResponse = $this->parseExecution($parsedResponse); break; case Response::MODEL_PLATFORM: case Response::MODEL_PROJECT: @@ -42,51 +46,62 @@ class V15 extends Filter case Response::MODEL_WEBHOOK: case Response::MODEL_DOMAIN: case Response::MODEL_DATABASE: - $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt']); + $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['$createdAt', '$updatedAt']); break; case Response::MODEL_FUNCTION: - $parsedResponse = $this->handleFunctionAttribtues($content); + $parsedResponse = $this->parseFunction($parsedResponse); break; case Response::MODEL_KEY: - $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'expire']); + $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['$createdAt', '$updatedAt', 'expire']); break; case Response::MODEL_LOG: - $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'time']); + $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['$createdAt', '$updatedAt', 'time']); break; case Response::MODEL_MEMBERSHIP: - $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'invited', 'joined']); + $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['$createdAt', '$updatedAt', 'invited', 'joined']); break; case Response::MODEL_SESSION: - $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', 'expire', 'providerAccessTokenExpiry']); + $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['$createdAt', 'expire', 'providerAccessTokenExpiry']); break; case Response::MODEL_TOKEN: - $parsedResponse = $this->handleDatetimeAttributes($content, ['$createdAt', 'expire']); + $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['$createdAt', 'expire']); break; case Response::MODEL_USAGE_FUNCTIONS: - $parsedResponse = $this->handleModelUsageFuncAttributes($content); + $parsedResponse = $this->parseModelUsageFunc($parsedResponse); + break; + case Response::MODEL_USAGE_PROJECT: + $parsedResponse = $this->parseUsageProject($parsedResponse); + break; + case Response::MODEL_USAGE_STORAGE: + $parsedResponse = $this->parseUsageStorage($parsedResponse); + break; } // Downgrade Permissions for all models - $parsedResponse = $this->handleDowngradePermissions($parsedResponse); + $parsedResponse = $this->parsePermissions($parsedResponse); return $parsedResponse; } - protected function handleBucketAttributes(array $content) + protected function parseBucket(array $content) { - if ($content['fileSecurity']) { - $content['permssion'] = 'file'; - } else { - $content['permssion'] = 'bucket'; + if (isset($content['fileSecurity'])) { + if ($content['fileSecurity']) { + $content['permission'] = 'file'; + } else { + $content['permission'] = 'bucket'; + } } unset($content['fileSecurity']); unset($content['compression']); - $content = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt']); + $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt']); + + return $content; } - protected function handleDatetimeAttributes(array $content, array $attributes): array + protected function parseDatetimeAttributes(array $content, array $attributes): array { foreach ($attributes as $attribute) { if (isset($content[$attribute])) { @@ -96,117 +111,141 @@ class V15 extends Filter return $content; } - protected function handleUser(array $content): array + protected function parseUser(array $content): array { unset($content['password']); unset($content['hash']); unset($content['hashOptions']); - $content = $this->handleDatetimeAttributes($content, ['registration', 'passwordUpdate', '$createdAt', '$updatedAt']); + $content = $this->parseDatetimeAttributes($content, ['registration', 'passwordUpdate', '$createdAt', '$updatedAt']); return $content; } - protected function handleMetricAttributes(array $content) + protected function parseMetric(array $content) { - $content['timestamp'] = $content['date']; - unset($content['date']); + $content = $this->parseDatetimeAttributes($content, ['date']); + return $content; } - protected function handleDowngradePermissions(array $content) + protected function parsePermissions(array $content) { if (!isset($content['$permissions'])) { return $content; } - $content = array_merge($content, $this->downgradePermissions($content['permissions'])); - unset($content['permissions']); - return $content; - } - protected function downgradePermissionSelector(string $permSelector) - { - switch ($permSelector) { - case 'any': - return 'role:all'; - case 'users': - return 'role:user'; - case 'guests': - return 'role:guest'; - } - - return $permSelector; - } - - protected function downgradePermissions(array $model) - { - if (!isset($model['$permissions'])) { - return $model; - } - - $permissions = $model['$permissions']; - - $result = [ - '$read' => [], - '$write' => [] - ]; + $read = []; + $write = []; // downgrade the permissions - foreach ($permissions as $permission) { + foreach ($content['$permissions'] as $permission) { $permission = Permission::parse($permission); - // permission = "read('any')" = ["$read" => "role:all"] + $permission_value = $permission->getRole(); + if ($permission->getIdentifier()) { + $permission_value .= ':' . $permission->getIdentifier(); + } + if ($permission->getDimension()) { + $permission_value .= '/' . $permission->getDimension(); + } // Old type permissions meant that 'write' is equivalent to 'create', 'update' and 'delete' - switch ($permission->getPermission()) { - case 'update': - case 'delete': - case 'write': - case 'create': - if (!in_array($this->downgradePermissionSelector($permission_value), $result['write'])) { - $result['$write'][] = $this->downgradePermissionSelector($permission_value); - } + case Database::PERMISSION_UPDATE: + case Database::PERMISSION_DELETE: + case Database::PERMISSION_WRITE: + case Database::PERMISSION_CREATE: + $write[$this->parseRole($permission_value)] = true; break; - case 'read': - if (!in_array($this->downgradePermissionSelector($permission_value), $result['read'])) { - $result['$read'][] = $this->downgradePermissionSelector($permission_value); - } + case Database::PERMISSION_READ: + $read[$this->parseRole($permission_value)] = true; break; } } - unset($model['$permissions']); - return array_merge($model, $result); + $content['$read'] = array_keys($read); + $content['$write'] = array_keys($write); + + unset($content['$permissions']); + + return $content; } - protected function handleCollectionAttributes(array $content) + protected function parseRole(string $role) { - $content['permission'] = $content['documentSecurity']; + switch ($role) { + case Role::any()->toString(): + return 'role:all'; + case Role::users()->toString(): + return 'role:member'; + case Role::guests()->toString(): + return 'role:guest'; + default: + return $role; + } + + return $role; + } + + protected function parseCollection(array $content) + { + if (isset($content['documentSecurity'])) { + if ($content['documentSecurity']) { + $content['permission'] = 'document'; + } else { + $content['permission'] = 'collection'; + } + } unset($content['documentSecurity']); - $content = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt']); + $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt']); return $content; } - private function handleExecutionAttributes($content) + private function parseExecution($content) { unset($content['stdout']); - - $content = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt']); + $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'startTime', 'endTime']); return $content; } - private function handleFunctionAttribtues($content) + private function parseFunction($content) { - $content['execute'] = array_map($this->downgradePermissionSelector, $content['execute']); + if (isset($content['execute'])) { + foreach ($content['execute'] as $i => $role) { + $content['execute'][$i] = $this->parseRole($role); + } + } - $content = $this->handleDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'scheduleNext', 'schedulePrevious']); + if (isset($content['vars'])) { + $vars = []; + foreach ($content['vars'] as $i => $var) { + $vars[$var['key']] = $var['value']; + } + $content['vars'] = $vars; + } + + $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'scheduleNext', 'schedulePrevious']); return $content; } - private function handleModelUsageFuncAttributes($content) + private function parseModelUsageFunc($content) { - $content['functiosnExecutions'] = $content['executionsTotal']; - $content['functionsFailures'] = $content['executionsFailure']; - $content['functionsCompute'] = $content['executionsTime']; + $mapping = [ + 'executionsTotal' => 'functionsExecutions', + 'executionsFailure' => 'functionsFailures', + 'executionsTime' => 'functionsCompute', + ]; + + foreach ($mapping as $new => $old) { + if (isset($content[$new])) { + $data = []; + foreach ($content[$new] as $metric) { + $data[] = $this->parseMetric($metric); + } + $content[$old] = $data; + unset($content[$new]); + } + } + unset($content['functionExecutions']); unset($content['functionFailure']); unset($content['executionsTime']); @@ -219,18 +258,59 @@ class V15 extends Filter return $content; } - private function handleUsageProjectAttributes($content) + private function parseUsageProject($content) { $content['functions'] = $content['executions']; unset($content['executions']); + $usage = [ + 'collections', + 'documents', + 'functions', + 'network', + 'requests', + 'storage', + 'users', + ]; + + foreach ($usage as $name) { + $data = []; + foreach ($content[$name] as $metric) { + $data[] = $this->parseMetric($metric); + } + $content[$name] = $data; + } + return $content; } - private function handleUsageStorageAttribtues($content) + private function parseUsageStorage($content) { $content['filesStorage'] = $content['storage']; - $content['tagsStorage'] = []; unset($content['storage']); + + $usage = [ + 'bucketsCount', + 'bucketsCreate', + 'bucketsDelete', + 'bucketsRead', + 'bucketsUpdate', + 'filesCount', + 'filesCreate', + 'filesDelete', + 'filesRead', + 'filesStorage', + 'filesUpdate', + ]; + + foreach ($usage as $name) { + $data = []; + foreach ($content[$name] as $metric) { + $data[] = $this->parseMetric($metric); + } + $content[$name] = $data; + } + + return $content; } } diff --git a/src/Appwrite/Utopia/Response/Model/Metric.php b/src/Appwrite/Utopia/Response/Model/Metric.php index 7fb1b98386..16cf7f7df2 100644 --- a/src/Appwrite/Utopia/Response/Model/Metric.php +++ b/src/Appwrite/Utopia/Response/Model/Metric.php @@ -17,12 +17,11 @@ class Metric extends Model 'example' => 1, ]) ->addRule('date', [ - 'type' => self::TYPE_INTEGER, - 'description' => 'The UNIX timestamp at which this metric was aggregated.', - 'default' => 0, - 'example' => 1592981250 - ]) - ; + 'type' => self::TYPE_DATETIME, + 'description' => 'The date at which this metric was aggregated in ISO 8601 format.', + 'default' => '', + 'example' => self::TYPE_DATETIME_EXAMPLE + ]); } /** diff --git a/tests/unit/Utopia/Request/Filters/V15Test.php b/tests/unit/Utopia/Request/Filters/V15Test.php index 28bdda3911..1617c2b0c5 100644 --- a/tests/unit/Utopia/Request/Filters/V15Test.php +++ b/tests/unit/Utopia/Request/Filters/V15Test.php @@ -1,6 +1,6 @@ filter = new V15(); + } + + public function tearDown(): void + { + } + + public function createdAtUpdatedAtProvider(): array + { + return [ + 'basic datetimes' => [ + [ + '$createdAt' => '2020-06-24T06:47:30.000Z', + '$updatedAt' => '2020-06-24T06:47:30.000Z', + ], + [ + '$createdAt' => 1592981250, + '$updatedAt' => 1592981250, + ], + ], + 'null datetime' => [ + [ + '$createdAt' => null, + '$updatedAt' => null, + ], + [ + '$createdAt' => 0, + '$updatedAt' => 0, + ], + ], + 'empty datetime' => [ + [ + '$createdAt' => '', + '$updatedAt' => '', + ], + [ + '$createdAt' => 0, + '$updatedAt' => 0, + ], + ], + ]; + } + + public function permissionsProvider(): array + { + return [ + 'basic permissions' => [ + [ + '$permissions' => [ + Permission::read(Role::any()), + Permission::write(Role::user('608f9da25e7e1')), + ], + ], + [ + '$read' => ['role:all'], + '$write' => ['user:608f9da25e7e1'], + ], + ], + 'all roles' => [ + [ + '$permissions' => [ + Permission::read(Role::any()), + Permission::read(Role::guests()), + Permission::read(Role::users()), + Permission::read(Role::user('asdf')), + Permission::read(Role::team('qwer')), + Permission::read(Role::team('qwer', 'uiop')), + Permission::read(Role::member('zxcv')), + ], + ], + [ + '$read' => [ + 'role:all', + 'role:guest', + 'role:member', + 'user:asdf', + 'team:qwer', + 'team:qwer/uiop', + 'member:zxcv', + ], + '$write' => [], + ], + ], + 'create conversion' => [ + [ + '$permissions' => [Permission::create(Role::user('a'))], + ], + [ + '$read' => [], + '$write' => ['user:a'], + ], + ], + 'update conversion' => [ + [ + '$permissions' => [Permission::update(Role::user('a'))], + ], + [ + '$read' => [], + '$write' => ['user:a'], + ], + ], + 'delete conversion' => [ + [ + '$permissions' => [Permission::delete(Role::user('a'))], + ], + [ + '$read' => [], + '$write' => ['user:a'], + ], + ], + 'write conversion' => [ + [ + '$permissions' => [Permission::write(Role::user('a'))], + ], + [ + '$read' => [], + '$write' => ['user:a'], + ], + ], + ]; + } + + public function testAccount(): void + { + $model = Response::MODEL_ACCOUNT; + + $content = [ + '$id' => '6264711f995c5b012b48', + '$createdAt' => '2020-06-24T06:47:30.000Z', + '$updatedAt' => '2020-06-24T06:47:30.000Z', + 'name' => 'John Doe', + 'registration' => '2020-06-24T06:47:30.000Z', + 'status' => true, + 'passwordUpdate' => '2020-06-24T06:47:30.000Z', + 'email' => 'john@appwrite.io', + 'phone' => '+4930901820', + 'emailVerification' => true, + 'phoneVerification' => true, + 'prefs' => new \stdClass(), + ]; + + $expected = [ + '$id' => '6264711f995c5b012b48', + '$createdAt' => 1592981250, + '$updatedAt' => 1592981250, + 'name' => 'John Doe', + 'registration' => 1592981250, + 'status' => true, + 'passwordUpdate' => 1592981250, + 'email' => 'john@appwrite.io', + 'phone' => '+4930901820', + 'emailVerification' => true, + 'phoneVerification' => true, + 'prefs' => new \stdClass(), + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function bucketProvider(): array + { + return [ + 'basic bucket' => [ + [ + '$id' => '5e5ea5c16897e', + '$createdAt' => '2020-06-24T06:47:30.000Z', + '$updatedAt' => '2020-06-24T06:47:30.000Z', + 'fileSecurity' => true, + '$permissions' => [ + Permission::read(Role::any()), + Permission::write(Role::user('608f9da25e7e1')), + ], + 'name' => 'Documents', + 'enabled' => false, + 'maximumFileSize' => 100, + 'allowedFileExtensions' => [ + 'jpg', + 'png' + ], + 'encryption' => false, + 'antivirus' => false, + ], + [ + '$id' => '5e5ea5c16897e', + '$createdAt' => 1592981250, + '$updatedAt' => 1592981250, + '$read' => ['role:all'], + '$write' => ['user:608f9da25e7e1'], + 'permission' => 'file', + 'name' => 'Documents', + 'enabled' => false, + 'maximumFileSize' => 100, + 'allowedFileExtensions' => [ + 'jpg', + 'png' + ], + 'encryption' => false, + 'antivirus' => false, + ], + ], + 'false fileSecurity' => [ + ['fileSecurity' => false], + ['permission' => 'bucket'], + ], + ]; + } + + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider permissionsProvider + * @dataProvider bucketProvider + */ + public function testBucket(array $content, array $expected): void + { + $model = Response::MODEL_BUCKET; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function testBuild(): void + { + $model = Response::MODEL_BUILD; + + $content = [ + 'startTime' => '2020-06-24T06:47:30.000Z', + 'endTime' => '2020-06-24T06:47:30.000Z', + ]; + + $expected = [ + 'startTime' => 1592981250, + 'endTime' => 1592981250, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function collectionProvider(): array + { + return [ + 'basic collection' => [ + [ + '$id' => '5e5ea5c16897e', + '$createdAt' => '2020-06-24T06:47:30.000Z', + '$updatedAt' => '2020-06-24T06:47:30.000Z', + '$permissions' => [ + Permission::read(Role::any()), + Permission::write(Role::user('608f9da25e7e1')), + ], + 'documentSecurity' => true, + 'databaseId' => '5e5ea5c16897e', + 'name' => 'My Collection', + 'enabled' => false, + 'attributes' => [ + 'key' => 'isEnabled', + 'type' => 'boolean', + 'status' => 'available', + 'required' => true, + 'array' => false, + 'default' => false + ], + 'indexes' => [ + 'key' => 'index1', + 'type' => 'primary', + 'status' => 'available', + 'attributes' => [], + 'orders' => [] + ], + ], + [ + '$id' => '5e5ea5c16897e', + '$createdAt' => 1592981250, + '$updatedAt' => 1592981250, + '$read' => [ + 'role:all' + ], + '$write' => [ + 'user:608f9da25e7e1' + ], + 'databaseId' => '5e5ea5c16897e', + 'name' => 'My Collection', + 'enabled' => false, + 'permission' => 'document', + 'attributes' => [ + 'key' => 'isEnabled', + 'type' => 'boolean', + 'status' => 'available', + 'required' => true, + 'array' => false, + 'default' => false + ], + 'indexes' => [ + 'key' => 'index1', + 'type' => 'primary', + 'status' => 'available', + 'attributes' => [], + 'orders' => [] + ], + ], + ], + 'false documentSecurity' => [ + ['documentSecurity' => false], + ['permission' => 'collection'], + ], + + ]; + } + + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider permissionsProvider + * @dataProvider collectionProvider + */ + public function testCollection(array $content, array $expected): void + { + $model = Response::MODEL_COLLECTION; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider createdAtUpdatedAtProvider + */ + public function testDatabase(array $content, array $expected): void + { + $model = Response::MODEL_DATABASE; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider createdAtUpdatedAtProvider + */ + public function testDeployment(array $content, array $expected): void + { + $model = Response::MODEL_DEPLOYMENT; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider permissionsProvider + */ + public function testDocument(array $content, array $expected): void + { + $model = Response::MODEL_DOCUMENT; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider createdAtUpdatedAtProvider + */ + public function testDomain(array $content, array $expected): void + { + $model = Response::MODEL_DOMAIN; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function executionProvider(): array + { + return [ + 'basic execution' => [ + ['stdout' => ''], + [], + ], + ]; + } + + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider permissionsProvider + * @dataProvider executionProvider + */ + public function testExecution(array $content, array $expected): void + { + $model = Response::MODEL_EXECUTION; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider permissionsProvider + */ + public function testFile(array $content, array $expected): void + { + $model = Response::MODEL_FILE; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function functionProvider(): array + { + return [ + 'basic function' => [ + [ + '$id' => '5e5ea5c16897e', + '$createdAt' => '2020-06-24T06:47:30.000Z', + '$updatedAt' => '2020-06-24T06:47:30.000Z', + 'execute' => [ + Role::users()->toString(), + ], + 'name' => 'My Function', + 'status' => 'enabled', + 'runtime' => 'python-3.8', + 'deployment' => '5e5ea5c16897e', + 'vars' => [ + [ + '$id' => '631bd31717e034f14aa8', + '$createdAt' => '2020-06-24T06:47:30.000Z', + '$updatedAt' => '2020-06-24T06:47:30.000Z', + 'key' => 'key', + 'value' => 'value', + 'functionId' => '5e5ea5c16897e', + ] + ], + 'events' => [ + 'account.create' + ], + 'schedule' => '5 4 * * *', + 'scheduleNext' => '2020-06-24T06:48:12.000Z', + 'schedulePrevious' => '2020-06-24T06:47:17.000Z', + 'timeout' => 1592981237 + ], + [ + '$id' => '5e5ea5c16897e', + '$createdAt' => 1592981250, + '$updatedAt' => 1592981250, + 'execute' => [ + 'role:member' + ], + 'name' => 'My Function', + 'status' => 'enabled', + 'runtime' => 'python-3.8', + 'deployment' => '5e5ea5c16897e', + 'vars' => [ + 'key' => 'value' + ], + 'events' => [ + 'account.create' + ], + 'schedule' => '5 4 * * *', + 'scheduleNext' => 1592981292, + 'schedulePrevious' => 1592981237, + 'timeout' => 1592981237 + ], + ], + ]; + } + + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider functionProvider + */ + public function testFunc(array $content, array $expected): void + { + $model = Response::MODEL_FUNCTION; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function keyProvider(): array + { + return [ + 'basic key' => [ + ['expire' => '2020-06-24T06:47:30.000Z'], + ['expire' => 1592981250], + ], + ]; + } + + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider keyProvider + */ + public function testKey(array $content, array $expected): void + { + $model = Response::MODEL_KEY; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function logProvider(): array + { + return [ + 'basic log' => [ + ['time' => '2020-06-24T06:47:30.000Z'], + ['time' => 1592981250], + ], + ]; + } + + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider logProvider + */ + public function testLog(array $content, array $expected): void + { + $model = Response::MODEL_LOG; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function membershipProvider(): array + { + return [ + 'basic membership' => [ + [ + 'invited' => '2020-06-24T06:47:30.000Z', + 'joined' => '2020-06-24T06:47:30.000Z', + ], + [ + 'invited' => 1592981250, + 'joined' => 1592981250, + ], + ], + ]; + } + + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider membershipProvider + */ + public function testMembership(array $content, array $expected): void + { + $model = Response::MODEL_MEMBERSHIP; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function metricProvider(): array + { + return [ + 'basic metric' => [ + [ + 'date' => '2020-06-24T06:47:30.000Z', + ], + [ + 'date' => 1592981250, + ], + ], + ]; + } + + /** + * @dataProvider metricProvider + */ + public function testMetric(array $content, array $expected): void + { + $model = Response::MODEL_METRIC; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider createdAtUpdatedAtProvider + */ + public function testPlatform(array $content, array $expected): void + { + $model = Response::MODEL_PLATFORM; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider createdAtUpdatedAtProvider + */ + public function testProject(array $content, array $expected): void + { + $model = Response::MODEL_PROJECT; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function sessionProvider(): array + { + return [ + 'basic session' => [ + [ + '$createdAt' => '2020-06-24T06:47:30.000Z', + 'expire' => '2020-06-24T06:47:30.000Z', + 'providerAccessTokenExpiry' => '2020-06-24T06:47:30.000Z', + ], + [ + '$createdAt' => 1592981250, + 'expire' => 1592981250, + 'providerAccessTokenExpiry' => 1592981250, + ], + ], + ]; + } + + /** + * @dataProvider sessionProvider + */ + public function testSession(array $content, array $expected): void + { + $model = Response::MODEL_SESSION; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider createdAtUpdatedAtProvider + */ + public function testTeam(array $content, array $expected): void + { + $model = Response::MODEL_TEAM; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function tokenProvider(): array + { + return [ + 'basic token' => [ + [ + '$createdAt' => '2020-06-24T06:47:30.000Z', + 'expire' => '2020-06-24T06:47:30.000Z', + ], + [ + '$createdAt' => 1592981250, + 'expire' => 1592981250, + ], + ], + ]; + } + + /** + * @dataProvider tokenProvider + */ + public function testToken(array $content, array $expected): void + { + $model = Response::MODEL_TOKEN; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function usageFunctionsProvider(): array + { + return [ + 'basic usage functions' => [ + [ + 'executionsTotal' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'executionsFailure' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'executionsSuccess' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'executionsTime' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'buildsTotal' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'buildsFailure' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'buildsSuccess' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'buildsTime' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + ], + [ + 'functionsExecutions' => [ + ['date' => 1592981250], + ], + 'functionsFailures' => [ + ['date' => 1592981250], + ], + 'functionsCompute' => [ + ['date' => 1592981250], + ], + ], + ], + ]; + } + + /** + * @dataProvider usageFunctionsProvider + */ + public function testUsageFunctions(array $content, array $expected): void + { + $model = Response::MODEL_USAGE_FUNCTIONS; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function usageProjectProvider(): array + { + return [ + 'basic usage project' => [ + [ + 'collections' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'documents' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'executions' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'network' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'requests' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'storage' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'users' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + ], + [ + 'collections' => [ + ['date' => 1592981250], + ], + 'documents' => [ + ['date' => 1592981250], + ], + 'functions' => [ + ['date' => 1592981250], + ], + 'network' => [ + ['date' => 1592981250], + ], + 'requests' => [ + ['date' => 1592981250], + ], + 'storage' => [ + ['date' => 1592981250], + ], + 'users' => [ + ['date' => 1592981250], + ], + ], + ], + ]; + } + + /** + * @dataProvider usageProjectProvider + */ + public function testUsageProject(array $content, array $expected): void + { + $model = Response::MODEL_USAGE_PROJECT; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function usageStorageProvider(): array + { + return [ + 'basic usage storage' => [ + [ + 'bucketsCount' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'bucketsCreate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'bucketsDelete' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'bucketsRead' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'bucketsUpdate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'filesCount' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'filesCreate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'filesDelete' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'filesRead' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'storage' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'filesUpdate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + ], + [ + 'bucketsCount' => [ + ['date' => 1592981250], + ], + 'bucketsCreate' => [ + ['date' => 1592981250], + ], + 'bucketsDelete' => [ + ['date' => 1592981250], + ], + 'bucketsRead' => [ + ['date' => 1592981250], + ], + 'bucketsUpdate' => [ + ['date' => 1592981250], + ], + 'filesCount' => [ + ['date' => 1592981250], + ], + 'filesCreate' => [ + ['date' => 1592981250], + ], + 'filesDelete' => [ + ['date' => 1592981250], + ], + 'filesRead' => [ + ['date' => 1592981250], + ], + 'filesStorage' => [ + ['date' => 1592981250], + ], + 'filesUpdate' => [ + ['date' => 1592981250], + ], + ], + ], + ]; + } + + /** + * @dataProvider usageStorageProvider + */ + public function testUsageStorage(array $content, array $expected): void + { + $model = Response::MODEL_USAGE_STORAGE; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } +} From 79b57cf69634c2e73efb39cfa2da32585bf4420b Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Mon, 12 Sep 2022 19:07:08 +0000 Subject: [PATCH 23/30] Update response filters for list models --- src/Appwrite/Utopia/Response/Filters/V15.php | 164 +++++- .../unit/Utopia/Response/Filters/V15Test.php | 519 ++++++++++++++++++ 2 files changed, 666 insertions(+), 17 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Filters/V15.php b/src/Appwrite/Utopia/Response/Filters/V15.php index f28200d388..047eb1d268 100644 --- a/src/Appwrite/Utopia/Response/Filters/V15.php +++ b/src/Appwrite/Utopia/Response/Filters/V15.php @@ -20,48 +20,134 @@ class V15 extends Filter case Response::MODEL_USER: $parsedResponse = $this->parseUser($parsedResponse); break; + case Response::MODEL_USER_LIST: + $listKey = 'users'; + $parsedResponse[$listKey] = array_map(fn ($content) => $this->parseUser($content), $parsedResponse[$listKey]); + break; case Response::MODEL_METRIC: $parsedResponse = $this->parseMetric($parsedResponse); break; case Response::MODEL_BUILD: - $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['startTime', 'endTime']); + $parsedResponse = $this->parseBuild($parsedResponse); + break; + case Response::MODEL_BUILD_LIST: + $listKey = 'builds'; + $parsedResponse[$listKey] = array_map(fn ($content) => $this->parseBuild($content), $parsedResponse[$listKey]); break; case Response::MODEL_BUCKET: $parsedResponse = $this->parseBucket($parsedResponse); break; + case Response::MODEL_BUCKET_LIST: + $listKey = 'buckets'; + $parsedResponse[$listKey] = array_map(fn ($content) => $this->parseBucket($content), $parsedResponse[$listKey]); + break; case Response::MODEL_COLLECTION: $parsedResponse = $this->parseCollection($parsedResponse); break; + case Response::MODEL_COLLECTION_LIST: + $listKey = 'collections'; + $parsedResponse[$listKey] = array_map(fn ($content) => $this->parseCollection($content), $parsedResponse[$listKey]); + break; + case Response::MODEL_DATABASE: case Response::MODEL_DEPLOYMENT: + case Response::MODEL_DOMAIN: + case Response::MODEL_PLATFORM: + case Response::MODEL_PROJECT: + case Response::MODEL_TEAM: + case Response::MODEL_WEBHOOK: + $parsedResponse = $this->parseCreatedAtUpdatedAt($parsedResponse); + break; + case Response::MODEL_DATABASE_LIST: + case Response::MODEL_DEPLOYMENT_LIST: + case Response::MODEL_DOMAIN_LIST: + case Response::MODEL_PLATFORM_LIST: + case Response::MODEL_PROJECT_LIST: + case Response::MODEL_TEAM_LIST: + case Response::MODEL_WEBHOOK_LIST: + $listKey = ''; + switch ($model) { + case Response::MODEL_DATABASE_LIST: + $listKey = 'databases'; + break; + case Response::MODEL_DEPLOYMENT_LIST: + $listKey = 'deployments'; + break; + case Response::MODEL_DOMAIN_LIST: + $listKey = 'domains'; + break; + case Response::MODEL_PLATFORM_LIST: + $listKey = 'platforms'; + break; + case Response::MODEL_PROJECT_LIST: + $listKey = 'projects'; + break; + case Response::MODEL_TEAM_LIST: + $listKey = 'teams'; + break; + case Response::MODEL_WEBHOOK_LIST: + $listKey = 'webhooks'; + break; + } + $parsedResponse[$listKey] = array_map(fn ($content) => $this->parseCreatedAtUpdatedAt($content), $parsedResponse[$listKey]); + break; case Response::MODEL_DOCUMENT: - $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['$createdAt', '$updatedAt']); + case Response::MODEL_FILE: + $parsedResponse = $this->parsePermissionsCreatedAtUpdatedAt($parsedResponse); + break; + case Response::MODEL_DOCUMENT_LIST: + case Response::MODEL_FILE_LIST: + $listKey = ''; + switch ($model) { + case Response::MODEL_DOCUMENT_LIST: + $listKey = 'documents'; + break; + case Response::MODEL_FILE_LIST: + $listKey = 'files'; + break; + } + $parsedResponse[$listKey] = array_map(fn ($content) => $this->parsePermissionsCreatedAtUpdatedAt($content), $parsedResponse[$listKey]); break; case Response::MODEL_EXECUTION: $parsedResponse = $this->parseExecution($parsedResponse); break; - case Response::MODEL_PLATFORM: - case Response::MODEL_PROJECT: - case Response::MODEL_TEAM: - case Response::MODEL_FILE: - case Response::MODEL_WEBHOOK: - case Response::MODEL_DOMAIN: - case Response::MODEL_DATABASE: - $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['$createdAt', '$updatedAt']); + case Response::MODEL_EXECUTION_LIST: + $listKey = 'executions'; + $parsedResponse[$listKey] = array_map(fn ($content) => $this->parseExecution($content), $parsedResponse[$listKey]); break; case Response::MODEL_FUNCTION: $parsedResponse = $this->parseFunction($parsedResponse); break; + case Response::MODEL_FUNCTION_LIST: + $listKey = 'functions'; + $parsedResponse[$listKey] = array_map(fn ($content) => $this->parseFunction($content), $parsedResponse[$listKey]); + break; case Response::MODEL_KEY: - $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['$createdAt', '$updatedAt', 'expire']); + $parsedResponse = $this->parseKey($parsedResponse); + break; + case Response::MODEL_KEY_LIST: + $listKey = 'keys'; + $parsedResponse[$listKey] = array_map(fn ($content) => $this->parseKey($content), $parsedResponse[$listKey]); break; case Response::MODEL_LOG: - $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['$createdAt', '$updatedAt', 'time']); + $parsedResponse = $this->parseLog($parsedResponse); + break; + case Response::MODEL_LOG_LIST: + $listKey = 'logs'; + $parsedResponse[$listKey] = array_map(fn ($content) => $this->parseLog($content), $parsedResponse[$listKey]); break; case Response::MODEL_MEMBERSHIP: - $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['$createdAt', '$updatedAt', 'invited', 'joined']); + $parsedResponse = $this->parseMembership($parsedResponse); + break; + case Response::MODEL_MEMBERSHIP_LIST: + $listKey = 'memberships'; + $parsedResponse[$listKey] = array_map(fn ($content) => $this->parseMembership($content), $parsedResponse[$listKey]); break; case Response::MODEL_SESSION: - $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['$createdAt', 'expire', 'providerAccessTokenExpiry']); + $parsedResponse = $this->parseSession($parsedResponse); + break; + case Response::MODEL_SESSION_LIST: + $listKey = 'sessions'; + $parsedResponse[$listKey] = array_map(fn ($content) => $this->parseSession($content), $parsedResponse[$listKey]); break; case Response::MODEL_TOKEN: $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['$createdAt', 'expire']); @@ -77,12 +163,16 @@ class V15 extends Filter break; } - // Downgrade Permissions for all models - $parsedResponse = $this->parsePermissions($parsedResponse); - return $parsedResponse; } + protected function parseBuild(array $content) + { + $content = $this->parseDatetimeAttributes($content, ['startTime', 'endTime']); + + return $content; + } + protected function parseBucket(array $content) { if (isset($content['fileSecurity'])) { @@ -96,6 +186,7 @@ class V15 extends Filter unset($content['fileSecurity']); unset($content['compression']); + $content = $this->parsePermissions($content); $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt']); return $content; @@ -196,6 +287,14 @@ class V15 extends Filter } unset($content['documentSecurity']); + $content = $this->parsePermissions($content); + $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt']); + return $content; + } + + protected function parsePermissionsCreatedAtUpdatedAt(array $content) + { + $content = $this->parsePermissions($content); $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt']); return $content; } @@ -203,10 +302,17 @@ class V15 extends Filter private function parseExecution($content) { unset($content['stdout']); + $content = $this->parsePermissions($content); $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'startTime', 'endTime']); return $content; } + private function parseCreatedAtUpdatedAt($content) + { + $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt']); + return $content; + } + private function parseFunction($content) { if (isset($content['execute'])) { @@ -227,6 +333,30 @@ class V15 extends Filter return $content; } + private function parseKey($content) + { + $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'expire']); + return $content; + } + + private function parseLog($content) + { + $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'time']); + return $content; + } + + private function parseMembership($content) + { + $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'invited', 'joined']); + return $content; + } + + private function parseSession($content) + { + $content = $this->parseDatetimeAttributes($content, ['$createdAt', 'expire', 'providerAccessTokenExpiry']); + return $content; + } + private function parseModelUsageFunc($content) { $mapping = [ diff --git a/tests/unit/Utopia/Response/Filters/V15Test.php b/tests/unit/Utopia/Response/Filters/V15Test.php index 48754e8dca..092fe0b5c6 100644 --- a/tests/unit/Utopia/Response/Filters/V15Test.php +++ b/tests/unit/Utopia/Response/Filters/V15Test.php @@ -7,6 +7,7 @@ use Appwrite\Utopia\Response; use Utopia\Database\Permission; use Utopia\Database\Role; use PHPUnit\Framework\TestCase; +use stdClass; class V15Test extends TestCase { @@ -240,6 +241,49 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider permissionsProvider + * @dataProvider bucketProvider + */ + public function testBucketList(array $content, array $expected): void + { + $model = Response::MODEL_BUCKET_LIST; + + $content = [ + 'buckets' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'buckets' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function buildProvider(): array + { + return [ + 'build start and end time' => [ + [ + 'startTime' => '2020-06-24T06:47:30.000Z', + 'endTime' => '2020-06-24T06:47:30.000Z', + ], + [ + 'startTime' => 1592981250, + 'endTime' => 1592981250, + ] + ] + ]; + } + + /** + * @dataProvider buildProvider + */ public function testBuild(): void { $model = Response::MODEL_BUILD; @@ -259,6 +303,28 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider buildProvider + */ + public function testBuildList(array $content, array $expected): void + { + $model = Response::MODEL_BUILD_LIST; + + $content = [ + 'builds' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'builds' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + public function collectionProvider(): array { return [ @@ -344,6 +410,30 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider permissionsProvider + * @dataProvider collectionProvider + */ + public function testCollectionList(array $content, array $expected): void + { + $model = Response::MODEL_COLLECTION_LIST; + + $content = [ + 'collections' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'collections' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + /** * @dataProvider createdAtUpdatedAtProvider */ @@ -356,6 +446,28 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider createdAtUpdatedAtProvider + */ + public function testDatabaseList(array $content, array $expected): void + { + $model = Response::MODEL_DATABASE_LIST; + + $content = [ + 'databases' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'databases' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + /** * @dataProvider createdAtUpdatedAtProvider */ @@ -368,6 +480,28 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider createdAtUpdatedAtProvider + */ + public function testDeploymentList(array $content, array $expected): void + { + $model = Response::MODEL_DEPLOYMENT_LIST; + + $content = [ + 'deployments' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'deployments' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + /** * @dataProvider createdAtUpdatedAtProvider * @dataProvider permissionsProvider @@ -381,6 +515,29 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider permissionsProvider + */ + public function testDocumentList(array $content, array $expected): void + { + $model = Response::MODEL_DOCUMENT_LIST; + + $content = [ + 'documents' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'documents' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + /** * @dataProvider createdAtUpdatedAtProvider */ @@ -393,6 +550,28 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider createdAtUpdatedAtProvider + */ + public function testDomainList(array $content, array $expected): void + { + $model = Response::MODEL_DOMAIN_LIST; + + $content = [ + 'domains' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'domains' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + public function executionProvider(): array { return [ @@ -417,6 +596,30 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider permissionsProvider + * @dataProvider executionProvider + */ + public function testExecutionList(array $content, array $expected): void + { + $model = Response::MODEL_EXECUTION_LIST; + + $content = [ + 'executions' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'executions' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + /** * @dataProvider createdAtUpdatedAtProvider * @dataProvider permissionsProvider @@ -430,6 +633,29 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider permissionsProvider + */ + public function testFileList(array $content, array $expected): void + { + $model = Response::MODEL_FILE_LIST; + + $content = [ + 'files' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'files' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + public function functionProvider(): array { return [ @@ -502,6 +728,29 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider functionProvider + */ + public function testFuncList(array $content, array $expected): void + { + $model = Response::MODEL_FUNCTION_LIST; + + $content = [ + 'functions' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'functions' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + public function keyProvider(): array { return [ @@ -525,6 +774,29 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider keyProvider + */ + public function testKeyList(array $content, array $expected): void + { + $model = Response::MODEL_KEY_LIST; + + $content = [ + 'keys' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'keys' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + public function logProvider(): array { return [ @@ -548,6 +820,29 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider logProvider + */ + public function testLogList(array $content, array $expected): void + { + $model = Response::MODEL_LOG_LIST; + + $content = [ + 'logs' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'logs' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + public function membershipProvider(): array { return [ @@ -577,6 +872,29 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider membershipProvider + */ + public function testMembershipList(array $content, array $expected): void + { + $model = Response::MODEL_MEMBERSHIP_LIST; + + $content = [ + 'memberships' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'memberships' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + public function metricProvider(): array { return [ @@ -615,6 +933,28 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider createdAtUpdatedAtProvider + */ + public function testPlatformList(array $content, array $expected): void + { + $model = Response::MODEL_PLATFORM_LIST; + + $content = [ + 'platforms' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'platforms' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + /** * @dataProvider createdAtUpdatedAtProvider */ @@ -627,6 +967,28 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider createdAtUpdatedAtProvider + */ + public function testProjectList(array $content, array $expected): void + { + $model = Response::MODEL_PROJECT_LIST; + + $content = [ + 'projects' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'projects' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + public function sessionProvider(): array { return [ @@ -657,6 +1019,28 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider sessionProvider + */ + public function testSessionList(array $content, array $expected): void + { + $model = Response::MODEL_SESSION_LIST; + + $content = [ + 'sessions' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'sessions' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + /** * @dataProvider createdAtUpdatedAtProvider */ @@ -669,6 +1053,28 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + /** + * @dataProvider createdAtUpdatedAtProvider + */ + public function testTeamList(array $content, array $expected): void + { + $model = Response::MODEL_TEAM_LIST; + + $content = [ + 'teams' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'teams' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + public function tokenProvider(): array { return [ @@ -909,4 +1315,117 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + + public function userProvider(): array + { + return [ + 'basic user' => [ + [ + '$id' => '5e5ea5c16897e', + '$createdAt' => '2020-06-24T06:47:30.000Z', + '$updatedAt' => '2020-06-24T06:47:30.000Z', + 'name' => 'John Doe', + 'password' => '$argon2id$v=19$m=2048,t=4,p=3$aUZjLnliVWRINmFNTWMudg$5S+x+7uA31xFnrHFT47yFwcJeaP0w92L/4LdgrVRXxE', + 'hash' => 'argon2', + 'hashOptions' => [ + 'memoryCost' => 65536, + 'timeCost' => 4, + 'threads' => 3, + ], + 'registration' => '2020-06-24T06:47:30.000Z', + 'status' => true, + 'passwordUpdate' => '2020-06-24T06:47:30.000Z', + 'email' => 'john@appwrite.io', + 'phone' => '+4930901820', + 'emailVerification' => true, + 'phoneVerification' => true, + 'prefs' => new \stdClass(), + ], + [ + '$id' => '5e5ea5c16897e', + '$createdAt' => 1592981250, + '$updatedAt' => 1592981250, + 'name' => 'John Doe', + 'registration' => 1592981250, + 'status' => true, + 'passwordUpdate' => 1592981250, + 'email' => 'john@appwrite.io', + 'phone' => '+4930901820', + 'emailVerification' => true, + 'phoneVerification' => true, + 'prefs' => new \stdClass(), + ], + ], + ]; + } + + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider userProvider + */ + public function testUser(array $content, array $expected): void + { + $model = Response::MODEL_USER; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider createdAtUpdatedAtProvider + * @dataProvider userProvider + */ + public function testUserList(array $content, array $expected): void + { + $model = Response::MODEL_USER_LIST; + + $content = [ + 'users' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'users' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider createdAtUpdatedAtProvider + */ + public function testWebhook(array $content, array $expected): void + { + $model = Response::MODEL_WEBHOOK; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider createdAtUpdatedAtProvider + */ + public function testWebhookList(array $content, array $expected): void + { + $model = Response::MODEL_WEBHOOK_LIST; + + $content = [ + 'webhooks' => [$content], + 'total' => 1, + ]; + + $expected = [ + 'webhooks' => [$expected], + 'total' => 1, + ]; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } } From 1d7c25354a88e99bcfab65a88df0b4c3656bd867 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Mon, 12 Sep 2022 22:15:13 +0000 Subject: [PATCH 24/30] Update response filters for usage --- src/Appwrite/Utopia/Response/Filters/V15.php | 146 ++++++- .../unit/Utopia/Response/Filters/V15Test.php | 384 ++++++++++++++++++ 2 files changed, 512 insertions(+), 18 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Filters/V15.php b/src/Appwrite/Utopia/Response/Filters/V15.php index 047eb1d268..c927dd8605 100644 --- a/src/Appwrite/Utopia/Response/Filters/V15.php +++ b/src/Appwrite/Utopia/Response/Filters/V15.php @@ -152,8 +152,23 @@ class V15 extends Filter case Response::MODEL_TOKEN: $parsedResponse = $this->parseDatetimeAttributes($parsedResponse, ['$createdAt', 'expire']); break; + case Response::MODEL_USAGE_DATABASES: + $parsedResponse = $this->parseUsageDatabases($parsedResponse); + break; + case Response::MODEL_USAGE_DATABASE: + $parsedResponse = $this->parseUsageDatabase($parsedResponse); + break; + case Response::MODEL_USAGE_COLLECTION: + $parsedResponse = $this->parseUsageCollection($parsedResponse); + break; + case Response::MODEL_USAGE_USERS: + $parsedResponse = $this->parseUsageUsers($parsedResponse); + break; + case Response::MODEL_USAGE_BUCKETS: + $parsedResponse = $this->parseUsageBuckets($parsedResponse); + break; case Response::MODEL_USAGE_FUNCTIONS: - $parsedResponse = $this->parseModelUsageFunc($parsedResponse); + $parsedResponse = $this->parseUsageFuncs($parsedResponse); break; case Response::MODEL_USAGE_PROJECT: $parsedResponse = $this->parseUsageProject($parsedResponse); @@ -357,7 +372,114 @@ class V15 extends Filter return $content; } - private function parseModelUsageFunc($content) + private function parseUsage($content, $keys) + { + foreach ($keys as $key) { + $data = []; + foreach ($content[$key] as $metric) { + $data[] = $this->parseMetric($metric); + } + $content[$key] = $data; + } + + return $content; + } + + private function parseUsageDatabases($content) + { + $keys = [ + 'databasesCount', + 'documentsCount', + 'collectionsCount', + 'databasesCreate', + 'databasesRead', + 'databasesUpdate', + 'databasesDelete', + 'documentsCreate', + 'documentsRead', + 'documentsUpdate', + 'documentsDelete', + 'collectionsCreate', + 'collectionsRead', + 'collectionsUpdate', + 'collectionsDelete', + ]; + + $content = $this->parseUsage($content, $keys); + + return $content; + } + + private function parseUsageDatabase($content) + { + $keys = [ + 'documentsCount', + 'collectionsCount', + 'documentsCreate', + 'documentsRead', + 'documentsUpdate', + 'documentsDelete', + 'collectionsCreate', + 'collectionsRead', + 'collectionsUpdate', + 'collectionsDelete', + ]; + + $content = $this->parseUsage($content, $keys); + + return $content; + } + + private function parseUsageCollection($content) + { + $keys = [ + 'documentsCount', + 'documentsCreate', + 'documentsRead', + 'documentsUpdate', + 'documentsDelete', + ]; + + $content = $this->parseUsage($content, $keys); + + return $content; + } + + private function parseUsageUsers($content) + { + $keys = [ + 'usersCount', + 'usersCreate', + 'usersRead', + 'usersUpdate', + 'usersDelete', + 'sessionsCreate', + 'sessionsProviderCreate', + 'sessionsDelete', + ]; + + $content = $this->parseUsage($content, $keys); + + return $content; + } + + private function parseUsageBuckets($content) + { + $keys = [ + 'filesCount', + 'filesStorage', + 'filesCreate', + 'filesRead', + 'filesUpdate', + 'filesDelete', + ]; + + $content = $this->parseUsage($content, $keys); + + return $content; + } + + private function parseUsageFuncs($content) { $mapping = [ 'executionsTotal' => 'functionsExecutions', @@ -393,7 +515,7 @@ class V15 extends Filter $content['functions'] = $content['executions']; unset($content['executions']); - $usage = [ + $keys = [ 'collections', 'documents', 'functions', @@ -403,13 +525,7 @@ class V15 extends Filter 'users', ]; - foreach ($usage as $name) { - $data = []; - foreach ($content[$name] as $metric) { - $data[] = $this->parseMetric($metric); - } - $content[$name] = $data; - } + $content = $this->parseUsage($content, $keys); return $content; } @@ -419,7 +535,7 @@ class V15 extends Filter $content['filesStorage'] = $content['storage']; unset($content['storage']); - $usage = [ + $keys = [ 'bucketsCount', 'bucketsCreate', 'bucketsDelete', @@ -433,13 +549,7 @@ class V15 extends Filter 'filesUpdate', ]; - foreach ($usage as $name) { - $data = []; - foreach ($content[$name] as $metric) { - $data[] = $this->parseMetric($metric); - } - $content[$name] = $data; - } + $content = $this->parseUsage($content, $keys); return $content; } diff --git a/tests/unit/Utopia/Response/Filters/V15Test.php b/tests/unit/Utopia/Response/Filters/V15Test.php index 092fe0b5c6..dbec42c341 100644 --- a/tests/unit/Utopia/Response/Filters/V15Test.php +++ b/tests/unit/Utopia/Response/Filters/V15Test.php @@ -1103,6 +1103,390 @@ class V15Test extends TestCase $this->assertEquals($expected, $result); } + public function usageDatabasesProvider(): array + { + return [ + 'basic usage databases' => [ + [ + 'databasesCount' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'documentsCount' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'collectionsCount' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'databasesCreate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'databasesRead' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'databasesUpdate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'databasesDelete' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'documentsCreate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'documentsRead' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'documentsUpdate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'documentsDelete' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'collectionsCreate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'collectionsRead' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'collectionsUpdate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'collectionsDelete' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + ], + [ + 'databasesCount' => [ + ['date' => 1592981250], + ], + 'documentsCount' => [ + ['date' => 1592981250], + ], + 'collectionsCount' => [ + ['date' => 1592981250], + ], + 'databasesCreate' => [ + ['date' => 1592981250], + ], + 'databasesRead' => [ + ['date' => 1592981250], + ], + 'databasesUpdate' => [ + ['date' => 1592981250], + ], + 'databasesDelete' => [ + ['date' => 1592981250], + ], + 'documentsCreate' => [ + ['date' => 1592981250], + ], + 'documentsRead' => [ + ['date' => 1592981250], + ], + 'documentsUpdate' => [ + ['date' => 1592981250], + ], + 'documentsDelete' => [ + ['date' => 1592981250], + ], + 'collectionsCreate' => [ + ['date' => 1592981250], + ], + 'collectionsRead' => [ + ['date' => 1592981250], + ], + 'collectionsUpdate' => [ + ['date' => 1592981250], + ], + 'collectionsDelete' => [ + ['date' => 1592981250], + ], + ], + ], + ]; + } + + /** + * @dataProvider usageDatabasesProvider + */ + public function testUsageDatabases(array $content, array $expected): void + { + $model = Response::MODEL_USAGE_DATABASES; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function usageDatabaseProvider(): array + { + return [ + 'basic usage database' => [ + [ + 'documentsCount' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'collectionsCount' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'documentsCreate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'documentsRead' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'documentsUpdate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'documentsDelete' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'collectionsCreate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'collectionsRead' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'collectionsUpdate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'collectionsDelete' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + ], + [ + 'documentsCount' => [ + ['date' => 1592981250], + ], + 'collectionsCount' => [ + ['date' => 1592981250], + ], + 'documentsCreate' => [ + ['date' => 1592981250], + ], + 'documentsRead' => [ + ['date' => 1592981250], + ], + 'documentsUpdate' => [ + ['date' => 1592981250], + ], + 'documentsDelete' => [ + ['date' => 1592981250], + ], + 'collectionsCreate' => [ + ['date' => 1592981250], + ], + 'collectionsRead' => [ + ['date' => 1592981250], + ], + 'collectionsUpdate' => [ + ['date' => 1592981250], + ], + 'collectionsDelete' => [ + ['date' => 1592981250], + ], + ], + ], + ]; + } + + /** + * @dataProvider usageDatabaseProvider + */ + public function testUsageDatabase(array $content, array $expected): void + { + $model = Response::MODEL_USAGE_DATABASE; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function usageCollectionProvider(): array + { + return [ + 'basic usage collection' => [ + [ + 'documentsCount' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'documentsCreate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'documentsRead' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'documentsUpdate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'documentsDelete' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + ], + [ + 'documentsCount' => [ + ['date' => 1592981250], + ], + 'documentsCreate' => [ + ['date' => 1592981250], + ], + 'documentsRead' => [ + ['date' => 1592981250], + ], + 'documentsUpdate' => [ + ['date' => 1592981250], + ], + 'documentsDelete' => [ + ['date' => 1592981250], + ], + ], + ], + ]; + } + + /** + * @dataProvider usageCollectionProvider + */ + public function testUsageCollection(array $content, array $expected): void + { + $model = Response::MODEL_USAGE_COLLECTION; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function usageUsersProvider(): array + { + return [ + 'basic usage users' => [ + [ + 'usersCount' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'usersCreate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'usersRead' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'usersUpdate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'usersDelete' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'sessionsCreate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'sessionsProviderCreate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'sessionsDelete' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + ], + [ + 'usersCount' => [ + ['date' => 1592981250], + ], + 'usersCreate' => [ + ['date' => 1592981250], + ], + 'usersRead' => [ + ['date' => 1592981250], + ], + 'usersUpdate' => [ + ['date' => 1592981250], + ], + 'usersDelete' => [ + ['date' => 1592981250], + ], + 'sessionsCreate' => [ + ['date' => 1592981250], + ], + 'sessionsProviderCreate' => [ + ['date' => 1592981250], + ], + 'sessionsDelete' => [ + ['date' => 1592981250], + ], + ], + ], + ]; + } + + /** + * @dataProvider usageUsersProvider + */ + public function testUsageUsers(array $content, array $expected): void + { + $model = Response::MODEL_USAGE_USERS; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + + public function usageBucketsProvider(): array + { + return [ + 'basic usage buckets' => [ + [ + 'filesCount' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'filesStorage' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'filesCreate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'filesRead' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'filesUpdate' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + 'filesDelete' => [ + ['date' => '2020-06-24T06:47:30.000Z'], + ], + ], + [ + 'filesCount' => [ + ['date' => 1592981250], + ], + 'filesStorage' => [ + ['date' => 1592981250], + ], + 'filesCreate' => [ + ['date' => 1592981250], + ], + 'filesRead' => [ + ['date' => 1592981250], + ], + 'filesUpdate' => [ + ['date' => 1592981250], + ], + 'filesDelete' => [ + ['date' => 1592981250], + ], + ], + ], + ]; + } + + /** + * @dataProvider usageBucketsProvider + */ + public function testUsageBuckets(array $content, array $expected): void + { + $model = Response::MODEL_USAGE_BUCKETS; + + $result = $this->filter->parse($content, $model); + + $this->assertEquals($expected, $result); + } + public function usageFunctionsProvider(): array { return [ From f6868318e06d0b781ec33a3a8e05ae3bdc4da7fe Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Tue, 13 Sep 2022 21:54:57 +0530 Subject: [PATCH 25/30] Update docs/sdks/apple/GETTING_STARTED.md Co-authored-by: Jake Barnby --- docs/sdks/apple/GETTING_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdks/apple/GETTING_STARTED.md b/docs/sdks/apple/GETTING_STARTED.md index de2f233189..1da041112c 100644 --- a/docs/sdks/apple/GETTING_STARTED.md +++ b/docs/sdks/apple/GETTING_STARTED.md @@ -122,7 +122,7 @@ func main() { let account = Account(client) do { - let account = try await account.get() + let user = try await account.get() print(String(describing: user.toMap())) } catch { print(error.localizedDescription) From eca5aa3c2bba4b624752befe76af96b3fee8ea4d Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Tue, 13 Sep 2022 20:42:45 +0000 Subject: [PATCH 26/30] Fix version matching for 0.15.X request & response --- app/controllers/general.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 09a0415f90..e56357869d 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -70,7 +70,7 @@ App::init() case version_compare($requestFormat, '0.14.0', '<'): Request::setFilter(new RequestV14()); break; - case version_compare($requestFormat, '0.15.0', '<'): + case version_compare($requestFormat, '0.15.3', '<'): Request::setFilter(new RequestV15()); break; default: @@ -199,7 +199,7 @@ App::init() case version_compare($responseFormat, '0.14.0', '<='): Response::setFilter(new ResponseV14()); break; - case version_compare($responseFormat, '0.15.0', '<='): + case version_compare($responseFormat, '0.15.3', '<='): Response::setFilter(new ResponseV15()); break; default: From 717abccd10a334cd259e85cf0024ca2a0585c064 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Tue, 13 Sep 2022 20:43:46 +0000 Subject: [PATCH 27/30] Fix V15 response format for functions and executions --- src/Appwrite/Utopia/Response/Filters/V15.php | 25 ++++++++++- .../unit/Utopia/Response/Filters/V15Test.php | 41 ++++++++++++++++--- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Filters/V15.php b/src/Appwrite/Utopia/Response/Filters/V15.php index c927dd8605..8c56e65e2a 100644 --- a/src/Appwrite/Utopia/Response/Filters/V15.php +++ b/src/Appwrite/Utopia/Response/Filters/V15.php @@ -317,8 +317,23 @@ class V15 extends Filter private function parseExecution($content) { unset($content['stdout']); - $content = $this->parsePermissions($content); - $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'startTime', 'endTime']); + + if (isset($content['$permissions'])) { + $read = []; + foreach ($content['$permissions'] as $role) { + $read[] = $this->parseRole($role); + } + $content['$read'] = $read; + unset($content['$permissions']); + } + + if (isset($content['duration'])) { + $content['time'] = $content['duration']; + unset($content['duration']); + } + + $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt']); + return $content; } @@ -344,7 +359,13 @@ class V15 extends Filter $content['vars'] = $vars; } + if (isset($content['enabled'])) { + $content['status'] = $content['enabled'] ? 'enabled' : 'disabled'; + unset($content['enabled']); + } + $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'scheduleNext', 'schedulePrevious']); + return $content; } diff --git a/tests/unit/Utopia/Response/Filters/V15Test.php b/tests/unit/Utopia/Response/Filters/V15Test.php index dbec42c341..8998c759b1 100644 --- a/tests/unit/Utopia/Response/Filters/V15Test.php +++ b/tests/unit/Utopia/Response/Filters/V15Test.php @@ -576,15 +576,43 @@ class V15Test extends TestCase { return [ 'basic execution' => [ - ['stdout' => ''], - [], + [ + '$id' => '5e5ea5c16897e', + '$createdAt' => '2020-06-24T06:47:30.000Z', + '$updatedAt' => '2020-06-24T06:47:30.000Z', + '$permissions' => [ + "any" + ], + 'functionId' => '5e5ea6g16897e', + 'trigger' => 'http', + 'status' => 'processing', + 'statusCode' => 0, + 'response' => '', + 'stdout' => '', + 'stderr' => '', + 'duration' => 0.4 + ], + [ + '$id' => '5e5ea5c16897e', + '$createdAt' => 1592981250, + '$updatedAt' => 1592981250, + '$read' => [ + "role:all" + ], + 'functionId' => '5e5ea6g16897e', + 'trigger' => 'http', + 'status' => 'processing', + 'statusCode' => 0, + 'response' => '', + 'stderr' => '', + 'time' => 0.4 + ], ], ]; } /** * @dataProvider createdAtUpdatedAtProvider - * @dataProvider permissionsProvider * @dataProvider executionProvider */ public function testExecution(array $content, array $expected): void @@ -598,7 +626,6 @@ class V15Test extends TestCase /** * @dataProvider createdAtUpdatedAtProvider - * @dataProvider permissionsProvider * @dataProvider executionProvider */ public function testExecutionList(array $content, array $expected): void @@ -668,7 +695,7 @@ class V15Test extends TestCase Role::users()->toString(), ], 'name' => 'My Function', - 'status' => 'enabled', + 'enabled' => true, 'runtime' => 'python-3.8', 'deployment' => '5e5ea5c16897e', 'vars' => [ @@ -712,6 +739,10 @@ class V15Test extends TestCase 'timeout' => 1592981237 ], ], + 'enabled false' => [ + ['enabled' => false], + ['status' => 'disabled'], + ], ]; } From 8f67743834de28659c98e661578b6bc7ae5a12cc Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Tue, 13 Sep 2022 20:46:32 +0000 Subject: [PATCH 28/30] Rename teams.getMemberships to teams.listMemberships in V15 request filter --- src/Appwrite/Utopia/Request/Filters/V15.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Utopia/Request/Filters/V15.php b/src/Appwrite/Utopia/Request/Filters/V15.php index d9e6df8be3..7badac23b7 100644 --- a/src/Appwrite/Utopia/Request/Filters/V15.php +++ b/src/Appwrite/Utopia/Request/Filters/V15.php @@ -33,7 +33,7 @@ class V15 extends Filter case 'storage.listBuckets': case 'storage.listFiles': case 'teams.list': - case 'teams.getMemberships': + case 'teams.listMemberships': case 'users.list': $content = $this->convertLimitAndOffset($content); $content = $this->convertCursor($content); From 2da7da0558bcdbab9855a926aba7ce2fd1b0d2bb Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Tue, 13 Sep 2022 21:18:13 +0000 Subject: [PATCH 29/30] Remove response parse of log $createdAt and $updatedAt --- src/Appwrite/Utopia/Response/Filters/V15.php | 2 +- .../unit/Utopia/Response/Filters/V15Test.php | 50 +++++++++++++++++-- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Utopia/Response/Filters/V15.php b/src/Appwrite/Utopia/Response/Filters/V15.php index 8c56e65e2a..8e688d58ff 100644 --- a/src/Appwrite/Utopia/Response/Filters/V15.php +++ b/src/Appwrite/Utopia/Response/Filters/V15.php @@ -377,7 +377,7 @@ class V15 extends Filter private function parseLog($content) { - $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'time']); + $content = $this->parseDatetimeAttributes($content, ['time']); return $content; } diff --git a/tests/unit/Utopia/Response/Filters/V15Test.php b/tests/unit/Utopia/Response/Filters/V15Test.php index 8998c759b1..a68da474d9 100644 --- a/tests/unit/Utopia/Response/Filters/V15Test.php +++ b/tests/unit/Utopia/Response/Filters/V15Test.php @@ -832,14 +832,57 @@ class V15Test extends TestCase { return [ 'basic log' => [ - ['time' => '2020-06-24T06:47:30.000Z'], - ['time' => 1592981250], + [ + 'event' => 'account.sessions.create', + 'userId' => '610fc2f985ee0', + 'userEmail' => 'john@appwrite.io', + 'userName' => 'John Doe', + 'mode' => 'admin', + 'ip' => '127.0.0.1', + 'time' => '2020-06-24T06:47:30.000Z', + 'osCode' => 'Mac', + 'osName' => 'Mac', + 'osVersion' => 'Mac', + 'clientType' => 'browser', + 'clientCode' => 'CM', + 'clientName' => 'Chrome Mobile iOS', + 'clientVersion' => '84.0', + 'clientEngine' => 'WebKit', + 'clientEngineVersion' => '605.1.15', + 'deviceName' => 'smartphone', + 'deviceBrand' => 'Google', + 'deviceModel' => 'Nexus 5', + 'countryCode' => 'US', + 'countryName' => 'United States' + ], + [ + 'event' => 'account.sessions.create', + 'userId' => '610fc2f985ee0', + 'userEmail' => 'john@appwrite.io', + 'userName' => 'John Doe', + 'mode' => 'admin', + 'ip' => '127.0.0.1', + 'time' => 1592981250, + 'osCode' => 'Mac', + 'osName' => 'Mac', + 'osVersion' => 'Mac', + 'clientType' => 'browser', + 'clientCode' => 'CM', + 'clientName' => 'Chrome Mobile iOS', + 'clientVersion' => '84.0', + 'clientEngine' => 'WebKit', + 'clientEngineVersion' => '605.1.15', + 'deviceName' => 'smartphone', + 'deviceBrand' => 'Google', + 'deviceModel' => 'Nexus 5', + 'countryCode' => 'US', + 'countryName' => 'United States' + ] ], ]; } /** - * @dataProvider createdAtUpdatedAtProvider * @dataProvider logProvider */ public function testLog(array $content, array $expected): void @@ -852,7 +895,6 @@ class V15Test extends TestCase } /** - * @dataProvider createdAtUpdatedAtProvider * @dataProvider logProvider */ public function testLogList(array $content, array $expected): void From 041066520f455e5ce728750919fd879a18a8401d Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Tue, 13 Sep 2022 21:28:23 +0000 Subject: [PATCH 30/30] Fix some sdk methods for V15 request filter --- src/Appwrite/Utopia/Request/Filters/V15.php | 4 ++-- tests/unit/Utopia/Request/Filters/V15Test.php | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Appwrite/Utopia/Request/Filters/V15.php b/src/Appwrite/Utopia/Request/Filters/V15.php index 7badac23b7..65b09e1f24 100644 --- a/src/Appwrite/Utopia/Request/Filters/V15.php +++ b/src/Appwrite/Utopia/Request/Filters/V15.php @@ -14,12 +14,12 @@ class V15 extends Filter public function parse(array $content, string $model): array { switch ($model) { - case 'account.logs': + case 'account.listLogs': case 'databases.listLogs': case 'databases.listCollectionLogs': case 'databases.listDocumentLogs': case 'teams.listLogs': - case 'users.getLogs': + case 'users.listLogs': $content = $this->convertLimitAndOffset($content); break; case 'account.initials': diff --git a/tests/unit/Utopia/Request/Filters/V15Test.php b/tests/unit/Utopia/Request/Filters/V15Test.php index 1617c2b0c5..70bf71d4cb 100644 --- a/tests/unit/Utopia/Request/Filters/V15Test.php +++ b/tests/unit/Utopia/Request/Filters/V15Test.php @@ -36,9 +36,9 @@ class V15Test extends TestCase /** * @dataProvider limitOffsetProvider */ - public function testGetAccountLogs(array $content, array $expected): void + public function testListAccountLogs(array $content, array $expected): void { - $model = 'account.logs'; + $model = 'account.listLogs'; $result = $this->filter->parse($content, $model); @@ -692,9 +692,9 @@ class V15Test extends TestCase * @dataProvider cursorProvider * @dataProvider orderTypeProvider */ - public function testGetTeamMemberships(array $content, array $expected): void + public function testListTeamMemberships(array $content, array $expected): void { - $model = 'teams.getMemberships'; + $model = 'teams.listMemberships'; $result = $this->filter->parse($content, $model); @@ -731,9 +731,9 @@ class V15Test extends TestCase /** * @dataProvider limitOffsetProvider */ - public function testGetUserLogs(array $content, array $expected): void + public function testListUserLogs(array $content, array $expected): void { - $model = 'users.getLogs'; + $model = 'users.listLogs'; $result = $this->filter->parse($content, $model);