1
0
Fork 0
mirror of synced 2024-06-14 08:44:49 +12:00

Merge branch '0.16.x' of https://github.com/appwrite/appwrite into feat-additional-compression-support

This commit is contained in:
Damodar Lohani 2022-08-31 13:21:45 +00:00
commit 8fe365d66e
40 changed files with 1719 additions and 179 deletions

View file

@ -2113,12 +2113,12 @@ $collections = [
'$id' => ID::custom('vars'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 8192,
'size' => 16384,
'signed' => true,
'required' => false,
'default' => [],
'default' => null,
'array' => false,
'filters' => ['json', 'encrypt'],
'filters' => ['subQueryVariables'],
],
[
'$id' => ID::custom('events'),
@ -3420,6 +3420,99 @@ $collections = [
],
]
],
'variables' => [
'$collection' => Database::METADATA,
'$id' => 'variables',
'name' => 'variables',
'attributes' => [
[
'$id' => 'functionInternalId',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'functionId',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'key',
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => 'value',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 8192,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [ 'encrypt' ]
],
[
'$id' => ID::custom('search'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => '_key_function',
'type' => Database::INDEX_KEY,
'attributes' => ['functionInternalId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_uniqueKey',
'type' => Database::INDEX_UNIQUE,
'attributes' => ['functionInternalId', 'key'],
'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
],
[
'$id' => '_key_key',
'type' => Database::INDEX_KEY,
'attributes' => ['key'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_fulltext_search'),
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [],
'orders' => [],
],
],
],
];
return $collections;

View file

@ -534,6 +534,16 @@ return [
'description' => 'A Domain with the requested ID already exists.',
'code' => 409,
],
Exception::VARIABLE_NOT_FOUND => [
'name' => Exception::VARIABLE_NOT_FOUND,
'description' => 'Variable with the requested ID could not be found.',
'code' => 404,
],
Exception::VARIABLE_ALREADY_EXISTS => [
'name' => Exception::VARIABLE_ALREADY_EXISTS,
'description' => 'Variable with the same ID already exists in your project.',
'code' => 409,
],
Exception::DOMAIN_VERIFICATION_FAILED => [
'name' => Exception::DOMAIN_VERIFICATION_FAILED,
'description' => 'Domain verification for the requested domain has failed.',

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -246,7 +246,8 @@ App::get('/v1/databases')
}
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
$cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE);
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$databaseId = $cursor->getValue();
@ -566,7 +567,8 @@ App::get('/v1/databases/:databaseId/collections')
}
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
$cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE);
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$collectionId = $cursor->getValue();
@ -1811,8 +1813,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
->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}'])
->label('abuse-limit', 120)
->label('abuse-time', 60)
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'createDocument')
@ -1979,7 +1982,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
$queries = Query::parseQueries($queries);
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
$cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE);
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$documentId = $cursor->getValue();
@ -2189,8 +2193,9 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
->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}'])
->label('abuse-limit', 60)
->label('abuse-time', 60)
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'updateDocument')
@ -2320,8 +2325,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
->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}'])
->label('abuse-limit', 60)
->label('abuse-time', 60)
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'deleteDocument')

View file

@ -25,6 +25,7 @@ use Appwrite\Task\Validator\Cron;
use Appwrite\Utopia\Database\Validator\Queries\Deployments;
use Appwrite\Utopia\Database\Validator\Queries\Executions;
use Appwrite\Utopia\Database\Validator\Queries\Functions;
use Appwrite\Utopia\Database\Validator\Queries\Variables;
use Utopia\App;
use Utopia\Database\Database;
use Utopia\Database\Document;
@ -42,6 +43,7 @@ use Executor\Executor;
use Utopia\CLI\Console;
use Utopia\Database\Validator\Roles;
use Utopia\Validator\Boolean;
use Utopia\Database\Exception\Duplicate as DuplicateException;
include_once __DIR__ . '/../shared/api.php';
@ -62,14 +64,13 @@ App::post('/v1/functions')
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution roles. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.')
->param('runtime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution runtime.')
->param('vars', [], new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
->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)
->inject('response')
->inject('dbForProject')
->inject('events')
->action(function (string $functionId, string $name, array $execute, string $runtime, array $vars, 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, Response $response, Database $dbForProject, Event $eventsInstance) {
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
$function = $dbForProject->createDocument('functions', new Document([
@ -79,13 +80,12 @@ App::post('/v1/functions')
'name' => $name,
'runtime' => $runtime,
'deployment' => '',
'vars' => $vars,
'events' => $events,
'schedule' => $schedule,
'schedulePrevious' => null,
'scheduleNext' => null,
'timeout' => $timeout,
'search' => implode(' ', [$functionId, $name, $runtime]),
'search' => implode(' ', [$functionId, $name, $runtime])
]));
$eventsInstance->setParam('functionId', $function->getId());
@ -118,7 +118,8 @@ App::get('/v1/functions')
}
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
$cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE);
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$functionId = $cursor->getValue();
@ -417,7 +418,6 @@ App::put('/v1/functions/:functionId')
->param('functionId', '', new UID(), 'Function ID.')
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution roles. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.')
->param('vars', [], new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
->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)
@ -426,7 +426,7 @@ App::put('/v1/functions/:functionId')
->inject('project')
->inject('user')
->inject('events')
->action(function (string $functionId, string $name, array $execute, array $vars, 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, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance) {
$function = $dbForProject->getDocument('functions', $functionId);
@ -441,7 +441,6 @@ App::put('/v1/functions/:functionId')
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
'execute' => $execute,
'name' => $name,
'vars' => $vars,
'events' => $events,
'schedule' => $schedule,
'scheduleNext' => $next,
@ -792,7 +791,8 @@ App::get('/v1/functions/:functionId/deployments')
$queries[] = Query::equal('resourceType', ['functions']);
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
$cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE);
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$deploymentId = $cursor->getValue();
@ -929,8 +929,9 @@ App::post('/v1/functions/:functionId/executions')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_EXECUTION)
->label('abuse-limit', 60)
->label('abuse-time', 60)
->label('abuse-key', 'ip:{ip},userId:{userId}')
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->param('functionId', '', new UID(), 'Function ID.')
->param('data', '', new Text(8192), 'String of custom data to send to function.', true)
->param('async', true, new Boolean(), 'Execute code asynchronously. Default value is true.', true)
@ -1043,8 +1044,12 @@ App::post('/v1/functions/:functionId/executions')
return $response->dynamic($execution, Response::MODEL_EXECUTION);
}
/** Collect environment variables */
$vars = \array_merge($function->getAttribute('vars', []), [
$vars = array_reduce($function['vars'] ?? [], function (array $carry, Document $var) {
$carry[$var->getAttribute('key')] = $var->getAttribute('value');
return $carry;
}, []);
$vars = \array_merge($vars, [
'APPWRITE_FUNCTION_ID' => $function->getId(),
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(),
@ -1145,7 +1150,8 @@ App::get('/v1/functions/:functionId/executions')
$queries[] = Query::equal('functionId', [$function->getId()]);
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
$cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE);
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$executionId = $cursor->getValue();
@ -1281,3 +1287,228 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
$response->noContent();
});
// Variables
App::post('/v1/functions/:functionId/variables')
->desc('Create Variable')
->groups(['api', 'functions'])
->label('scope', 'functions.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'createVariable')
->label('sdk.description', '/docs/references/functions/create-variable.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_VARIABLE)
->param('functionId', null, new UID(), 'Function unique ID.', false)
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', false)
->inject('response')
->inject('dbForProject')
->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$variableId = ID::unique();
$variable = new Document([
'$id' => $variableId,
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'functionInternalId' => $function->getInternalId(),
'functionId' => $function->getId(),
'key' => $key,
'value' => $value,
'search' => implode(' ', [$variableId, $function->getId(), $key]),
]);
try {
$variable = $dbForProject->createDocument('variables', $variable);
} catch (DuplicateException $th) {
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
}
$dbForProject->deleteCachedDocument('functions', $function->getId());
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($variable, Response::MODEL_VARIABLE);
});
App::get('/v1/functions/:functionId/variables')
->desc('List Variables')
->groups(['api', 'functions'])
->label('scope', 'functions.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'listVariables')
->label('sdk.description', '/docs/references/functions/list-variables.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->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) {
$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 = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
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),
]), Response::MODEL_VARIABLE_LIST);
});
App::get('/v1/functions/:functionId/variables/:variableId')
->desc('Get Variable')
->groups(['api', 'functions'])
->label('scope', 'functions.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getVariable')
->label('sdk.description', '/docs/references/functions/get-variable.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_VARIABLE)
->param('functionId', null, new UID(), 'Function unique ID.', false)
->param('variableId', null, new UID(), 'Variable unique ID.', false)
->inject('response')
->inject('dbForProject')
->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$variable = $dbForProject->findOne('variables', [
Query::equal('$id', [$variableId]),
Query::equal('functionInternalId', [$function->getInternalId()]),
]);
if ($variable === false || $variable->isEmpty()) {
throw new Exception(Exception::VARIABLE_NOT_FOUND);
}
$response->dynamic($variable, Response::MODEL_VARIABLE);
});
App::put('/v1/functions/:functionId/variables/:variableId')
->desc('Update Variable')
->groups(['api', 'functions'])
->label('scope', 'functions.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'updateVariable')
->label('sdk.description', '/docs/references/functions/update-variable.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_VARIABLE)
->param('functionId', null, new UID(), 'Function unique ID.', false)
->param('variableId', null, new UID(), 'Variable unique ID.', false)
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$variable = $dbForProject->findOne('variables', [
Query::equal('$id', [$variableId]),
Query::equal('functionInternalId', [$function->getInternalId()]),
]);
if ($variable === false || $variable->isEmpty()) {
throw new Exception(Exception::VARIABLE_NOT_FOUND);
}
$variable
->setAttribute('key', $key)
->setAttribute('value', $value ?? $variable->getAttribute('value'))
->setAttribute('search', implode(' ', [$variableId, $function->getId(), $key]))
;
try {
$dbForProject->updateDocument('variables', $variable->getId(), $variable);
} catch (DuplicateException $th) {
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
}
$dbForProject->deleteCachedDocument('functions', $function->getId());
$response->dynamic($variable, Response::MODEL_VARIABLE);
});
App::delete('/v1/functions/:functionId/variables/:variableId')
->desc('Delete Variable')
->groups(['api', 'functions'])
->label('scope', 'functions.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'deleteVariable')
->label('sdk.description', '/docs/references/functions/delete-variable.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('functionId', null, new UID(), 'Function unique ID.', false)
->param('variableId', null, new UID(), 'Variable unique ID.', false)
->inject('response')
->inject('dbForProject')
->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$variable = $dbForProject->findOne('variables', [
Query::equal('$id', [$variableId]),
Query::equal('functionInternalId', [$function->getInternalId()]),
]);
if ($variable === false || $variable->isEmpty()) {
throw new Exception(Exception::VARIABLE_NOT_FOUND);
}
$dbForProject->deleteDocument('variables', $variable->getId());
$dbForProject->deleteCachedDocument('functions', $function->getId());
$response->noContent();
});

View file

@ -165,7 +165,8 @@ App::get('/v1/storage/buckets')
}
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
$cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE);
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$bucketId = $cursor->getValue();
@ -328,8 +329,9 @@ App::post('/v1/storage/buckets/:bucketId/files')
->label('audits.resource', 'files/{response.$id}')
->label('usage.metric', 'files.{scope}.requests.create')
->label('usage.params', ['bucketId:{request.bucketId}'])
->label('abuse-limit', 60)
->label('abuse-time', 60)
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'createFile')
@ -687,7 +689,8 @@ App::get('/v1/storage/buckets/:bucketId/files')
}
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
$cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE);
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$fileId = $cursor->getValue();
@ -1236,8 +1239,9 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
->label('audits.resource', 'files/{response.$id}')
->label('usage.metric', 'files.{scope}.requests.update')
->label('usage.params', ['bucketId:{request.bucketId}'])
->label('abuse-limit', 60)
->label('abuse-time', 60)
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'updateFile')
@ -1337,8 +1341,9 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
->label('audits.resource', 'file/{request.fileId}')
->label('usage.metric', 'files.{scope}.requests.delete')
->label('usage.params', ['bucketId:{request.bucketId}'])
->label('abuse-limit', 60)
->label('abuse-time', 60)
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'deleteFile')

View file

@ -144,7 +144,8 @@ App::get('/v1/teams')
}
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
$cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE);
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$teamId = $cursor->getValue();
@ -489,7 +490,8 @@ App::get('/v1/teams/:teamId/memberships')
$queries[] = Query::equal('teamId', [$teamId]);
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
$cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE);
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$membershipId = $cursor->getValue();

View file

@ -356,7 +356,8 @@ App::get('/v1/users')
}
// Get cursor document if there was a cursor query
$cursor = reset(Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE));
$cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE);
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$userId = $cursor->getValue();

View file

@ -83,7 +83,8 @@ App::init()
->setParam('{userId}', $user->getId())
->setParam('{userAgent}', $request->getUserAgent(''))
->setParam('{ip}', $request->getIP())
->setParam('{url}', $request->getHostname() . $route->getPath());
->setParam('{url}', $request->getHostname() . $route->getPath())
->setParam('{method}', $request->getMethod());
$timeLimitArray[] = $timeLimit;
}
@ -114,11 +115,14 @@ App::init()
;
}
$enabled = App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled';
if (
(App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled' // Route is rate-limited
&& $abuse->check()) // Abuse is not disabled
&& (!$isAppUser && !$isPrivilegedUser)
) { // User is not an admin or API key
$enabled // Abuse is enabled
&& !$isAppUser // User is not API key
&& !$isPrivilegedUser // User is not an admin
&& $abuse->check() // Route is rate-limited
) {
throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED);
}
}

View file

@ -90,6 +90,8 @@ const APP_LIMIT_COMPRESSION = 20000000; //20MB
const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value
const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length.
const APP_LIMIT_SUBQUERY = 1000;
const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period
const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds
const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 402;
const APP_VERSION_STABLE = '0.15.3';
@ -396,6 +398,19 @@ Database::addFilter(
}
);
Database::addFilter(
'subQueryVariables',
function (mixed $value) {
return null;
},
function (mixed $value, Document $document, Database $database) {
return $database
->find('variables', [
Query::equal('functionInternalId', [$document->getInternalId()]),
], APP_LIMIT_SUBQUERY);
}
);
Database::addFilter(
'encrypt',
function (mixed $value) {

View file

@ -522,6 +522,156 @@ sort($patterns);
</div>
</div>
</li>
<li data-state="/console/functions/function/variables?id={{router.params.id}}&project={{router.params.project}}">
<h2
data-service="functions.listVariables"
data-event="load,project.update,functions.createVariable,functions.updateVariable,functions.deleteVariable"
data-name="function-variables"
data-param-function-id="{{router.params.id}}"
data-scope="sdk">Variables</h2>
<div class="box margin-bottom" data-ls-if="0 < {{function-variables.variables.length}} && undefined !== {{function-variables.variables}}">
<ul data-ls-loop="function-variables.variables" data-ls-as="variable" class="list">
<li class="clear">
<div class="pull-end desktops-only">
<button data-ls-ui-trigger="variable-delete-{{variable.$id}}" class="reverse danger margin-end-small">Delete</button>
<button data-ls-ui-trigger="variable-update-{{variable.$id}}">Update</button>
</div>
<div data-ui-modal data-button-hide="on" data-open-event="variable-update-{{variable.$id}}">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Update Variable</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="sdk"
data-analytics-label="Update Function Variable"
data-service="functions.updateVariable"
data-scope="sdk"
data-event="submit"
data-success="alert,trigger"
data-success-param-alert-text="Updated variable successfully"
data-success-param-trigger-events="functions.updateVariable"
data-failure="alert,trigger"
data-failure-param-trigger-events="functions.updateVariable"
data-failure-param-alert-text="Failed to update variable"
data-failure-param-alert-classname="error">
<input type="hidden" name="functionId" data-ls-bind="{{router.params.id}}" />
<input type="hidden" name="variableId" data-ls-bind="{{variable.$id}}" />
<label for="name">Name <span class="tooltip large" data-tooltip="The name of the environment variable you want to set"><i class="icon-question"></i></span></label>
<input type="text" class="full-width" name="key" required autocomplete="off" placeholder="APP_ENV" maxlength="128" data-ls-attrs="id=key-{{variable.$id}}" data-ls-bind="{{variable.key}}" />
<label for="key">Value <span class="tooltip large" data-tooltip="The value of your environment variable"><i class="icon-question"></i></span></label>
<input type="text" class="full-width" name="value" required autocomplete="off" placeholder="Production" data-ls-attrs="id=value-{{variable.$id}}}" data-ls-bind="{{variable.value}}" />
<hr />
<button type="submit">Update</button> &nbsp; <button data-ls-ui-trigger="modal-close" type="button" class="reverse">Cancel</button>
</form>
</div>
<form class="pull-end margin-end"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Function Variable"
data-service="functions.deleteVariable"
data-scope="sdk"
data-event="variable-delete-{{variable.$id}}"
data-confirm="Are you sure you want to delete this variable?"
data-success="alert,trigger"
data-success-param-alert-text="Deleted variable successfully"
data-success-param-trigger-events="functions.deleteVariable"
data-failure="alert"
data-failure-param-alert-text="Failed to delete variable"
data-failure-param-alert-classname="error">
<input type="hidden" name="functionId" data-ls-bind="{{router.params.id}}" />
<input type="hidden" name="variableId" data-ls-bind="{{variable.$id}}" />
</form>
<div>
<span class="text-one-liner" data-ls-bind="{{variable.key}}"></span>
</div>
<div data-ui-modal class="modal box close" data-button-text="Show Value" data-button-class="link pull-start margin-end-small margin-top-tiny">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Variable Value</h1>
<form>
<div class="input-copy">
<textarea disabled data-forms-copy data-ls-bind="{{variable.value}}"></textarea>
</div>
<hr />
<div>
<button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</div>
</form>
</div>
<div class="phones-only-inline tablets-only-inline margin-top-small">
<button class="link" data-ls-ui-trigger="variable-update-{{variable.$id}}">Update</button>
<button class="link danger" data-ls-ui-trigger="variable-delete-{{variable.$id}}">Delete</button>
</div>
</li>
</ul>
</div>
<div data-ls-if="(!{{function-variables.variables.length}})" class="box dashboard margin-bottom">
<div class="margin-bottom-small margin-top-small margin-end margin-start">
<h3 class="margin-bottom-small text-bold">There are currently no variables</h3>
<p class="margin-bottom-no">Add your first variable to store information securely.</p>
</div>
</div>
<div data-ui-modal class="modal box close" data-button-alias=".variable-new">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Create a new variable</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create New Function Variable"
data-service="functions.createVariable"
data-event="submit"
data-success="alert,trigger,reset"
data-success-param-alert-text="Registered new variable successfully"
data-success-param-trigger-events="functions.createVariable"
data-failure="alert"
data-failure-param-alert-text="Failed to register variable"
data-failure-param-alert-classname="error">
<input type="hidden" name="functionId" data-ls-bind="{{router.params.id}}" />
<label for="name">Name <span class="tooltip large" data-tooltip="The name of the environment variable you want to set"><i class="icon-question"></i></span></label>
<input type="text" class="full-width" name="key" required autocomplete="off" placeholder="APP_ENV" maxlength="128" />
<label for="key">Value <span class="tooltip large" data-tooltip="The value of your environment variable"><i class="icon-question"></i></span></label>
<input type="text" class="full-width" name="value" required autocomplete="off" placeholder="Production" />
<hr />
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
</div>
<div class="pull-start link variable-new" data-ls-ui-open="" data-button-aria="Add Variable" data-button-text="Add Variable" data-button-class="button" data-blur="1">
</div>
</li>
<li data-state="/console/functions/function/settings?id={{router.params.id}}&project={{router.params.project}}" x-data="events">
<h2>Settings</h2>

View file

@ -15,6 +15,7 @@ use Utopia\Database\ID;
use Utopia\Storage\Storage;
use Utopia\Database\Document;
use Utopia\Config\Config;
use Utopia\Database\Query;
require_once __DIR__ . '/../init.php';
@ -144,7 +145,12 @@ class BuildsV1 extends Worker
);
$source = $deployment->getAttribute('path');
$vars = $function->getAttribute('vars', []);
$vars = array_reduce($function['vars'] ?? [], function (array $carry, Document $var) {
$carry[$var->getAttribute('key')] = $var->getAttribute('value');
return $carry;
}, []);
$baseImage = $runtime['image'];
try {

View file

@ -259,8 +259,13 @@ class FunctionsV1 extends Worker
$execution->setAttribute('status', 'processing');
$execution = $dbForProject->updateDocument('executions', $executionId, $execution);
$vars = array_reduce($function['vars'] ?? [], function (array $carry, Document $var) {
$carry[$var->getAttribute('key')] = $var->getAttribute('value');
return $carry;
}, []);
/** Collect environment variables */
$vars = [
$vars = \array_merge($vars, [
'APPWRITE_FUNCTION_ID' => $functionId,
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deploymentId,
@ -273,8 +278,7 @@ class FunctionsV1 extends Worker
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
'APPWRITE_FUNCTION_USER_ID' => $user->getId(),
'APPWRITE_FUNCTION_JWT' => $jwt,
];
$vars = \array_merge($function->getAttribute('vars', []), $vars);
]);
/** Execute function */
try {

View file

@ -0,0 +1 @@
Create a new function variable. These variables can be accessed within function in the `env` object under the request variable.

View file

@ -0,0 +1 @@
Delete a variable by its unique ID.

View file

@ -0,0 +1 @@
Get a variable by its unique ID.

View file

@ -0,0 +1 @@
Get a list of all variables of a specific function.

View file

@ -0,0 +1 @@
Update variable by its unique ID.

View file

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

View file

@ -406,7 +406,7 @@ class Functions extends Service{constructor(client){super(client);}
list(queries,search){return __awaiter(this,void 0,void 0,function*(){let path='/functions';let payload={};if(typeof queries!=='undefined'){payload['queries']=queries;}
if(typeof search!=='undefined'){payload['search']=search;}
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
create(functionId,name,execute,runtime,vars,events,schedule,timeout){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
create(functionId,name,execute,runtime,events,schedule,timeout){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof execute==='undefined'){throw new AppwriteException('Missing required parameter: "execute"');}
if(typeof runtime==='undefined'){throw new AppwriteException('Missing required parameter: "runtime"');}
@ -414,7 +414,6 @@ let path='/functions';let payload={};if(typeof functionId!=='undefined'){payload
if(typeof name!=='undefined'){payload['name']=name;}
if(typeof execute!=='undefined'){payload['execute']=execute;}
if(typeof runtime!=='undefined'){payload['runtime']=runtime;}
if(typeof vars!=='undefined'){payload['vars']=vars;}
if(typeof events!=='undefined'){payload['events']=events;}
if(typeof schedule!=='undefined'){payload['schedule']=schedule;}
if(typeof timeout!=='undefined'){payload['timeout']=timeout;}
@ -424,12 +423,11 @@ getUsage(range){return __awaiter(this,void 0,void 0,function*(){let path='/funct
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
get(functionId){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
update(functionId,name,execute,vars,events,schedule,timeout){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
update(functionId,name,execute,events,schedule,timeout){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof execute==='undefined'){throw new AppwriteException('Missing required parameter: "execute"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
if(typeof execute!=='undefined'){payload['execute']=execute;}
if(typeof vars!=='undefined'){payload['vars']=vars;}
if(typeof events!=='undefined'){payload['events']=events;}
if(typeof schedule!=='undefined'){payload['schedule']=schedule;}
if(typeof timeout!=='undefined'){payload['timeout']=timeout;}
@ -479,7 +477,26 @@ if(typeof executionId==='undefined'){throw new AppwriteException('Missing requir
let path='/functions/{functionId}/executions/{executionId}'.replace('{functionId}',functionId).replace('{executionId}',executionId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
getFunctionUsage(functionId,range){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/usage'.replace('{functionId}',functionId);let payload={};if(typeof range!=='undefined'){payload['range']=range;}
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}}
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
listVariables(functionId){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/variables'.replace('{functionId}',functionId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
createVariable(functionId,key,value){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof key==='undefined'){throw new AppwriteException('Missing required parameter: "key"');}
if(typeof value==='undefined'){throw new AppwriteException('Missing required parameter: "value"');}
let path='/functions/{functionId}/variables'.replace('{functionId}',functionId);let payload={};if(typeof key!=='undefined'){payload['key']=key;}
if(typeof value!=='undefined'){payload['value']=value;}
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('post',uri,{'content-type':'application/json',},payload);});}
getVariable(functionId,variableId){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof variableId==='undefined'){throw new AppwriteException('Missing required parameter: "variableId"');}
let path='/functions/{functionId}/variables/{variableId}'.replace('{functionId}',functionId).replace('{variableId}',variableId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
updateVariable(functionId,variableId,key,value){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof variableId==='undefined'){throw new AppwriteException('Missing required parameter: "variableId"');}
let path='/functions/{functionId}/variables/{variableId}'.replace('{functionId}',functionId).replace('{variableId}',variableId);let payload={};if(typeof key!=='undefined'){payload['key']=key;}
if(typeof value!=='undefined'){payload['value']=value;}
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('put',uri,{'content-type':'application/json',},payload);});}
deleteVariable(functionId,variableId){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof variableId==='undefined'){throw new AppwriteException('Missing required parameter: "variableId"');}
let path='/functions/{functionId}/variables/{variableId}'.replace('{functionId}',functionId).replace('{variableId}',variableId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('delete',uri,{'content-type':'application/json',},payload);});}}
class Health extends Service{constructor(client){super(client);}
get(){return __awaiter(this,void 0,void 0,function*(){let path='/health';let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
getAntivirus(){return __awaiter(this,void 0,void 0,function*(){let path='/health/anti-virus';let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}

View file

@ -406,7 +406,7 @@ class Functions extends Service{constructor(client){super(client);}
list(queries,search){return __awaiter(this,void 0,void 0,function*(){let path='/functions';let payload={};if(typeof queries!=='undefined'){payload['queries']=queries;}
if(typeof search!=='undefined'){payload['search']=search;}
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
create(functionId,name,execute,runtime,vars,events,schedule,timeout){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
create(functionId,name,execute,runtime,events,schedule,timeout){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof execute==='undefined'){throw new AppwriteException('Missing required parameter: "execute"');}
if(typeof runtime==='undefined'){throw new AppwriteException('Missing required parameter: "runtime"');}
@ -414,7 +414,6 @@ let path='/functions';let payload={};if(typeof functionId!=='undefined'){payload
if(typeof name!=='undefined'){payload['name']=name;}
if(typeof execute!=='undefined'){payload['execute']=execute;}
if(typeof runtime!=='undefined'){payload['runtime']=runtime;}
if(typeof vars!=='undefined'){payload['vars']=vars;}
if(typeof events!=='undefined'){payload['events']=events;}
if(typeof schedule!=='undefined'){payload['schedule']=schedule;}
if(typeof timeout!=='undefined'){payload['timeout']=timeout;}
@ -424,12 +423,11 @@ getUsage(range){return __awaiter(this,void 0,void 0,function*(){let path='/funct
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
get(functionId){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
update(functionId,name,execute,vars,events,schedule,timeout){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
update(functionId,name,execute,events,schedule,timeout){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof execute==='undefined'){throw new AppwriteException('Missing required parameter: "execute"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
if(typeof execute!=='undefined'){payload['execute']=execute;}
if(typeof vars!=='undefined'){payload['vars']=vars;}
if(typeof events!=='undefined'){payload['events']=events;}
if(typeof schedule!=='undefined'){payload['schedule']=schedule;}
if(typeof timeout!=='undefined'){payload['timeout']=timeout;}
@ -479,7 +477,26 @@ if(typeof executionId==='undefined'){throw new AppwriteException('Missing requir
let path='/functions/{functionId}/executions/{executionId}'.replace('{functionId}',functionId).replace('{executionId}',executionId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
getFunctionUsage(functionId,range){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/usage'.replace('{functionId}',functionId);let payload={};if(typeof range!=='undefined'){payload['range']=range;}
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}}
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
listVariables(functionId){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/variables'.replace('{functionId}',functionId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
createVariable(functionId,key,value){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof key==='undefined'){throw new AppwriteException('Missing required parameter: "key"');}
if(typeof value==='undefined'){throw new AppwriteException('Missing required parameter: "value"');}
let path='/functions/{functionId}/variables'.replace('{functionId}',functionId);let payload={};if(typeof key!=='undefined'){payload['key']=key;}
if(typeof value!=='undefined'){payload['value']=value;}
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('post',uri,{'content-type':'application/json',},payload);});}
getVariable(functionId,variableId){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof variableId==='undefined'){throw new AppwriteException('Missing required parameter: "variableId"');}
let path='/functions/{functionId}/variables/{variableId}'.replace('{functionId}',functionId).replace('{variableId}',variableId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
updateVariable(functionId,variableId,key,value){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof variableId==='undefined'){throw new AppwriteException('Missing required parameter: "variableId"');}
let path='/functions/{functionId}/variables/{variableId}'.replace('{functionId}',functionId).replace('{variableId}',variableId);let payload={};if(typeof key!=='undefined'){payload['key']=key;}
if(typeof value!=='undefined'){payload['value']=value;}
const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('put',uri,{'content-type':'application/json',},payload);});}
deleteVariable(functionId,variableId){return __awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof variableId==='undefined'){throw new AppwriteException('Missing required parameter: "variableId"');}
let path='/functions/{functionId}/variables/{variableId}'.replace('{functionId}',functionId).replace('{variableId}',variableId);let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('delete',uri,{'content-type':'application/json',},payload);});}}
class Health extends Service{constructor(client){super(client);}
get(){return __awaiter(this,void 0,void 0,function*(){let path='/health';let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}
getAntivirus(){return __awaiter(this,void 0,void 0,function*(){let path='/health/anti-virus';let payload={};const uri=new URL(this.client.config.endpoint+path);return yield this.client.call('get',uri,{'content-type':'application/json',},payload);});}

View file

@ -3037,14 +3037,13 @@
* @param {string} name
* @param {string[]} execute
* @param {string} runtime
* @param {object} vars
* @param {string[]} events
* @param {string} schedule
* @param {number} timeout
* @throws {AppwriteException}
* @returns {Promise}
*/
create(functionId, name, execute, runtime, vars, events, schedule, timeout) {
create(functionId, name, execute, runtime, events, schedule, timeout) {
return __awaiter(this, void 0, void 0, function* () {
if (typeof functionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "functionId"');
@ -3072,9 +3071,6 @@
if (typeof runtime !== 'undefined') {
payload['runtime'] = runtime;
}
if (typeof vars !== 'undefined') {
payload['vars'] = vars;
}
if (typeof events !== 'undefined') {
payload['events'] = events;
}
@ -3159,14 +3155,13 @@
* @param {string} functionId
* @param {string} name
* @param {string[]} execute
* @param {object} vars
* @param {string[]} events
* @param {string} schedule
* @param {number} timeout
* @throws {AppwriteException}
* @returns {Promise}
*/
update(functionId, name, execute, vars, events, schedule, timeout) {
update(functionId, name, execute, events, schedule, timeout) {
return __awaiter(this, void 0, void 0, function* () {
if (typeof functionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "functionId"');
@ -3185,9 +3180,6 @@
if (typeof execute !== 'undefined') {
payload['execute'] = execute;
}
if (typeof vars !== 'undefined') {
payload['vars'] = vars;
}
if (typeof events !== 'undefined') {
payload['events'] = events;
}
@ -3571,6 +3563,152 @@
}, payload);
});
}
/**
* List Variables
*
* Get a list of all variables that are currently active on your function.
*
* @param {string} functionId
* @throws {AppwriteException}
* @returns {Promise}
*/
listVariables(functionId) {
return __awaiter(this, void 0, void 0, function* () {
if (typeof functionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "functionId"');
}
let path = '/functions/{functionId}/variables'.replace('{functionId}', functionId);
let payload = {};
const uri = new URL(this.client.config.endpoint + path);
return yield this.client.call('get', uri, {
'content-type': 'application/json',
}, payload);
});
}
/**
* Create Variable
*
* Create a new function variable. These variables can be accessed within
* functions using the `env` object recieved through the payload of an
* execution.
*
* @param {string} functionId
* @param {string} key
* @param {string} value
* @throws {AppwriteException}
* @returns {Promise}
*/
createVariable(functionId, key, value) {
return __awaiter(this, void 0, void 0, function* () {
if (typeof functionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "functionId"');
}
if (typeof key === 'undefined') {
throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof value === 'undefined') {
throw new AppwriteException('Missing required parameter: "value"');
}
let path = '/functions/{functionId}/variables'.replace('{functionId}', functionId);
let payload = {};
if (typeof key !== 'undefined') {
payload['key'] = key;
}
if (typeof value !== 'undefined') {
payload['value'] = value;
}
const uri = new URL(this.client.config.endpoint + path);
return yield this.client.call('post', uri, {
'content-type': 'application/json',
}, payload);
});
}
/**
* Get Variable
*
* Get a variable by its unique ID.
*
* @param {string} functionId
* @param {string} variableId
* @throws {AppwriteException}
* @returns {Promise}
*/
getVariable(functionId, variableId) {
return __awaiter(this, void 0, void 0, function* () {
if (typeof functionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "functionId"');
}
if (typeof variableId === 'undefined') {
throw new AppwriteException('Missing required parameter: "variableId"');
}
let path = '/functions/{functionId}/variables/{variableId}'.replace('{functionId}', functionId).replace('{variableId}', variableId);
let payload = {};
const uri = new URL(this.client.config.endpoint + path);
return yield this.client.call('get', uri, {
'content-type': 'application/json',
}, payload);
});
}
/**
* Update Variable
*
* Update variable by its unique ID.
*
* @param {string} functionId
* @param {string} variableId
* @param {string} key
* @param {string} value
* @throws {AppwriteException}
* @returns {Promise}
*/
updateVariable(functionId, variableId, key, value) {
return __awaiter(this, void 0, void 0, function* () {
if (typeof functionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "functionId"');
}
if (typeof variableId === 'undefined') {
throw new AppwriteException('Missing required parameter: "variableId"');
}
let path = '/functions/{functionId}/variables/{variableId}'.replace('{functionId}', functionId).replace('{variableId}', variableId);
let payload = {};
if (typeof key !== 'undefined') {
payload['key'] = key;
}
if (typeof value !== 'undefined') {
payload['value'] = value;
}
const uri = new URL(this.client.config.endpoint + path);
return yield this.client.call('put', uri, {
'content-type': 'application/json',
}, payload);
});
}
/**
* Delete Variable
*
* Delete a variable by its unique ID.
*
* @param {string} functionId
* @param {string} variableId
* @throws {AppwriteException}
* @returns {Promise}
*/
deleteVariable(functionId, variableId) {
return __awaiter(this, void 0, void 0, function* () {
if (typeof functionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "functionId"');
}
if (typeof variableId === 'undefined') {
throw new AppwriteException('Missing required parameter: "variableId"');
}
let path = '/functions/{functionId}/variables/{variableId}'.replace('{functionId}', functionId).replace('{variableId}', variableId);
let payload = {};
const uri = new URL(this.client.config.endpoint + path);
return yield this.client.call('delete', uri, {
'content-type': 'application/json',
}, payload);
});
}
}
class Health extends Service {

View file

@ -165,6 +165,10 @@ class Exception extends \Exception
/** Keys */
public const KEY_NOT_FOUND = 'key_not_found';
/** Variables */
public const VARIABLE_NOT_FOUND = 'variable_not_found';
public const VARIABLE_ALREADY_EXISTS = 'variable_already_exists';
/** Platform */
public const PLATFORM_NOT_FOUND = 'platform_not_found';

View file

@ -0,0 +1,21 @@
<?php
namespace Appwrite\Utopia\Database\Validator\Queries;
use Appwrite\Utopia\Database\Validator\Queries\Base;
class Variables extends Base
{
public const ALLOWED_ATTRIBUTES = [
'key'
];
/**
* Expression constructor
*
*/
public function __construct()
{
parent::__construct('variables', self::ALLOWED_ATTRIBUTES);
}
}

View file

@ -80,6 +80,7 @@ use Appwrite\Utopia\Response\Model\UsageFunctions;
use Appwrite\Utopia\Response\Model\UsageProject;
use Appwrite\Utopia\Response\Model\UsageStorage;
use Appwrite\Utopia\Response\Model\UsageUsers;
use Appwrite\Utopia\Response\Model\Variable;
/**
* @method Response setStatusCode(int $code = 200)
@ -197,6 +198,8 @@ class Response extends SwooleResponse
public const MODEL_PLATFORM_LIST = 'platformList';
public const MODEL_DOMAIN = 'domain';
public const MODEL_DOMAIN_LIST = 'domainList';
public const MODEL_VARIABLE = 'variable';
public const MODEL_VARIABLE_LIST = 'variableList';
// Health
public const MODEL_HEALTH_STATUS = 'healthStatus';
@ -264,6 +267,7 @@ class Response extends SwooleResponse
->setModel(new BaseList('Currencies List', self::MODEL_CURRENCY_LIST, 'currencies', self::MODEL_CURRENCY))
->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE))
->setModel(new BaseList('Metric List', self::MODEL_METRIC_LIST, 'metrics', self::MODEL_METRIC, true, false))
->setModel(new BaseList('Variables List', self::MODEL_VARIABLE_LIST, 'variables', self::MODEL_VARIABLE))
// Entities
->setModel(new Database())
->setModel(new Collection())
@ -309,6 +313,7 @@ class Response extends SwooleResponse
->setModel(new Key())
->setModel(new Domain())
->setModel(new Platform())
->setModel(new Variable())
->setModel(new Country())
->setModel(new Continent())
->setModel(new Language())

View file

@ -23,16 +23,16 @@ class Database extends Model
'example' => 'My Database',
])
->addRule('$createdAt', [
'type' => self::TYPE_INTEGER,
'description' => 'Collection creation date in Unix timestamp.',
'default' => 0,
'example' => 1592981250,
'type' => self::TYPE_DATETIME,
'description' => 'Database creation date in Datetime',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('$updatedAt', [
'type' => self::TYPE_INTEGER,
'description' => 'Collection update date in Unix timestamp.',
'default' => 0,
'example' => 1592981250,
'type' => self::TYPE_DATETIME,
'description' => 'Database update date in Datetime',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
;
}

View file

@ -62,10 +62,11 @@ class Func extends Model
'example' => '5e5ea5c16897e',
])
->addRule('vars', [
'type' => self::TYPE_JSON,
'description' => 'Function environment variables.',
'default' => new \stdClass(),
'example' => ['key' => 'value'],
'type' => Response::MODEL_VARIABLE,
'description' => 'Function variables.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('events', [
'type' => self::TYPE_STRING,
@ -120,25 +121,4 @@ class Func extends Model
{
return Response::MODEL_FUNCTION;
}
/**
* Filter Function
*
* Automatically converts a [] default to a stdClass, this is called while grabbing the document.
*
* @param Document $document
* @return Document
*/
public function filter(Document $document): Document
{
$vars = $document->getAttribute('vars');
if ($vars instanceof Document) {
$vars = $vars->getArrayCopy();
}
if (is_array($vars) && empty($vars)) {
$document->setAttribute('vars', new stdClass());
}
return $document;
}
}

View file

@ -36,7 +36,7 @@ class Token extends Model
])
->addRule('expire', [
'type' => self::TYPE_DATETIME,
'description' => 'Token expiration date in Unix timestamp.',
'description' => 'Token expiration date in Datetime.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])

View file

@ -0,0 +1,72 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Variable extends Model
{
public function __construct()
{
$this
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'Variable ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('$createdAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Variable creation date in Datetime',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('$updatedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Variable creation date in Datetime',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('key', [
'type' => self::TYPE_STRING,
'description' => 'Variable key.',
'default' => '',
'example' => 'API_KEY',
'array' => false,
])
->addRule('value', [
'type' => self::TYPE_STRING,
'description' => 'Variable value.',
'default' => '',
'example' => 'myPa$$word1',
])
->addRule('functionId', [
'type' => self::TYPE_STRING,
'description' => 'Function ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Variable';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_VARIABLE;
}
}

View file

@ -0,0 +1,287 @@
<?php
namespace Tests\E2E\General;
use CURLFile;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideNone;
use Utopia\App;
use Utopia\Database\ID;
use Utopia\Database\Permission;
use Utopia\Database\Role;
class AbuseTest extends Scope
{
use ProjectCustom;
use SideNone;
protected function setUp(): void
{
parent::setUp();
if (App::getEnv('_APP_OPTIONS_ABUSE') === 'disabled') {
$this->markTestSkipped('Abuse is not enabled.');
}
}
public function testAbuseCreateDocument()
{
$data = $this->createCollection();
$databaseId = $data['databaseId'];
$collectionId = $data['collectionId'];
$max = 120;
for ($i = 0; $i <= $max + 1; $i++) {
$response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'documentId' => ID::unique(),
'data' => [
'title' => 'The Hulk ' . $i,
],
]);
if ($i < $max) {
$this->assertEquals(201, $response['headers']['status-code']);
} else {
$this->assertEquals(429, $response['headers']['status-code']);
}
}
}
public function testAbuseUpdateDocument()
{
$data = $this->createCollection();
$databaseId = $data['databaseId'];
$collectionId = $data['collectionId'];
$max = 120;
$document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'documentId' => ID::unique(),
'data' => [
'title' => 'The Hulk',
],
]);
$documentId = $document['body']['$id'];
for ($i = 0; $i <= $max + 1; $i++) {
$response = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'data' => [
'title' => 'The Hulk ' . $i,
],
]);
if ($i < $max) {
$this->assertEquals(200, $response['headers']['status-code']);
} else {
$this->assertEquals(429, $response['headers']['status-code']);
}
}
}
public function testAbuseDeleteDocument()
{
$data = $this->createCollection();
$databaseId = $data['databaseId'];
$collectionId = $data['collectionId'];
$max = 60;
for ($i = 0; $i <= $max + 1; $i++) {
$document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'documentId' => ID::unique(),
'data' => [
'title' => 'The Hulk',
],
]);
$documentId = $document['body']['$id'];
$response = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]);
if ($i < $max) {
$this->assertEquals(204, $response['headers']['status-code']);
} else {
$this->assertEquals(429, $response['headers']['status-code']);
}
}
}
public function testAbuseCreateFile()
{
$data = $this->createBucket();
$bucketId = $data['bucketId'];
$max = 60;
for ($i = 0; $i <= $max + 1; $i++) {
$response = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', [
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'fileId' => ID::unique(),
'file' => new CURLFile(realpath(__DIR__ . '/../../resources/logo.png'), 'image/png', 'permissions.png'),
]);
if ($i < $max) {
$this->assertEquals(201, $response['headers']['status-code']);
} else {
$this->assertEquals(429, $response['headers']['status-code']);
}
}
}
public function testAbuseUpdateFile()
{
$data = $this->createBucket();
$bucketId = $data['bucketId'];
$max = 60;
$response = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', [
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'fileId' => ID::unique(),
'file' => new CURLFile(realpath(__DIR__ . '/../../resources/logo.png'), 'image/png', 'permissions.png'),
]);
$fileId = $response['body']['$id'];
for ($i = 0; $i <= $max + 1; $i++) {
$response = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $bucketId . '/files/' . $fileId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], [
'name' => 'permissions' . $i . '.png',
]);
if ($i < $max) {
$this->assertEquals(200, $response['headers']['status-code']);
} else {
$this->assertEquals(429, $response['headers']['status-code']);
}
}
}
public function testAbuseDeleteFile()
{
$data = $this->createBucket();
$bucketId = $data['bucketId'];
$max = 60;
for ($i = 0; $i <= $max + 1; $i++) {
$response = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', [
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'fileId' => ID::unique(),
'file' => new CURLFile(realpath(__DIR__ . '/../../resources/logo.png'), 'image/png', 'permissions.png'),
]);
$fileId = $response['body']['$id'];
$response = $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $bucketId . '/files/' . $fileId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]);
if ($i < $max) {
$this->assertEquals(204, $response['headers']['status-code']);
} else {
$this->assertEquals(429, $response['headers']['status-code']);
}
}
}
private function createCollection(): array
{
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'databaseId' => ID::unique(),
'name' => 'AbuseDatabase',
]);
$this->assertEquals(201, $database['headers']['status-code']);
$this->assertEquals('AbuseDatabase', $database['body']['name']);
$databaseId = $database['body']['$id'];
$movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'collectionId' => ID::unique(),
'name' => 'Movies',
'permissions' => [
Permission::read(Role::any()),
Permission::create(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
]);
$collectionId = $movies['body']['$id'];
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/string', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'title',
'size' => 256,
'required' => true,
]);
sleep(1);
return [
'databaseId' => $databaseId,
'collectionId' => $collectionId,
];
}
private function createBucket(): array
{
$bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'bucketId' => ID::unique(),
'name' => 'Test Bucket',
'fileSecurity' => true,
'permissions' => [
Permission::read(Role::any()),
Permission::create(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
]);
return [
'bucketId' => $bucket['body']['$id'],
];
}
}

View file

@ -2,6 +2,7 @@
namespace Tests\E2E\General;
use Appwrite\Tests\Retry;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
@ -25,6 +26,7 @@ class UsageTest extends Scope
parent::setUp();
}
#[Retry(count: 1)]
public function testUsersStats(): array
{
$project = $this->getProject(true);
@ -92,6 +94,7 @@ class UsageTest extends Scope
}
/** @depends testUsersStats */
#[Retry(count: 1)]
public function testStorageStats(array $data): array
{
$projectId = $data['projectId'];
@ -236,6 +239,7 @@ class UsageTest extends Scope
}
/** @depends testStorageStats */
#[Retry(count: 1)]
public function testDatabaseStats(array $data): array
{
$headers = $data['headers'];
@ -436,6 +440,7 @@ class UsageTest extends Scope
/** @depends testDatabaseStats */
#[Retry(count: 1)]
public function testFunctionsStats(array $data): void
{
$headers = $data['headers'];

View file

@ -24,11 +24,6 @@ class FunctionsConsoleClientTest extends Scope
'name' => 'Test',
'execute' => [Role::user($this->getUser()['$id'])->toString()],
'runtime' => 'php-8.0',
'vars' => [
'funcKey1' => 'funcValue1',
'funcKey2' => 'funcValue2',
'funcKey3' => 'funcValue3',
],
'events' => [
'users.*.create',
'users.*.delete',
@ -95,10 +90,331 @@ class FunctionsConsoleClientTest extends Scope
]);
$this->assertEquals($response['headers']['status-code'], 200);
$this->assertEquals(count($response['body']), 4);
$this->assertEquals(count($response['body']), 9);
$this->assertEquals($response['body']['range'], '24h');
$this->assertIsArray($response['body']['functionsExecutions']);
$this->assertIsArray($response['body']['functionsFailures']);
$this->assertIsArray($response['body']['functionsCompute']);
$this->assertIsArray($response['body']['executionsTotal']);
$this->assertIsArray($response['body']['executionsFailure']);
$this->assertIsArray($response['body']['executionsSuccess']);
$this->assertIsArray($response['body']['executionsTime']);
$this->assertIsArray($response['body']['buildsTotal']);
$this->assertIsArray($response['body']['buildsFailure']);
$this->assertIsArray($response['body']['buildsSuccess']);
$this->assertIsArray($response['body']['buildsTime']);
}
/**
* @depends testCreateFunction
*/
public function testCreateFunctionVariable(array $data)
{
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'APP_TEST',
'value' => 'TESTINGVALUE'
]);
$this->assertEquals(201, $response['headers']['status-code']);
$variableId = $response['body']['$id'];
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'APP_TEST',
'value' => 'ANOTHER_TESTINGVALUE'
]);
$this->assertEquals(409, $response['headers']['status-code']);
return array_merge(
$data,
[
'variableId' => $variableId
]
);
$longKey = str_repeat("A", 256);
$response = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => $longKey,
'value' => 'TESTINGVALUE'
]);
$this->assertEquals(400, $response['headers']['status-code']);
$longValue = str_repeat("#", 8193);
$response = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'LONGKEY',
'value' => $longValue
]);
$this->assertEquals(400, $response['headers']['status-code']);
}
/**
* @depends testCreateFunctionVariable
*/
public function testListVariables(array $data)
{
/**
* Test for SUCCESS
*/
$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()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, sizeof($response['body']['variables']));
$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;
}
/**
* @depends testListVariables
*/
public function testGetVariable(array $data)
{
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals("APP_TEST", $response['body']['key']);
$this->assertEquals("TESTINGVALUE", $response['body']['value']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/variables/NON_EXISTING_VARIABLE', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(404, $response['headers']['status-code']);
return $data;
}
/**
* @depends testGetVariable
*/
public function testUpdateVariable(array $data)
{
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'APP_TEST_UPDATE',
'value' => 'TESTINGVALUEUPDATED'
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals("APP_TEST_UPDATE", $response['body']['key']);
$this->assertEquals("TESTINGVALUEUPDATED", $response['body']['value']);
$variable = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $variable['headers']['status-code']);
$this->assertEquals("APP_TEST_UPDATE", $variable['body']['key']);
$this->assertEquals("TESTINGVALUEUPDATED", $variable['body']['value']);
$response = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'APP_TEST_UPDATE_2',
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals("APP_TEST_UPDATE_2", $response['body']['key']);
$this->assertEquals("TESTINGVALUEUPDATED", $response['body']['value']);
$variable = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $variable['headers']['status-code']);
$this->assertEquals("APP_TEST_UPDATE_2", $variable['body']['key']);
$this->assertEquals("TESTINGVALUEUPDATED", $variable['body']['value']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'value' => 'TESTINGVALUEUPDATED_2'
]);
$this->assertEquals(400, $response['headers']['status-code']);
$longKey = str_repeat("A", 256);
$response = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => $longKey,
'value' => 'TESTINGVALUEUPDATED'
]);
$this->assertEquals(400, $response['headers']['status-code']);
$longValue = str_repeat("#", 8193);
$response = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'APP_TEST_UPDATE',
'value' => $longValue
]);
$this->assertEquals(400, $response['headers']['status-code']);
return $data;
}
/**
* @depends testUpdateVariable
*/
public function testDeleteVariable(array $data)
{
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(204, $response['headers']['status-code']);
$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()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(0, sizeof($response['body']['variables']));
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'] . '/variables/NON_EXISTING_VARIABLE', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(404, $response['headers']['status-code']);
return $data;
}
}

View file

@ -29,11 +29,6 @@ class FunctionsCustomClientTest extends Scope
], $this->getHeaders()), [
'functionId' => ID::unique(),
'name' => 'Test',
'vars' => [
'funcKey1' => 'funcValue1',
'funcKey2' => 'funcValue2',
'funcKey3' => 'funcValue3',
],
'events' => [
'users.*.create',
'users.*.delete',
@ -61,11 +56,6 @@ class FunctionsCustomClientTest extends Scope
'name' => 'Test',
'execute' => [Role::user($this->getUser()['$id'])->toString()],
'runtime' => 'php-8.0',
'vars' => [
'funcKey1' => 'funcValue1',
'funcKey2' => 'funcValue2',
'funcKey3' => 'funcValue3',
],
'events' => [
'users.*.create',
'users.*.delete',
@ -76,6 +66,38 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals(201, $function['headers']['status-code']);
/** Create Variables */
$variable = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/variables', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'key' => 'funcKey1',
'value' => 'funcValue1',
]);
$variable2 = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/variables', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'key' => 'funcKey2',
'value' => 'funcValue2',
]);
$variable3 = $this->client->call(Client::METHOD_POST, '/functions/' . $function['body']['$id'] . '/variables', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'key' => 'funcKey3',
'value' => 'funcValue3',
]);
$this->assertEquals(201, $variable['headers']['status-code']);
$this->assertEquals(201, $variable2['headers']['status-code']);
$this->assertEquals(201, $variable3['headers']['status-code']);
$folder = 'php';
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);
@ -151,11 +173,6 @@ class FunctionsCustomClientTest extends Scope
'name' => 'Test',
'execute' => [Role::any()->toString()],
'runtime' => 'php-8.0',
'vars' => [
'funcKey1' => 'funcValue1',
'funcKey2' => 'funcValue2',
'funcKey3' => 'funcValue3',
],
'timeout' => 10,
]);
@ -163,6 +180,38 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals(201, $function['headers']['status-code']);
/** Create Variables */
$variable = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'key' => 'funcKey1',
'value' => 'funcValue1',
]);
$variable2 = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'key' => 'funcKey2',
'value' => 'funcValue2',
]);
$variable3 = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'key' => 'funcKey3',
'value' => 'funcValue3',
]);
$this->assertEquals(201, $variable['headers']['status-code']);
$this->assertEquals(201, $variable2['headers']['status-code']);
$this->assertEquals(201, $variable3['headers']['status-code']);
$folder = 'php-fn';
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);
@ -447,11 +496,6 @@ class FunctionsCustomClientTest extends Scope
'name' => 'Test',
'execute' => [Role::any()->toString()],
'runtime' => 'php-8.0',
'vars' => [
'funcKey1' => 'funcValue1',
'funcKey2' => 'funcValue2',
'funcKey3' => 'funcValue3',
],
'timeout' => 10,
]);
@ -459,6 +503,38 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals(201, $function['headers']['status-code']);
/** Create Variables */
$variable = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'key' => 'funcKey1',
'value' => 'funcValue1',
]);
$variable2 = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'key' => 'funcKey2',
'value' => 'funcValue2',
]);
$variable3 = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'key' => 'funcKey3',
'value' => 'funcValue3',
]);
$this->assertEquals(201, $variable['headers']['status-code']);
$this->assertEquals(201, $variable2['headers']['status-code']);
$this->assertEquals(201, $variable3['headers']['status-code']);
$folder = 'php-fn';
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);

View file

@ -31,11 +31,6 @@ class FunctionsCustomServerTest extends Scope
'functionId' => ID::unique(),
'name' => 'Test',
'runtime' => 'php-8.0',
'vars' => [
'funcKey1' => 'funcValue1',
'funcKey2' => 'funcValue2',
'funcKey3' => 'funcValue3',
],
'events' => [
'users.*.create',
'users.*.delete',
@ -53,11 +48,6 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(true, DateTime::isValid($response1['body']['$createdAt']));
$this->assertEquals(true, DateTime::isValid($response1['body']['$updatedAt']));
$this->assertEquals('', $response1['body']['deployment']);
$this->assertEquals([
'funcKey1' => 'funcValue1',
'funcKey2' => 'funcValue2',
'funcKey3' => 'funcValue3',
], $response1['body']['vars']);
$this->assertEquals([
'users.*.create',
'users.*.delete',
@ -65,6 +55,35 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals('0 0 1 1 *', $response1['body']['schedule']);
$this->assertEquals(10, $response1['body']['timeout']);
/** Create Variables */
$variable = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'funcKey1',
'value' => 'funcValue1',
]);
$variable2 = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'funcKey2',
'value' => 'funcValue2',
]);
$variable3 = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'funcKey3',
'value' => 'funcValue3',
]);
$this->assertEquals(201, $variable['headers']['status-code']);
$this->assertEquals(201, $variable2['headers']['status-code']);
$this->assertEquals(201, $variable3['headers']['status-code']);
/**
* Test for FAILURE
*/
@ -169,11 +188,6 @@ class FunctionsCustomServerTest extends Scope
'functionId' => ID::unique(),
'name' => 'Test 2',
'runtime' => 'php-8.0',
'vars' => [
'funcKey1' => 'funcValue1',
'funcKey2' => 'funcValue2',
'funcKey3' => 'funcValue3',
],
'events' => [
'users.*.create',
'users.*.delete',
@ -183,6 +197,35 @@ class FunctionsCustomServerTest extends Scope
]);
$this->assertNotEmpty($response['body']['$id']);
/** Create Variables */
$variable = $this->client->call(Client::METHOD_POST, '/functions/' . $response['body']['$id'] . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'funcKey1',
'value' => 'funcValue1',
]);
$variable2 = $this->client->call(Client::METHOD_POST, '/functions/' . $response['body']['$id'] . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'funcKey2',
'value' => 'funcValue2',
]);
$variable3 = $this->client->call(Client::METHOD_POST, '/functions/' . $response['body']['$id'] . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'funcKey3',
'value' => 'funcValue3',
]);
$this->assertEquals(201, $variable['headers']['status-code']);
$this->assertEquals(201, $variable2['headers']['status-code']);
$this->assertEquals(201, $variable3['headers']['status-code']);
$functions = $this->client->call(Client::METHOD_GET, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@ -274,11 +317,6 @@ class FunctionsCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'name' => 'Test1',
'vars' => [
'key4' => 'value4',
'key5' => 'value5',
'key6' => 'value6',
],
'events' => [
'users.*.update.name',
'users.*.update.email',
@ -293,11 +331,6 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(true, DateTime::isValid($response1['body']['$createdAt']));
$this->assertEquals(true, DateTime::isValid($response1['body']['$updatedAt']));
$this->assertEquals('', $response1['body']['deployment']);
$this->assertEquals([
'key4' => 'value4',
'key5' => 'value5',
'key6' => 'value6',
], $response1['body']['vars']);
$this->assertEquals([
'users.*.update.name',
'users.*.update.email',
@ -826,7 +859,6 @@ class FunctionsCustomServerTest extends Scope
'functionId' => ID::unique(),
'name' => 'Test ' . $name,
'runtime' => $name,
'vars' => [],
'events' => [],
'schedule' => '',
'timeout' => $timeout,
@ -911,7 +943,6 @@ class FunctionsCustomServerTest extends Scope
'functionId' => ID::unique(),
'name' => 'Test ' . $name,
'runtime' => $name,
'vars' => [],
'events' => [],
'schedule' => '',
'timeout' => $timeout,
@ -1021,9 +1052,6 @@ class FunctionsCustomServerTest extends Scope
'functionId' => ID::unique(),
'name' => 'Test ' . $name,
'runtime' => $name,
'vars' => [
'CUSTOM_VARIABLE' => 'variable',
],
'events' => [],
'schedule' => '',
'timeout' => $timeout,
@ -1033,6 +1061,17 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(201, $function['headers']['status-code']);
// Create variable
$variable = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'CUSTOM_VARIABLE',
'value' => 'variable',
]);
$this->assertEquals(201, $variable['headers']['status-code']);
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
@ -1081,7 +1120,6 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT']);
$this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT_DATA']);
$this->assertEquals('foobar', $output['APPWRITE_FUNCTION_DATA']);
$this->assertEquals('variable', $output['CUSTOM_VARIABLE']);
$this->assertEquals('', $output['APPWRITE_FUNCTION_USER_ID']);
$this->assertEmpty($output['APPWRITE_FUNCTION_JWT']);
$this->assertEquals($this->getProject()['$id'], $output['APPWRITE_FUNCTION_PROJECT_ID']);
@ -1126,9 +1164,6 @@ class FunctionsCustomServerTest extends Scope
'functionId' => ID::unique(),
'name' => 'Test ' . $name,
'runtime' => $name,
'vars' => [
'CUSTOM_VARIABLE' => 'variable',
],
'events' => [],
'schedule' => '',
'timeout' => $timeout,
@ -1138,6 +1173,17 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(201, $function['headers']['status-code']);
/** Create Variables */
$variable = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'CUSTOM_VARIABLE',
'value' => 'variable',
]);
$this->assertEquals(201, $variable['headers']['status-code']);
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
@ -1231,9 +1277,6 @@ class FunctionsCustomServerTest extends Scope
'functionId' => ID::unique(),
'name' => 'Test ' . $name,
'runtime' => $name,
'vars' => [
'CUSTOM_VARIABLE' => 'variable',
],
'events' => [],
'schedule' => '',
'timeout' => $timeout,
@ -1243,6 +1286,17 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(201, $function['headers']['status-code']);
/** Create Variables */
$variable = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'CUSTOM_VARIABLE',
'value' => 'variable',
]);
$this->assertEquals(201, $variable['headers']['status-code']);
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
@ -1291,7 +1345,6 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT']);
$this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT_DATA']);
$this->assertEquals('foobar', $output['APPWRITE_FUNCTION_DATA']);
$this->assertEquals('variable', $output['CUSTOM_VARIABLE']);
$this->assertEquals('', $output['APPWRITE_FUNCTION_USER_ID']);
$this->assertEmpty($output['APPWRITE_FUNCTION_JWT']);
$this->assertEquals($this->getProject()['$id'], $output['APPWRITE_FUNCTION_PROJECT_ID']);
@ -1337,9 +1390,6 @@ class FunctionsCustomServerTest extends Scope
'functionId' => ID::unique(),
'name' => 'Test ' . $name,
'runtime' => $name,
'vars' => [
'CUSTOM_VARIABLE' => 'variable',
],
'events' => [],
'schedule' => '',
'timeout' => $timeout,
@ -1349,6 +1399,17 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(201, $function['headers']['status-code']);
/** Create Variables */
$variable = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'CUSTOM_VARIABLE',
'value' => 'variable',
]);
$this->assertEquals(201, $variable['headers']['status-code']);
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
@ -1397,7 +1458,6 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT']);
$this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT_DATA']);
$this->assertEquals('foobar', $output['APPWRITE_FUNCTION_DATA']);
$this->assertEquals('variable', $output['CUSTOM_VARIABLE']);
$this->assertEquals('', $output['APPWRITE_FUNCTION_USER_ID']);
$this->assertEmpty($output['APPWRITE_FUNCTION_JWT']);
$this->assertEquals($this->getProject()['$id'], $output['APPWRITE_FUNCTION_PROJECT_ID']);

View file

@ -455,7 +455,17 @@ class WebhooksCustomServerTest extends Scope
$this->assertEquals($function['headers']['status-code'], 200);
$this->assertEquals($function['body']['$id'], $data['functionId']);
$this->assertEquals($function['body']['vars'], ['key1' => 'value1']);
// Create variable
$variable = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'key1',
'value' => 'value1',
]);
$this->assertEquals(201, $variable['headers']['status-code']);
$webhook = $this->getLastRequest();
$signatureExpected = self::getWebhookSignature($webhook, $this->getProject()['signatureKey']);