diff --git a/CHANGES.md b/CHANGES.md
index bd23456d84..d11186427b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -10,6 +10,7 @@
- Queries have been improved to allow even more flexibility, and introduced to new endpoints. See the Queries V2 section in the document for more information [#3702](https://github.com/appwrite/appwrite/pull/3702)
- Compound indexes are now more flexible [#151](https://github.com/utopia-php/database/pull/151)
- `createExecution` parameter `async` default value was changed from `true` to `false` [#3781](https://github.com/appwrite/appwrite/pull/3781)
+- String attribute `status` has been refactored to a Boolean attribute `enabled` in the functions collection [#3798](https://github.com/appwrite/appwrite/pull/3798)
- `time` attribute in Execution response model has been reanamed to `duration` to be more consistent with other response models. [#3801](https://github.com/appwrite/appwrite/pull/3801)
## Features
diff --git a/app/config/collections.php b/app/config/collections.php
index fbc00feb91..0c8108fb58 100644
--- a/app/config/collections.php
+++ b/app/config/collections.php
@@ -2084,15 +2084,14 @@ $collections = [
'filters' => [],
],
[
- 'array' => false,
- '$id' => ID::custom('status'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
+ '$id' => ID::custom('enabled'),
+ 'type' => Database::VAR_BOOLEAN,
'signed' => true,
- 'required' => false,
- 'default' => null,
+ 'size' => 0,
+ 'format' => '',
'filters' => [],
+ 'required' => true,
+ 'array' => false,
],
[
'$id' => ID::custom('runtime'),
@@ -2210,10 +2209,10 @@ $collections = [
'orders' => [Database::ORDER_ASC],
],
[
- '$id' => ID::custom('_key_status'),
+ '$id' => ID::custom('_key_enabled'),
'type' => Database::INDEX_KEY,
- 'attributes' => ['status'],
- 'lengths' => [Database::LENGTH_KEY],
+ 'attributes' => ['enabled'],
+ 'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php
index eeec83e069..88fc37e934 100644
--- a/app/controllers/api/account.php
+++ b/app/controllers/api/account.php
@@ -53,6 +53,7 @@ App::post('/v1/account')
->label('event', 'users.[userId].create')
->label('scope', 'public')
->label('auth.type', 'emailPassword')
+ ->label('audits.event', 'user.create')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
->label('usage.metric', 'users.{scope}.requests.create')
@@ -146,6 +147,7 @@ App::post('/v1/account/sessions/email')
->label('event', 'users.[userId].sessions.[sessionId].create')
->label('scope', 'public')
->label('auth.type', 'emailPassword')
+ ->label('audits.event', 'session.create')
->label('audits.resource', 'user/{response.userId}')
->label('audits.userId', '{response.userId}')
->label('usage.metric', 'sessions.{scope}.requests.create')
@@ -367,6 +369,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('event', 'users.[userId].sessions.[sessionId].create')
->label('scope', 'public')
+ ->label('audits.event', 'session.create')
->label('audits.resource', 'user/{user.$id}')
->label('abuse-limit', 50)
->label('abuse-key', 'ip:{ip}')
@@ -605,6 +608,7 @@ App::post('/v1/account/sessions/magic-url')
->groups(['api', 'account'])
->label('scope', 'public')
->label('auth.type', 'magic-url')
+ ->label('audits.event', 'session.create')
->label('audits.resource', 'user/{response.userId}')
->label('audits.userId', '{response.userId}')
->label('sdk.auth', [])
@@ -737,6 +741,7 @@ App::put('/v1/account/sessions/magic-url')
->groups(['api', 'account'])
->label('scope', 'public')
->label('event', 'users.[userId].sessions.[sessionId].create')
+ ->label('audits.event', 'session.update')
->label('audits.resource', 'user/{response.userId}')
->label('audits.userId', '{response.userId}')
->label('usage.metric', 'sessions.{scope}.requests.create')
@@ -856,6 +861,7 @@ App::post('/v1/account/sessions/phone')
->groups(['api', 'account'])
->label('scope', 'public')
->label('auth.type', 'phone')
+ ->label('audits.event', 'session.create')
->label('audits.resource', 'user/{response.userId}')
->label('audits.userId', '{response.userId}')
->label('sdk.auth', [])
@@ -1089,6 +1095,7 @@ App::post('/v1/account/sessions/anonymous')
->label('event', 'users.[userId].sessions.[sessionId].create')
->label('scope', 'public')
->label('auth.type', 'anonymous')
+ ->label('audits.event', 'session.create')
->label('audits.resource', 'user/{response.userId}')
->label('audits.userId', '{response.userId}')
->label('usage.metric', 'sessions.{scope}.requests.create')
@@ -1444,6 +1451,7 @@ App::patch('/v1/account/name')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.name')
->label('scope', 'account')
+ ->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@@ -1474,6 +1482,7 @@ App::patch('/v1/account/password')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.password')
->label('scope', 'account')
+ ->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
->label('usage.metric', 'users.{scope}.requests.update')
@@ -1513,6 +1522,7 @@ App::patch('/v1/account/email')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.email')
->label('scope', 'account')
+ ->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@@ -1564,6 +1574,7 @@ App::patch('/v1/account/phone')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.phone')
->label('scope', 'account')
+ ->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@@ -1611,6 +1622,7 @@ App::patch('/v1/account/prefs')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.prefs')
->label('scope', 'account')
+ ->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@@ -1639,6 +1651,7 @@ App::patch('/v1/account/status')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.status')
->label('scope', 'account')
+ ->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.delete')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@@ -1673,6 +1686,7 @@ App::delete('/v1/account/sessions/:sessionId')
->groups(['api', 'account'])
->label('scope', 'account')
->label('event', 'users.[userId].sessions.[sessionId].delete')
+ ->label('audits.event', 'session.delete')
->label('audits.resource', 'user/{user.$id}')
->label('usage.metric', 'sessions.{scope}.requests.delete')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@@ -1743,6 +1757,7 @@ App::patch('/v1/account/sessions/:sessionId')
->groups(['api', 'account'])
->label('scope', 'account')
->label('event', 'users.[userId].sessions.[sessionId].update')
+ ->label('audits.event', 'session.update')
->label('audits.resource', 'user/{response.userId}')
->label('audits.userId', '{response.userId}')
->label('usage.metric', 'sessions.{scope}.requests.update')
@@ -1824,6 +1839,7 @@ App::delete('/v1/account/sessions')
->groups(['api', 'account'])
->label('scope', 'account')
->label('event', 'users.[userId].sessions.[sessionId].delete')
+ ->label('audits.event', 'session.delete')
->label('audits.resource', 'user/{user.$id}')
->label('usage.metric', 'sessions.{scope}.requests.delete')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@@ -1885,6 +1901,7 @@ App::post('/v1/account/recovery')
->groups(['api', 'account'])
->label('scope', 'public')
->label('event', 'users.[userId].recovery.[tokenId].create')
+ ->label('audits.event', 'recovery.create')
->label('audits.resource', 'user/{response.userId}')
->label('audits.userId', '{response.userId}')
->label('usage.metric', 'users.{scope}.requests.update')
@@ -1991,6 +2008,7 @@ App::put('/v1/account/recovery')
->groups(['api', 'account'])
->label('scope', 'public')
->label('event', 'users.[userId].recovery.[tokenId].update')
+ ->label('audits.event', 'recovery.update')
->label('audits.resource', 'user/{response.userId}')
->label('audits.userId', '{response.userId}')
->label('usage.metric', 'users.{scope}.requests.update')
@@ -2059,6 +2077,7 @@ App::post('/v1/account/verification')
->groups(['api', 'account'])
->label('scope', 'account')
->label('event', 'users.[userId].verification.[tokenId].create')
+ ->label('audits.event', 'verification.create')
->label('audits.resource', 'user/{response.userId}')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@@ -2148,6 +2167,7 @@ App::put('/v1/account/verification')
->groups(['api', 'account'])
->label('scope', 'public')
->label('event', 'users.[userId].verification.[tokenId].update')
+ ->label('audits.event', 'verification.update')
->label('audits.resource', 'user/{response.userId}')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@@ -2206,6 +2226,7 @@ App::post('/v1/account/verification/phone')
->groups(['api', 'account'])
->label('scope', 'account')
->label('event', 'users.[userId].verification.[tokenId].create')
+ ->label('audits.event', 'verification.create')
->label('audits.resource', 'user/{response.userId}')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@@ -2290,6 +2311,7 @@ App::put('/v1/account/verification/phone')
->groups(['api', 'account'])
->label('scope', 'public')
->label('event', 'users.[userId].verification.[tokenId].update')
+ ->label('audits.event', 'verification.update')
->label('audits.resource', 'user/{response.userId}')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php
index ebd719b389..6efb54d5ea 100644
--- a/app/controllers/api/databases.php
+++ b/app/controllers/api/databases.php
@@ -153,6 +153,7 @@ App::post('/v1/databases')
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].create')
->label('scope', 'databases.write')
+ ->label('audits.event', 'database.create')
->label('audits.resource', 'database/{response.$id}')
->label('usage.metric', 'databases.{scope}.requests.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -386,6 +387,7 @@ App::put('/v1/databases/:databaseId')
->groups(['api', 'database'])
->label('scope', 'databases.write')
->label('event', 'databases.[databaseId].update')
+ ->label('audits.event', 'database.update')
->label('audits.resource', 'database/{response.$id}')
->label('usage.metric', 'databases.{scope}.requests.update')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -428,6 +430,7 @@ App::delete('/v1/databases/:databaseId')
->groups(['api', 'database'])
->label('scope', 'databases.write')
->label('event', 'databases.[databaseId].delete')
+ ->label('audits.event', 'database.delete')
->label('audits.resource', 'database/{request.databaseId}')
->label('usage.metric', 'databases.{scope}.requests.delete')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -474,6 +477,7 @@ App::post('/v1/databases/:databaseId/collections')
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].create')
->label('scope', 'collections.write')
+ ->label('audits.event', 'collection.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{response.$id}')
->label('usage.metric', 'collections.{scope}.requests.create')
->label('usage.params', ['databaseId:{request.databaseId}'])
@@ -728,6 +732,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId')
->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('event', 'databases.[databaseId].collections.[collectionId].update')
+ ->label('audits.event', 'collection.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('usage.metric', 'collections.{scope}.requests.update')
->label('usage.params', ['databaseId:{request.databaseId}'])
@@ -795,6 +800,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId')
->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('event', 'databases.[databaseId].collections.[collectionId].delete')
+ ->label('audits.event', 'collection.delete')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('usage.metric', 'collections.{scope}.requests.delete')
->label('usage.params', ['databaseId:{request.databaseId}'])
@@ -851,6 +857,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
+ ->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('usage.metric', 'collections.{scope}.requests.update')
->label('usage.params', ['databaseId:{request.databaseId}'])
@@ -900,6 +907,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email'
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
+ ->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('usage.metric', 'collections.{scope}.requests.update')
->label('usage.params', ['databaseId:{request.databaseId}'])
@@ -943,6 +951,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum')
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
+ ->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('usage.metric', 'collections.{scope}.requests.update')
->label('usage.params', ['databaseId:{request.databaseId}'])
@@ -1002,6 +1011,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip')
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
+ ->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('usage.metric', 'collections.{scope}.requests.update')
->label('usage.params', ['databaseId:{request.databaseId}'])
@@ -1045,6 +1055,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url')
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
+ ->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('usage.metric', 'collections.{scope}.requests.update')
->label('usage.params', ['databaseId:{request.databaseId}'])
@@ -1088,6 +1099,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
+ ->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('usage.metric', 'collections.{scope}.requests.update')
->label('usage.params', ['databaseId:{request.databaseId}'])
@@ -1160,6 +1172,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float'
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
+ ->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('usage.metric', 'collections.{scope}.requests.update')
->label('usage.params', ['databaseId:{request.databaseId}'])
@@ -1235,6 +1248,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
+ ->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('usage.metric', 'collections.{scope}.requests.update')
->label('usage.params', ['databaseId:{request.databaseId}'])
@@ -1278,6 +1292,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
+ ->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('usage.metric', 'collections.{scope}.requests.update')
->label('usage.params', ['databaseId:{request.databaseId}'])
@@ -1432,6 +1447,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete')
+ ->label('audits.event', 'attribute.delete')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('usage.metric', 'collections.{scope}.requests.update')
->label('usage.params', ['databaseId:{request.databaseId}'])
@@ -1519,6 +1535,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].create')
->label('scope', 'collections.write')
+ ->label('audits.event', 'index.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('usage.metric', 'collections.{scope}.requests.update')
->label('usage.params', ['databaseId:{request.databaseId}'])
@@ -1755,6 +1772,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].delete')
+ ->label('audits.event', 'index.delete')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('usage.metric', 'collections.{scope}.requests.update')
->label('usage.params', ['databaseId:{request.databaseId}'])
@@ -1822,6 +1840,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].create')
->label('scope', 'documents.write')
+ ->label('audits.event', 'document.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('usage.metric', 'documents.{scope}.requests.create')
->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}'])
@@ -2203,6 +2222,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].update')
->label('scope', 'documents.write')
+ ->label('audits.event', 'document.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{response.$id}')
->label('usage.metric', 'documents.{scope}.requests.update')
->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}'])
@@ -2335,6 +2355,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
->groups(['api', 'database'])
->label('scope', 'documents.write')
->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].delete')
+ ->label('audits.event', 'document.delete')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{request.documentId}')
->label('usage.metric', 'documents.{scope}.requests.delete')
->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}'])
diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php
index f12c2ebbae..606d8bfd1a 100644
--- a/app/controllers/api/functions.php
+++ b/app/controllers/api/functions.php
@@ -52,6 +52,7 @@ App::post('/v1/functions')
->desc('Create Function')
->label('scope', 'functions.write')
->label('event', 'functions.[functionId].create')
+ ->label('audits.event', 'function.create')
->label('audits.resource', 'function/{response.$id}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
@@ -67,16 +68,17 @@ App::post('/v1/functions')
->param('events', [], new ArrayList(new ValidatorEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.', true)
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.', true)
+ ->param('enabled', true, new Boolean(), 'Is function enabled?', true)
->inject('response')
->inject('dbForProject')
->inject('events')
- ->action(function (string $functionId, string $name, array $execute, string $runtime, array $events, string $schedule, int $timeout, Response $response, Database $dbForProject, Event $eventsInstance) {
+ ->action(function (string $functionId, string $name, array $execute, string $runtime, array $events, string $schedule, int $timeout, bool $enabled, Response $response, Database $dbForProject, Event $eventsInstance) {
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
$function = $dbForProject->createDocument('functions', new Document([
'$id' => $functionId,
'execute' => $execute,
- 'status' => 'disabled',
+ 'enabled' => $enabled,
'name' => $name,
'runtime' => $runtime,
'deployment' => '',
@@ -410,6 +412,7 @@ App::put('/v1/functions/:functionId')
->desc('Update Function')
->label('scope', 'functions.write')
->label('event', 'functions.[functionId].update')
+ ->label('audits.event', 'function.update')
->label('audits.resource', 'function/{response.$id}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
@@ -424,12 +427,13 @@ App::put('/v1/functions/:functionId')
->param('events', [], new ArrayList(new ValidatorEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.', true)
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Maximum execution time in seconds.', true)
+ ->param('enabled', true, new Boolean(), 'Is function enabled?', true)
->inject('response')
->inject('dbForProject')
->inject('project')
->inject('user')
->inject('events')
- ->action(function (string $functionId, string $name, array $execute, array $events, string $schedule, int $timeout, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance) {
+ ->action(function (string $functionId, string $name, array $execute, array $events, string $schedule, int $timeout, bool $enabled, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance) {
$function = $dbForProject->getDocument('functions', $functionId);
@@ -441,6 +445,8 @@ App::put('/v1/functions/:functionId')
$cron = (!empty($function->getAttribute('deployment')) && !empty($schedule)) ? new CronExpression($schedule) : null;
$next = (!empty($function->getAttribute('deployment')) && !empty($schedule)) ? DateTime::format($cron->getNextRunDate()) : null;
+ $enabled ??= $function->getAttribute('enabled', true);
+
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
'execute' => $execute,
'name' => $name,
@@ -448,6 +454,7 @@ App::put('/v1/functions/:functionId')
'schedule' => $schedule,
'scheduleNext' => $next,
'timeout' => $timeout,
+ 'enabled' => $enabled,
'search' => implode(' ', [$functionId, $name, $function->getAttribute('runtime')]),
])));
@@ -472,6 +479,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
->desc('Update Function Deployment')
->label('scope', 'functions.write')
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
+ ->label('audits.event', 'deployment.update')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
@@ -538,6 +546,7 @@ App::delete('/v1/functions/:functionId')
->desc('Delete Function')
->label('scope', 'functions.write')
->label('event', 'functions.[functionId].delete')
+ ->label('audits.event', 'function.delete')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
@@ -576,6 +585,7 @@ App::post('/v1/functions/:functionId/deployments')
->desc('Create Deployment')
->label('scope', 'functions.write')
->label('event', 'functions.[functionId].deployments.[deploymentId].create')
+ ->label('audits.event', 'deployment.create')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
@@ -868,6 +878,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
->desc('Delete Deployment')
->label('scope', 'functions.write')
->label('event', 'functions.[functionId].deployments.[deploymentId].delete')
+ ->label('audits.event', 'deployment.delete')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
@@ -945,12 +956,15 @@ App::post('/v1/functions/:functionId/executions')
->inject('user')
->inject('events')
->inject('usage')
- ->action(function (string $functionId, string $data, bool $async, Response $response, Document $project, Database $dbForProject, Document $user, Event $events, Stats $usage) {
+ ->inject('mode')
+ ->action(function (string $functionId, string $data, bool $async, Response $response, Document $project, Database $dbForProject, Document $user, Event $events, Stats $usage, string $mode) {
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
- if ($function->isEmpty()) {
- throw new Exception(Exception::FUNCTION_NOT_FOUND);
+ if ($function->isEmpty() || !$function->getAttribute('enabled')) {
+ if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
+ throw new Exception(Exception::FUNCTION_NOT_FOUND);
+ }
}
$runtimes = Config::getParam('runtimes', []);
@@ -1137,12 +1151,15 @@ App::get('/v1/functions/:functionId/executions')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
- ->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject) {
+ ->inject('mode')
+ ->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) {
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
- if ($function->isEmpty()) {
- throw new Exception(Exception::FUNCTION_NOT_FOUND);
+ if ($function->isEmpty() || !$function->getAttribute('enabled')) {
+ if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
+ throw new Exception(Exception::FUNCTION_NOT_FOUND);
+ }
}
$queries = Query::parseQueries($queries);
@@ -1206,12 +1223,15 @@ App::get('/v1/functions/:functionId/executions/:executionId')
->param('executionId', '', new UID(), 'Execution ID.')
->inject('response')
->inject('dbForProject')
- ->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject) {
+ ->inject('mode')
+ ->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, string $mode) {
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
- if ($function->isEmpty()) {
- throw new Exception(Exception::FUNCTION_NOT_FOUND);
+ if ($function->isEmpty() || !$function->getAttribute('enabled')) {
+ if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) {
+ throw new Exception(Exception::FUNCTION_NOT_FOUND);
+ }
}
$execution = $dbForProject->getDocument('executions', $executionId);
@@ -1240,6 +1260,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
->desc('Retry Build')
->label('scope', 'functions.write')
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
+ ->label('audits.event', 'deployment.update')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'functions')
@@ -1299,6 +1320,8 @@ App::post('/v1/functions/:functionId/variables')
->desc('Create Variable')
->groups(['api', 'functions'])
->label('scope', 'functions.write')
+ ->label('audits.event', 'variable.create')
+ ->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'createVariable')
@@ -1359,43 +1382,18 @@ App::get('/v1/functions/:functionId/variables')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_VARIABLE_LIST)
->param('functionId', null, new UID(), 'Function unique ID.', false)
- ->param('queries', [], new Variables(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Variables::ALLOWED_ATTRIBUTES), true)
- ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
- ->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject) {
+ ->action(function (string $functionId, Response $response, Database $dbForProject) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
- $queries = Query::parseQueries($queries);
-
- if (!empty($search)) {
- $queries[] = Query::search('search', $search);
- }
-
- // Get cursor document if there was a cursor query
- $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE);
- $cursor = reset($cursor);
- if ($cursor) {
- /** @var Query $cursor */
- $variableId = $cursor->getValue();
- $cursorDocument = $dbForProject->getDocument('variables', $variableId);
-
- if ($cursorDocument->isEmpty()) {
- throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Variable '{$variableId}' for the 'cursor' value not found.");
- }
-
- $cursor->setValue($cursorDocument);
- }
-
- $filterQueries = Query::groupByType($queries)['filters'];
-
$response->dynamic(new Document([
- 'variables' => $dbForProject->find('variables', $queries),
- 'total' => $dbForProject->count('variables', $filterQueries, APP_LIMIT_COUNT),
+ 'variables' => $function->getAttribute('vars'),
+ 'total' => \count($function->getAttribute('vars')),
]), Response::MODEL_VARIABLE_LIST);
});
@@ -1437,6 +1435,8 @@ App::put('/v1/functions/:functionId/variables/:variableId')
->desc('Update Variable')
->groups(['api', 'functions'])
->label('scope', 'functions.write')
+ ->label('audits.event', 'variable.update')
+ ->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'updateVariable')
@@ -1488,6 +1488,8 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
->desc('Delete Variable')
->groups(['api', 'functions'])
->label('scope', 'functions.write')
+ ->label('audits.event', 'variable.delete')
+ ->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'deleteVariable')
diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php
index c41d105d06..1134887fb6 100644
--- a/app/controllers/api/storage.php
+++ b/app/controllers/api/storage.php
@@ -48,6 +48,7 @@ App::post('/v1/storage/buckets')
->groups(['api', 'storage'])
->label('scope', 'buckets.write')
->label('event', 'buckets.[bucketId].create')
+ ->label('audits.event', 'bucket.create')
->label('audits.resource', 'bucket/{response.$id}')
->label('usage.metric', 'buckets.{scope}.requests.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -219,6 +220,7 @@ App::put('/v1/storage/buckets/:bucketId')
->groups(['api', 'storage'])
->label('scope', 'buckets.write')
->label('event', 'buckets.[bucketId].update')
+ ->label('audits.event', 'bucket.update')
->label('audits.resource', 'bucket/{response.$id}')
->label('usage.metric', 'buckets.{scope}.requests.update')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -284,6 +286,7 @@ App::delete('/v1/storage/buckets/:bucketId')
->desc('Delete Bucket')
->groups(['api', 'storage'])
->label('scope', 'buckets.write')
+ ->label('audits.event', 'bucket.delete')
->label('event', 'buckets.[bucketId].delete')
->label('audits.resource', 'bucket/{request.bucketId}')
->label('usage.metric', 'buckets.{scope}.requests.delete')
@@ -326,6 +329,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
->desc('Create File')
->groups(['api', 'storage'])
->label('scope', 'files.write')
+ ->label('audits.event', 'file.create')
->label('event', 'buckets.[bucketId].files.[fileId].create')
->label('audits.resource', 'file/{response.$id}')
->label('usage.metric', 'files.{scope}.requests.create')
@@ -1238,6 +1242,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('event', 'buckets.[bucketId].files.[fileId].update')
+ ->label('audits.event', 'file.update')
->label('audits.resource', 'file/{response.$id}')
->label('usage.metric', 'files.{scope}.requests.update')
->label('usage.params', ['bucketId:{request.bucketId}'])
@@ -1340,6 +1345,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('event', 'buckets.[bucketId].files.[fileId].delete')
+ ->label('audits.event', 'file.delete')
->label('audits.resource', 'file/{request.fileId}')
->label('usage.metric', 'files.{scope}.requests.delete')
->label('usage.params', ['bucketId:{request.bucketId}'])
diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php
index 29463dc32c..ae819973a6 100644
--- a/app/controllers/api/teams.php
+++ b/app/controllers/api/teams.php
@@ -45,6 +45,7 @@ App::post('/v1/teams')
->groups(['api', 'teams'])
->label('event', 'teams.[teamId].create')
->label('scope', 'teams.write')
+ ->label('audits.event', 'team.create')
->label('audits.resource', 'team/{response.$id}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams')
@@ -200,6 +201,7 @@ App::put('/v1/teams/:teamId')
->groups(['api', 'teams'])
->label('event', 'teams.[teamId].update')
->label('scope', 'teams.write')
+ ->label('audits.event', 'team.update')
->label('audits.resource', 'team/{response.$id}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams')
@@ -235,6 +237,7 @@ App::delete('/v1/teams/:teamId')
->groups(['api', 'teams'])
->label('event', 'teams.[teamId].delete')
->label('scope', 'teams.write')
+ ->label('audits.event', 'team.delete')
->label('audits.resource', 'team/{request.teamId}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams')
@@ -289,6 +292,7 @@ App::post('/v1/teams/:teamId/memberships')
->label('event', 'teams.[teamId].memberships.[membershipId].create')
->label('scope', 'teams.write')
->label('auth.type', 'invites')
+ ->label('audits.event', 'membership.create')
->label('audits.resource', 'team/{request.teamId}')
->label('audits.userId', '{request.userId}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
@@ -584,6 +588,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->groups(['api', 'teams'])
->label('event', 'teams.[teamId].memberships.[membershipId].update')
->label('scope', 'teams.write')
+ ->label('audits.event', 'membership.update')
->label('audits.resource', 'team/{request.teamId}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams')
@@ -654,6 +659,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->groups(['api', 'teams'])
->label('event', 'teams.[teamId].memberships.[membershipId].update.status')
->label('scope', 'public')
+ ->label('audits.event', 'membership.update')
->label('audits.resource', 'team/{request.teamId}')
->label('audits.userId', '{request.userId}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@@ -787,6 +793,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
->groups(['api', 'teams'])
->label('event', 'teams.[teamId].memberships.[membershipId].delete')
->label('scope', 'teams.write')
+ ->label('audits.event', 'membership.delete')
->label('audits.resource', 'team/{request.teamId}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams')
diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php
index a3b9f8741b..a50aef782e 100644
--- a/app/controllers/api/users.php
+++ b/app/controllers/api/users.php
@@ -88,6 +88,7 @@ App::post('/v1/users')
->groups(['api', 'users'])
->label('event', 'users.[userId].create')
->label('scope', 'users.write')
+ ->label('audits.event', 'user.create')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -118,6 +119,7 @@ App::post('/v1/users/bcrypt')
->groups(['api', 'users'])
->label('event', 'users.[userId].create')
->label('scope', 'users.write')
+ ->label('audits.event', 'user.create')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -147,6 +149,7 @@ App::post('/v1/users/md5')
->groups(['api', 'users'])
->label('event', 'users.[userId].create')
->label('scope', 'users.write')
+ ->label('audits.event', 'user.create')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -176,6 +179,7 @@ App::post('/v1/users/argon2')
->groups(['api', 'users'])
->label('event', 'users.[userId].create')
->label('scope', 'users.write')
+ ->label('audits.event', 'user.create')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -205,6 +209,7 @@ App::post('/v1/users/sha')
->groups(['api', 'users'])
->label('event', 'users.[userId].create')
->label('scope', 'users.write')
+ ->label('audits.event', 'user.create')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -241,6 +246,7 @@ App::post('/v1/users/phpass')
->groups(['api', 'users'])
->label('event', 'users.[userId].create')
->label('scope', 'users.write')
+ ->label('audits.event', 'user.create')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -270,6 +276,7 @@ App::post('/v1/users/scrypt')
->groups(['api', 'users'])
->label('event', 'users.[userId].create')
->label('scope', 'users.write')
+ ->label('audits.event', 'user.create')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -312,6 +319,7 @@ App::post('/v1/users/scrypt-modified')
->groups(['api', 'users'])
->label('event', 'users.[userId].create')
->label('scope', 'users.write')
+ ->label('audits.event', 'user.create')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -609,6 +617,7 @@ App::patch('/v1/users/:userId/status')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.status')
->label('scope', 'users.write')
+ ->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
->label('usage.metric', 'users.{scope}.requests.update')
@@ -645,6 +654,7 @@ App::patch('/v1/users/:userId/verification')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.verification')
->label('scope', 'users.write')
+ ->label('audits.event', 'verification.update')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -680,6 +690,7 @@ App::patch('/v1/users/:userId/verification/phone')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.verification')
->label('scope', 'users.write')
+ ->label('audits.event', 'verification.update')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -715,6 +726,7 @@ App::patch('/v1/users/:userId/name')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.name')
->label('scope', 'users.write')
+ ->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
->label('usage.metric', 'users.{scope}.requests.update')
@@ -755,6 +767,7 @@ App::patch('/v1/users/:userId/password')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.password')
->label('scope', 'users.write')
+ ->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
->label('usage.metric', 'users.{scope}.requests.update')
@@ -796,6 +809,7 @@ App::patch('/v1/users/:userId/email')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.email')
->label('scope', 'users.write')
+ ->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
->label('usage.metric', 'users.{scope}.requests.update')
@@ -842,6 +856,7 @@ App::patch('/v1/users/:userId/phone')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.phone')
->label('scope', 'users.write')
+ ->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -886,6 +901,7 @@ App::patch('/v1/users/:userId/verification')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.verification')
->label('scope', 'users.write')
+ ->label('audits.event', 'verification.update')
->label('audits.resource', 'user/{request.userId}')
->label('audits.userId', '{request.userId}')
->label('usage.metric', 'users.{scope}.requests.update')
@@ -955,6 +971,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
->groups(['api', 'users'])
->label('event', 'users.[userId].sessions.[sessionId].delete')
->label('scope', 'users.write')
+ ->label('audits.event', 'session.delete')
->label('audits.resource', 'user/{request.userId}')
->label('usage.metric', 'sessions.{scope}.requests.delete')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -997,6 +1014,7 @@ App::delete('/v1/users/:userId/sessions')
->groups(['api', 'users'])
->label('event', 'users.[userId].sessions.[sessionId].delete')
->label('scope', 'users.write')
+ ->label('audits.event', 'session.delete')
->label('audits.resource', 'user/{user.$id}')
->label('usage.metric', 'sessions.{scope}.requests.delete')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -1039,6 +1057,7 @@ App::delete('/v1/users/:userId')
->groups(['api', 'users'])
->label('event', 'users.[userId].delete')
->label('scope', 'users.write')
+ ->label('audits.event', 'user.delete')
->label('audits.resource', 'user/{request.userId}')
->label('usage.metric', 'users.{scope}.requests.delete')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
diff --git a/app/controllers/general.php b/app/controllers/general.php
index e24c0a8a41..e56357869d 100644
--- a/app/controllers/general.php
+++ b/app/controllers/general.php
@@ -21,6 +21,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\DateTime;
@@ -31,6 +32,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;
use Utopia\Validator\WhiteList;
@@ -68,6 +70,9 @@ App::init()
case version_compare($requestFormat, '0.14.0', '<'):
Request::setFilter(new RequestV14());
break;
+ case version_compare($requestFormat, '0.15.3', '<'):
+ Request::setFilter(new RequestV15());
+ break;
default:
Request::setFilter(null);
}
@@ -194,6 +199,9 @@ App::init()
case version_compare($responseFormat, '0.14.0', '<='):
Response::setFilter(new ResponseV14());
break;
+ case version_compare($responseFormat, '0.15.3', '<='):
+ Response::setFilter(new ResponseV15());
+ break;
default:
Response::setFilter(null);
}
diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php
index 93bc020d97..e07f405140 100644
--- a/app/controllers/shared/api.php
+++ b/app/controllers/shared/api.php
@@ -144,7 +144,7 @@ App::init()
->setMode($mode)
->setUserAgent($request->getUserAgent(''))
->setIP($request->getIP())
- ->setEvent($route->getLabel('event', ''))
+ ->setEvent($route->getLabel('audits.event', ''))
->setProject($project)
->setUser($user);
diff --git a/app/views/console/databases/collection.phtml b/app/views/console/databases/collection.phtml
index 35e78b4220..19454a88db 100644
--- a/app/views/console/databases/collection.phtml
+++ b/app/views/console/databases/collection.phtml
@@ -607,7 +607,7 @@ $permissions = $this->getParam('permissions', null);
data-success="alert,trigger,redirect"
data-success-param-alert-text="Collection deleted successfully"
data-success-param-trigger-events="databases.deleteCollection"
- data-success-param-redirect-url="/console/databases?project={{router.params.project}}"
+ data-success-param-redirect-url="/console/databases?project={{router.params.project}}&databaseId={{router.params.databaseId}}"
data-failure="alert"
data-failure-param-alert-text="Failed to delete collection"
data-failure-param-alert-classname="error">
diff --git a/app/views/console/functions/function.phtml b/app/views/console/functions/function.phtml
index 235892c2db..80806fed56 100644
--- a/app/views/console/functions/function.phtml
+++ b/app/views/console/functions/function.phtml
@@ -391,9 +391,9 @@ sort($patterns);
|
Created |
- Status |
- Trigger |
- Runtime |
+ Status |
+ Trigger |
+ Runtime |
|
@@ -416,17 +416,17 @@ sort($patterns);
-
+
-
|
-
+
-
+
@@ -523,8 +523,6 @@ sort($patterns);
data-service="functions.listVariables"
data-event="load,project.update,functions.createVariable,functions.updateVariable,functions.deleteVariable"
data-name="function-variables"
- data-param-queries="limit(100)"
- data-param-queries-cast-to="array" data-param-queries-cast-from="csv"
data-param-function-id="{{router.params.id}}"
data-scope="sdk">Variables
diff --git a/app/workers/audits.php b/app/workers/audits.php
index 696eb6df50..b86649543b 100644
--- a/app/workers/audits.php
+++ b/app/workers/audits.php
@@ -24,7 +24,7 @@ class AuditsV1 extends Worker
public function run(): void
{
- $events = $this->args['events'];
+ $event = $this->args['event'];
$payload = $this->args['payload'];
$mode = $this->args['mode'];
$resource = $this->args['resource'];
@@ -42,7 +42,7 @@ class AuditsV1 extends Worker
$audit->log(
userId: $user->getId(),
// Pass first, most verbose event pattern
- event: $events[0],
+ event: $event,
resource: $resource,
userAgent: $userAgent,
ip: $ip,
diff --git a/composer.lock b/composer.lock
index 62b00640ff..0218e07b73 100644
--- a/composer.lock
+++ b/composer.lock
@@ -5393,5 +5393,5 @@
"platform-overrides": {
"php": "8.0"
},
- "plugin-api-version": "2.3.0"
+ "plugin-api-version": "2.2.0"
}
diff --git a/docs/sdks/android/GETTING_STARTED.md b/docs/sdks/android/GETTING_STARTED.md
index 0c00bf6019..684f98e0f6 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,10 +82,10 @@ 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", user.toMap())
} catch(e : AppwriteException) {
- Log.e("AppwriteException",e.message.toString())
+ e.printStackTrace()
}
```
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..1da041112c 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 user = try await account.create(
+ userId: ID.unique(),
+ email: "email@example.com",
+ password: "password"
+ )
+ print(String(describing: user.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 user = 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 user = try await account.get()
+ print(String(describing: user.toMap()))
+ } catch {
+ print(error.localizedDescription)
}
}
```
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..23bab6f70a 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..99d5e719af 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,20 +59,19 @@ 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)
+ e.printStackTrace()
}
}
```
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..9f693b65c6 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.id 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/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)
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) {
diff --git a/phpunit.xml b/phpunit.xml
index fd59a8f236..97bec18b42 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -6,7 +6,7 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
- stopOnFailure="false"
+ stopOnFailure="true"
>
diff --git a/src/Appwrite/Event/Audit.php b/src/Appwrite/Event/Audit.php
index 4df3eeec62..254f7c294a 100644
--- a/src/Appwrite/Event/Audit.php
+++ b/src/Appwrite/Event/Audit.php
@@ -124,7 +124,7 @@ class Audit extends Event
'mode' => $this->mode,
'ip' => $this->ip,
'userAgent' => $this->userAgent,
- 'events' => Event::generateEvents($this->getEvent(), $this->getParams())
+ 'event' => $this->event,
]);
}
}
diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Functions.php b/src/Appwrite/Utopia/Database/Validator/Queries/Functions.php
index ee4c311163..a2ba368953 100644
--- a/src/Appwrite/Utopia/Database/Validator/Queries/Functions.php
+++ b/src/Appwrite/Utopia/Database/Validator/Queries/Functions.php
@@ -6,7 +6,7 @@ class Functions extends Base
{
public const ALLOWED_ATTRIBUTES = [
'name',
- 'status',
+ 'enabled',
'runtime',
'deployment',
'schedule',
diff --git a/src/Appwrite/Utopia/Request/Filters/V15.php b/src/Appwrite/Utopia/Request/Filters/V15.php
new file mode 100644
index 0000000000..65b09e1f24
--- /dev/null
+++ b/src/Appwrite/Utopia/Request/Filters/V15.php
@@ -0,0 +1,275 @@
+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':
+ case 'storage.listBuckets':
+ case 'storage.listFiles':
+ case 'teams.list':
+ case 'teams.listMemberships':
+ case 'users.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':
+ case 'storage.createFile':
+ case 'storage.updateFile':
+ $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 'storage.createBucket':
+ case 'storage.updateBucket':
+ $content = $this->convertBucketPermission($content);
+ $content = $this->convertReadWrite($content);
+ break;
+ }
+
+ return $content;
+ }
+
+ protected function convertLimitAndOffset($content)
+ {
+ if (isset($content['limit'])) {
+ $content['queries'][] = 'limit(' . $content['limit'] . ')';
+ }
+
+ if (isset($content['offset'])) {
+ $content['queries'][] = 'offset(' . $content['offset'] . ')';
+ }
+
+ unset($content['limit']);
+ unset($content['offset']);
+
+ return $content;
+ }
+
+ protected function convertCursor($content)
+ {
+ 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 convertOrderType($content)
+ {
+ if (isset($content['orderType'])) {
+ if ($content['orderType'] === Database::ORDER_DESC) {
+ $content['queries'][] = 'orderDesc("")';
+ } else {
+ $content['queries'][] = 'orderAsc("")';
+ }
+ }
+ unset($content['orderType']);
+
+ 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 {
+ $content['queries'][] = 'orderAsc("' . $attribute . '")';
+ }
+ }
+ }
+
+ 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'][] = Permission::read(Role::any());
+ } elseif ($read === 'role:guest') {
+ $content['permissions'][] = Permission::read(Role::guests());
+ } elseif ($read === 'role:member') {
+ $content['permissions'][] = Permission::read(Role::users());
+ } elseif (str_contains($read, ':')) {
+ $content['permissions'][] = Permission::read(Role::parse($read));
+ }
+ }
+ }
+
+ if (isset($content['write'])) {
+ foreach ($content['write'] as $write) {
+ if ($write === 'role:all' || $write === 'role:member') {
+ $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'][] = Permission::write(Role::parse($write));
+ }
+ }
+ }
+
+ unset($content['read']);
+ unset($content['write']);
+
+ return $content;
+ }
+
+ protected function convertFilters($content)
+ {
+ if (!isset($content['queries'])) {
+ return $content;
+ }
+
+ $operations = [
+ '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) {
+ $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[] = Role::users()->toString();
+ } 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;
+ }
+
+ protected function convertBucketPermission($content)
+ {
+ if (isset($content['permission'])) {
+ $content['fileSecurity'] = $content['permission'] === 'file';
+ }
+
+ unset($content['permission']);
+
+ return $content;
+ }
+}
diff --git a/src/Appwrite/Utopia/Response/Filters/V15.php b/src/Appwrite/Utopia/Response/Filters/V15.php
new file mode 100644
index 0000000000..8e688d58ff
--- /dev/null
+++ b/src/Appwrite/Utopia/Response/Filters/V15.php
@@ -0,0 +1,577 @@
+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->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:
+ 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_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->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->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->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->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']);
+ 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->parseUsageFuncs($parsedResponse);
+ break;
+ case Response::MODEL_USAGE_PROJECT:
+ $parsedResponse = $this->parseUsageProject($parsedResponse);
+ break;
+ case Response::MODEL_USAGE_STORAGE:
+ $parsedResponse = $this->parseUsageStorage($parsedResponse);
+ break;
+ }
+
+ return $parsedResponse;
+ }
+
+ protected function parseBuild(array $content)
+ {
+ $content = $this->parseDatetimeAttributes($content, ['startTime', 'endTime']);
+
+ return $content;
+ }
+
+ protected function parseBucket(array $content)
+ {
+ if (isset($content['fileSecurity'])) {
+ if ($content['fileSecurity']) {
+ $content['permission'] = 'file';
+ } else {
+ $content['permission'] = 'bucket';
+ }
+ }
+
+ unset($content['fileSecurity']);
+ unset($content['compression']);
+
+ $content = $this->parsePermissions($content);
+ $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt']);
+
+ return $content;
+ }
+
+ protected function parseDatetimeAttributes(array $content, array $attributes): array
+ {
+ foreach ($attributes as $attribute) {
+ if (isset($content[$attribute])) {
+ $content[$attribute] = strtotime($content[$attribute]);
+ }
+ }
+ return $content;
+ }
+
+ protected function parseUser(array $content): array
+ {
+ unset($content['password']);
+ unset($content['hash']);
+ unset($content['hashOptions']);
+
+ $content = $this->parseDatetimeAttributes($content, ['registration', 'passwordUpdate', '$createdAt', '$updatedAt']);
+ return $content;
+ }
+
+ protected function parseMetric(array $content)
+ {
+ $content = $this->parseDatetimeAttributes($content, ['date']);
+ return $content;
+ }
+
+ protected function parsePermissions(array $content)
+ {
+ if (!isset($content['$permissions'])) {
+ return $content;
+ }
+
+ $read = [];
+ $write = [];
+
+ // downgrade the permissions
+ foreach ($content['$permissions'] as $permission) {
+ $permission = Permission::parse($permission);
+ $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 Database::PERMISSION_UPDATE:
+ case Database::PERMISSION_DELETE:
+ case Database::PERMISSION_WRITE:
+ case Database::PERMISSION_CREATE:
+ $write[$this->parseRole($permission_value)] = true;
+ break;
+ case Database::PERMISSION_READ:
+ $read[$this->parseRole($permission_value)] = true;
+ break;
+ }
+ }
+
+ $content['$read'] = array_keys($read);
+ $content['$write'] = array_keys($write);
+
+ unset($content['$permissions']);
+
+ return $content;
+ }
+
+ protected function parseRole(string $role)
+ {
+ 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->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;
+ }
+
+ private function parseExecution($content)
+ {
+ unset($content['stdout']);
+
+ 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;
+ }
+
+ private function parseCreatedAtUpdatedAt($content)
+ {
+ $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt']);
+ return $content;
+ }
+
+ private function parseFunction($content)
+ {
+ if (isset($content['execute'])) {
+ foreach ($content['execute'] as $i => $role) {
+ $content['execute'][$i] = $this->parseRole($role);
+ }
+ }
+
+ if (isset($content['vars'])) {
+ $vars = [];
+ foreach ($content['vars'] as $i => $var) {
+ $vars[$var['key']] = $var['value'];
+ }
+ $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;
+ }
+
+ private function parseKey($content)
+ {
+ $content = $this->parseDatetimeAttributes($content, ['$createdAt', '$updatedAt', 'expire']);
+ return $content;
+ }
+
+ private function parseLog($content)
+ {
+ $content = $this->parseDatetimeAttributes($content, ['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 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',
+ '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']);
+ unset($content['buildsTotal']);
+ unset($content['executionsSuccess']);
+ unset($content['buildsFailure']);
+ unset($content['buildsSuccess']);
+ unset($content['buildsTime']);
+
+ return $content;
+ }
+
+ private function parseUsageProject($content)
+ {
+ $content['functions'] = $content['executions'];
+ unset($content['executions']);
+
+ $keys = [
+ 'collections',
+ 'documents',
+ 'functions',
+ 'network',
+ 'requests',
+ 'storage',
+ 'users',
+ ];
+
+ $content = $this->parseUsage($content, $keys);
+
+ return $content;
+ }
+
+ private function parseUsageStorage($content)
+ {
+ $content['filesStorage'] = $content['storage'];
+ unset($content['storage']);
+
+ $keys = [
+ 'bucketsCount',
+ 'bucketsCreate',
+ 'bucketsDelete',
+ 'bucketsRead',
+ 'bucketsUpdate',
+ 'filesCount',
+ 'filesCreate',
+ 'filesDelete',
+ 'filesRead',
+ 'filesStorage',
+ 'filesUpdate',
+ ];
+
+ $content = $this->parseUsage($content, $keys);
+
+ return $content;
+ }
+}
diff --git a/src/Appwrite/Utopia/Response/Model/Func.php b/src/Appwrite/Utopia/Response/Model/Func.php
index fbe23bab0b..c7e69fff88 100644
--- a/src/Appwrite/Utopia/Response/Model/Func.php
+++ b/src/Appwrite/Utopia/Response/Model/Func.php
@@ -43,11 +43,11 @@ class Func extends Model
'default' => '',
'example' => 'My Function',
])
- ->addRule('status', [
- 'type' => self::TYPE_STRING,
- 'description' => 'Function status. Possible values: `disabled`, `enabled`',
- 'default' => '',
- 'example' => 'enabled',
+ ->addRule('enabled', [
+ 'type' => self::TYPE_BOOLEAN,
+ 'description' => 'Function enabled.',
+ 'default' => true,
+ 'example' => false,
])
->addRule('runtime', [
'type' => self::TYPE_STRING,
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/e2e/Services/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php
index 69ac671167..e8bf146316 100644
--- a/tests/e2e/Services/Account/AccountBase.php
+++ b/tests/e2e/Services/Account/AccountBase.php
@@ -341,7 +341,7 @@ trait AccountBase
$this->assertNotEmpty($response['body']['logs']);
$this->assertCount(3, $response['body']['logs']);
$this->assertIsNumeric($response['body']['total']);
- $this->assertContains($response['body']['logs'][1]['event'], ["users.{$userId}.create", "users.{$userId}.sessions.{$sessionId}.create"]);
+ $this->assertContains($response['body']['logs'][1]['event'], ["session.create"]);
$this->assertEquals($response['body']['logs'][1]['ip'], filter_var($response['body']['logs'][1]['ip'], FILTER_VALIDATE_IP));
$this->assertEquals(true, DateTime::isValid($response['body']['logs'][1]['time']));
@@ -363,7 +363,7 @@ trait AccountBase
$this->assertEquals('--', $response['body']['logs'][1]['countryCode']);
$this->assertEquals('Unknown', $response['body']['logs'][1]['countryName']);
- $this->assertContains($response['body']['logs'][2]['event'], ["users.{$userId}.create", "users.{$userId}.sessions.{$sessionId}.create"]);
+ $this->assertContains($response['body']['logs'][2]['event'], ["user.create"]);
$this->assertEquals($response['body']['logs'][2]['ip'], filter_var($response['body']['logs'][2]['ip'], FILTER_VALIDATE_IP));
$this->assertEquals(true, DateTime::isValid($response['body']['logs'][2]['time']));
diff --git a/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php
index a6e0434f67..68b9ac46ea 100644
--- a/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php
+++ b/tests/e2e/Services/Functions/FunctionsConsoleClientTest.php
@@ -182,74 +182,14 @@ class FunctionsConsoleClientTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, sizeof($response['body']['variables']));
+ $this->assertEquals(1, $response['body']['total']);
$this->assertEquals("APP_TEST", $response['body']['variables'][0]['key']);
$this->assertEquals("TESTINGVALUE", $response['body']['variables'][0]['value']);
- $variableId = $response['body']['variables'][0]['$id'];
-
- $response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/variables', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'queries' => [ 'limit(0)' ]
- ]);
-
- $this->assertEquals(200, $response['headers']['status-code']);
- $this->assertCount(0, $response['body']['variables']);
-
- $response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/variables', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'queries' => [ 'offset(1)' ]
- ]);
-
- $this->assertEquals(200, $response['headers']['status-code']);
- $this->assertCount(0, $response['body']['variables']);
-
- $response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/variables', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'queries' => [ 'equal("key", "APP_TEST")' ]
- ]);
-
- $this->assertEquals(200, $response['headers']['status-code']);
- $this->assertCount(1, $response['body']['variables']);
-
- $response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/variables', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'search' => $variableId
- ]);
-
- $this->assertEquals(200, $response['headers']['status-code']);
- $this->assertCount(1, $response['body']['variables']);
-
- $response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/variables', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'queries' => [ 'equal("key", "NON_EXISTING_VARIABLE")' ]
- ]);
-
- $this->assertEquals(200, $response['headers']['status-code']);
- $this->assertCount(0, $response['body']['variables']);
-
/**
* Test for FAILURE
*/
- $response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/variables', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'queries' => [ 'equal("value", "MY_SECRET")' ]
- ]);
-
- $this->assertEquals(400, $response['headers']['status-code']);
-
return $data;
}
@@ -403,6 +343,7 @@ class FunctionsConsoleClientTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(0, sizeof($response['body']['variables']));
+ $this->assertEquals(0, $response['body']['total']);
/**
* Test for FAILURE
diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
index f974c6006e..94f7e84f43 100644
--- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
+++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
@@ -140,7 +140,7 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
- 'queries' => [ 'equal("status", "disabled")' ]
+ 'queries' => [ 'equal("enabled", true)' ]
]);
$this->assertEquals($response['headers']['status-code'], 200);
@@ -150,7 +150,7 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
- 'queries' => [ 'equal("status", "enabled")' ]
+ 'queries' => [ 'equal("enabled", false)' ]
]);
$this->assertEquals($response['headers']['status-code'], 200);
diff --git a/tests/unit/Utopia/Request/Filters/V15Test.php b/tests/unit/Utopia/Request/Filters/V15Test.php
new file mode 100644
index 0000000000..70bf71d4cb
--- /dev/null
+++ b/tests/unit/Utopia/Request/Filters/V15Test.php
@@ -0,0 +1,742 @@
+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 testListAccountLogs(array $content, array $expected): void
+ {
+ $model = 'account.listLogs';
+
+ $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 collectionPermissionProvider(): 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 collectionPermissionProvider
+ * @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 collectionPermissionProvider
+ * @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);
+ }
+
+ /**
+ * @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);
+ }
+
+ 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 testListTeamMemberships(array $content, array $expected): void
+ {
+ $model = 'teams.listMemberships';
+
+ $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 testListUserLogs(array $content, array $expected): void
+ {
+ $model = 'users.listLogs';
+
+ $result = $this->filter->parse($content, $model);
+
+ $this->assertEquals($expected, $result);
+ }
+}
diff --git a/tests/unit/Utopia/Response/Filters/V15Test.php b/tests/unit/Utopia/Response/Filters/V15Test.php
new file mode 100644
index 0000000000..a68da474d9
--- /dev/null
+++ b/tests/unit/Utopia/Response/Filters/V15Test.php
@@ -0,0 +1,1888 @@
+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);
+ }
+
+ /**
+ * @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;
+
+ $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);
+ }
+
+ /**
+ * @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 [
+ '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
+ * @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
+ */
+ 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 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
+ */
+ public function testDeployment(array $content, array $expected): void
+ {
+ $model = Response::MODEL_DEPLOYMENT;
+
+ $result = $this->filter->parse($content, $model);
+
+ $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
+ */
+ public function testDocument(array $content, array $expected): void
+ {
+ $model = Response::MODEL_DOCUMENT;
+
+ $result = $this->filter->parse($content, $model);
+
+ $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
+ */
+ public function testDomain(array $content, array $expected): void
+ {
+ $model = Response::MODEL_DOMAIN;
+
+ $result = $this->filter->parse($content, $model);
+
+ $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 [
+ 'basic execution' => [
+ [
+ '$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 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 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
+ */
+ public function testFile(array $content, array $expected): void
+ {
+ $model = Response::MODEL_FILE;
+
+ $result = $this->filter->parse($content, $model);
+
+ $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 [
+ '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',
+ 'enabled' => true,
+ '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
+ ],
+ ],
+ 'enabled false' => [
+ ['enabled' => false],
+ ['status' => 'disabled'],
+ ],
+ ];
+ }
+
+ /**
+ * @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);
+ }
+
+ /**
+ * @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 [
+ '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);
+ }
+
+ /**
+ * @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 [
+ 'basic log' => [
+ [
+ '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 logProvider
+ */
+ public function testLog(array $content, array $expected): void
+ {
+ $model = Response::MODEL_LOG;
+
+ $result = $this->filter->parse($content, $model);
+
+ $this->assertEquals($expected, $result);
+ }
+
+ /**
+ * @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 [
+ '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);
+ }
+
+ /**
+ * @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 [
+ '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 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
+ */
+ public function testProject(array $content, array $expected): void
+ {
+ $model = Response::MODEL_PROJECT;
+
+ $result = $this->filter->parse($content, $model);
+
+ $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 [
+ '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 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
+ */
+ public function testTeam(array $content, array $expected): void
+ {
+ $model = Response::MODEL_TEAM;
+
+ $result = $this->filter->parse($content, $model);
+
+ $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 [
+ '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 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 [
+ '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);
+ }
+
+ 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);
+ }
+}
|