1
0
Fork 0
mirror of synced 2024-05-20 20:52:36 +12:00

Merge remote-tracking branch 'origin/0.16.x' into feat-grant-verified-dimension

This commit is contained in:
Jake Barnby 2022-09-14 17:31:23 +12:00
commit 696c4bfde3
No known key found for this signature in database
GPG key ID: C437A8CC85B96E9C
42 changed files with 3827 additions and 307 deletions

View file

@ -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

View file

@ -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],
],
[

View file

@ -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])

View file

@ -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}'])

View file

@ -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')

View file

@ -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}'])

View file

@ -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')

View file

@ -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])

View file

@ -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);
}

View file

@ -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);

View file

@ -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">

View file

@ -391,9 +391,9 @@ sort($patterns);
<tr>
<th width="30"></th>
<th width="160">Created</th>
<th width="100">Status</th>
<th width="80">Trigger</th>
<th width="60">Runtime</th>
<th width="150">Status</th>
<th width="120">Trigger</th>
<th width="80">Runtime</th>
<th width=""></th>
</tr>
</thead>
@ -416,17 +416,17 @@ sort($patterns);
<span data-ls-bind="{{execution.trigger}}"></span>
</td>
<td data-title="Time: ">
<span data-ls-if="{{execution.status}} === 'completed' || {{execution.status}} === 'failed'" data-ls-bind="{{execution.time|seconds2hum}}"></span>
<span data-ls-if="{{execution.status}} === 'completed' || {{execution.status}} === 'failed'" data-ls-bind="{{execution.duration|seconds2hum}}"></span>
<span data-ls-if="{{execution.status}} === 'waiting' || {{execution.status}} === 'processing'">-</span>
</td>
<td data-title="">
<div data-ls-if="{{execution.status}} === 'completed' || {{execution.status}} === 'failed'" data-title="" style="display: flex;">
<button class="desktops-only pull-end link margin-start text-danger" data-ls-ui-trigger="execution-stderr-{{execution.$id}}">Stderr</button>
<button class="desktops-only pull-end link margin-start" data-ls-ui-trigger="execution-stdout-{{execution.$id}}">Stdout</button>
<!--button class="desktops-only pull-end link margin-start" data-ls-ui-trigger="execution-stdout-{{execution.$id}}">Stdout</button-->
<button class="desktops-only pull-end link margin-start" data-ls-ui-trigger="execution-response-{{execution.$id}}">Response</button>
<button class="phones-only-inline tablets-only-inline link margin-end-small" data-ls-ui-trigger="execution-response-{{execution.$id}}">Response</button>
<button class="phones-only-inline tablets-only-inline link margin-end-small" data-ls-ui-trigger="execution-stdout-{{execution.$id}}">Stdout</button>
<!--button class="phones-only-inline tablets-only-inline link margin-end-small" data-ls-ui-trigger="execution-stdout-{{execution.$id}}">Stdout</button-->
<button class="phones-only-inline tablets-only-inline link text-danger" data-ls-ui-trigger="execution-stderr-{{execution.$id}}">Stderr</button>
<div data-ui-modal class="modal width-large box close" data-button-alias="none" data-open-event="execution-response-{{execution.$id}}">
@ -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</h2>

View file

@ -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,

2
composer.lock generated
View file

@ -5393,5 +5393,5 @@
"platform-overrides": {
"php": "8.0"
},
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.2.0"
}

View file

@ -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()
}
```

View file

@ -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)
}
}
```

View file

@ -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);

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);

View file

@ -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()
}
}
```

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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)
```

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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) {

View file

@ -6,7 +6,7 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
stopOnFailure="true"
>
<extensions>
<extension class="Appwrite\Tests\TestHook" />

View file

@ -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,
]);
}
}

View file

@ -6,7 +6,7 @@ class Functions extends Base
{
public const ALLOWED_ATTRIBUTES = [
'name',
'status',
'enabled',
'runtime',
'deployment',
'schedule',

View file

@ -0,0 +1,275 @@
<?php
namespace Appwrite\Utopia\Request\Filters;
use Appwrite\Utopia\Request\Filter;
use Utopia\Database\Database;
use Utopia\Database\Permission;
use Utopia\Database\Query;
use Utopia\Database\Role;
class V15 extends Filter
{
// Convert 0.15 params format to 0.16 format
public function parse(array $content, string $model): array
{
switch ($model) {
case 'account.listLogs':
case 'databases.listLogs':
case 'databases.listCollectionLogs':
case 'databases.listDocumentLogs':
case 'teams.listLogs':
case 'users.listLogs':
$content = $this->convertLimitAndOffset($content);
break;
case 'account.initials':
unset($content['color']);
break;
case 'databases.list':
case 'databases.listCollections':
case 'functions.list':
case 'functions.listDeployments':
case 'projects.list':
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;
}
}

View file

@ -0,0 +1,577 @@
<?php
namespace Appwrite\Utopia\Response\Filters;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Filter;
use Utopia\Database\Database;
use Utopia\Database\Permission;
use Utopia\Database\Role;
class V15 extends Filter
{
// Convert 0.16 Data format to 0.15 format
public function parse(array $content, string $model): array
{
$parsedResponse = $content;
switch ($model) {
case Response::MODEL_ACCOUNT:
case Response::MODEL_USER:
$parsedResponse = $this->parseUser($parsedResponse);
break;
case Response::MODEL_USER_LIST:
$listKey = 'users';
$parsedResponse[$listKey] = array_map(fn ($content) => $this->parseUser($content), $parsedResponse[$listKey]);
break;
case Response::MODEL_METRIC:
$parsedResponse = $this->parseMetric($parsedResponse);
break;
case Response::MODEL_BUILD:
$parsedResponse = $this->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;
}
}

View file

@ -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,

View file

@ -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
]);
}
/**

View file

@ -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']));

View file

@ -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

View file

@ -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);

View file

@ -0,0 +1,742 @@
<?php
namespace Tests\Unit\Utopia\Request\Filters;
use Appwrite\Utopia\Request\Filter;
use Appwrite\Utopia\Request\Filters\V15;
use Appwrite\Utopia\Response\Model;
use PHPUnit\Framework\TestCase;
class V15Test extends TestCase
{
/**
* @var Filter
*/
protected $filter = null;
public function setUp(): void
{
$this->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);
}
}

File diff suppressed because it is too large Load diff