Shared variables

This commit is contained in:
Matej Bačo
2023-03-11 17:06:02 +01:00
parent 541cb010a6
commit 1f409b4e40
20 changed files with 291 additions and 34 deletions
+32 -7
View File
@@ -3715,7 +3715,18 @@ $collections = [
'name' => 'variables',
'attributes' => [
[
'$id' => 'functionInternalId',
'$id' => ID::custom('resourceType'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 100,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('resourceInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
@@ -3726,7 +3737,7 @@ $collections = [
'filters' => [],
],
[
'$id' => 'functionId',
'$id' => ID::custom('resourceId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
@@ -3772,18 +3783,32 @@ $collections = [
],
'indexes' => [
[
'$id' => '_key_function',
'$id' => '_key_resourceInternalId',
'type' => Database::INDEX_KEY,
'attributes' => ['functionInternalId'],
'attributes' => ['resourceInternalId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_resourceId',
'type' => Database::INDEX_KEY,
'attributes' => ['resourceId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_resourceType',
'type' => Database::INDEX_KEY,
'attributes' => ['resourceType'],
'lengths' => [100],
'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],
'attributes' => ['resourceId', 'key', 'resourceType'],
'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY, 100],
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC, Database::ORDER_ASC],
],
[
'$id' => '_key_key',
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
+28 -9
View File
@@ -47,6 +47,8 @@ use Utopia\Database\Exception\Duplicate as DuplicateException;
include_once __DIR__ . '/../shared/api.php';
// TODO: @Meldiron Changes to variables must delete runtime on executor
App::post('/v1/functions')
->groups(['api', 'functions'])
->desc('Create Function')
@@ -1270,11 +1272,24 @@ App::post('/v1/functions/:functionId/executions')
->dynamic($execution, Response::MODEL_EXECUTION);
}
$vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) {
$vars = [];
// Shared vars
$vars = \array_merge($vars, \array_reduce($dbForProject->find('variables', [
Query::equal('resourceType', ['project']),
Query::limit(APP_LIMIT_SUBQUERY)
]), function (array $carry, Document $var) {
$carry[$var->getAttribute('key')] = $var->getAttribute('value') ?? '';
return $carry;
}, []);
}, []));
// Function vars
$vars = \array_merge($vars, array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) {
$carry[$var->getAttribute('key')] = $var->getAttribute('value') ?? '';
return $carry;
}, []));
// Appwrite vars
$vars = \array_merge($vars, [
'APPWRITE_FUNCTION_ID' => $function->getId(),
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'),
@@ -1507,11 +1522,12 @@ App::post('/v1/functions/:functionId/variables')
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'functionInternalId' => $function->getInternalId(),
'functionId' => $function->getId(),
'resourceInternalId' => $function->getInternalId(),
'resourceId' => $function->getId(),
'resourceType' => 'function',
'key' => $key,
'value' => $value,
'search' => implode(' ', [$variableId, $function->getId(), $key]),
'search' => implode(' ', [$variableId, $function->getId(), $key, 'function']),
]);
try {
@@ -1578,7 +1594,8 @@ App::get('/v1/functions/:functionId/variables/:variableId')
$variable = $dbForProject->findOne('variables', [
Query::equal('$id', [$variableId]),
Query::equal('functionInternalId', [$function->getInternalId()]),
Query::equal('resourceInternalId', [$function->getInternalId()]),
Query::equal('resourceType', ['function']),
]);
if ($variable === false || $variable->isEmpty()) {
@@ -1617,7 +1634,8 @@ App::put('/v1/functions/:functionId/variables/:variableId')
$variable = $dbForProject->findOne('variables', [
Query::equal('$id', [$variableId]),
Query::equal('functionInternalId', [$function->getInternalId()]),
Query::equal('resourceInternalId', [$function->getInternalId()]),
Query::equal('resourceType', ['function']),
]);
if ($variable === false || $variable->isEmpty()) {
@@ -1627,7 +1645,7 @@ App::put('/v1/functions/:functionId/variables/:variableId')
$variable
->setAttribute('key', $key)
->setAttribute('value', $value ?? $variable->getAttribute('value'))
->setAttribute('search', implode(' ', [$variableId, $function->getId(), $key]))
->setAttribute('search', implode(' ', [$variableId, $function->getId(), $key, 'function']))
;
try {
@@ -1666,7 +1684,8 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
$variable = $dbForProject->findOne('variables', [
Query::equal('$id', [$variableId]),
Query::equal('functionInternalId', [$function->getInternalId()]),
Query::equal('resourceInternalId', [$function->getInternalId()]),
Query::equal('resourceType', ['function']),
]);
if ($variable === false || $variable->isEmpty()) {
+186
View File
@@ -1,14 +1,23 @@
<?php
use Appwrite\Extend\Exception;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\ID;
use Utopia\Database\Permission;
use Utopia\Database\Query;
use Utopia\Database\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\UID;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
// TODO: @Meldiron Changes to variables must delete runtime on executor
App::get('/v1/project/usage')
->desc('Get usage stats for a project')
->groups(['api'])
@@ -110,3 +119,180 @@ App::get('/v1/project/usage')
$response->dynamic($usage, Response::MODEL_USAGE_PROJECT);
});
// Variables
App::post('/v1/project/variables')
->desc('Create Variable')
->groups(['api'])
->label('scope', 'projects.write')
->label('audits.event', 'variable.create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'project')
->label('sdk.method', 'createVariable')
->label('sdk.description', '/docs/references/project/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('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('project')
->inject('response')
->inject('dbForProject')
->action(function (string $key, string $value, Document $project, Response $response, Database $dbForProject) {
$variableId = ID::unique();
$variable = new Document([
'$id' => $variableId,
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'resourceInternalId' => '',
'resourceId' => '',
'resourceType' => 'project',
'key' => $key,
'value' => $value,
'search' => implode(' ', [$variableId, $key, 'project']),
]);
try {
$variable = $dbForProject->createDocument('variables', $variable);
} catch (DuplicateException $th) {
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
}
$dbForProject->deleteCachedDocument('projects', $project->getId());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($variable, Response::MODEL_VARIABLE);
});
App::get('/v1/project/variables')
->desc('List Variables')
->groups(['api'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'project')
->label('sdk.method', 'listVariables')
->label('sdk.description', '/docs/references/project/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)
->inject('response')
->inject('dbForProject')
->action(function (Response $response, Database $dbForProject) {
$variables = $dbForProject->find('variables', [
Query::equal('resourceType', ['project']),
Query::limit(APP_LIMIT_SUBQUERY)
]);
$response->dynamic(new Document([
'variables' => $variables,
'total' => \count($variables),
]), Response::MODEL_VARIABLE_LIST);
});
App::get('/v1/project/variables/:variableId')
->desc('Get Variable')
->groups(['api'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'project')
->label('sdk.method', 'getVariable')
->label('sdk.description', '/docs/references/project/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('variableId', '', new UID(), 'Variable unique ID.', false)
->inject('response')
->inject('dbForProject')
->action(function (string $variableId, Response $response, Database $dbForProject) {
$variable = $dbForProject->findOne('variables', [
Query::equal('$id', [$variableId]),
Query::equal('resourceType', ['project']),
]);
if ($variable === false || $variable->isEmpty()) {
throw new Exception(Exception::VARIABLE_NOT_FOUND);
}
$response->dynamic($variable, Response::MODEL_VARIABLE);
});
App::put('/v1/project/variables/:variableId')
->desc('Update Variable')
->groups(['api'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'project')
->label('sdk.method', 'updateVariable')
->label('sdk.description', '/docs/references/project/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('variableId', '', 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('project')
->inject('response')
->inject('dbForProject')
->action(function (string $variableId, string $key, ?string $value, Document $project, Response $response, Database $dbForProject) {
$variable = $dbForProject->findOne('variables', [
Query::equal('$id', [$variableId]),
Query::equal('resourceType', ['project']),
]);
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, $key, 'project']))
;
try {
$dbForProject->updateDocument('variables', $variable->getId(), $variable);
} catch (DuplicateException $th) {
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
}
$dbForProject->deleteCachedDocument('projects', $project->getId());
$response->dynamic($variable, Response::MODEL_VARIABLE);
});
App::delete('/v1/project/variables/:variableId')
->desc('Delete Variable')
->groups(['api'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'project')
->label('sdk.method', 'deleteVariable')
->label('sdk.description', '/docs/references/project/delete-variable.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('variableId', '', new UID(), 'Variable unique ID.', false)
->inject('project')
->inject('response')
->inject('dbForProject')
->action(function (string $variableId, Document $project, Response $response, Database $dbForProject) {
$variable = $dbForProject->findOne('variables', [
Query::equal('$id', [$variableId]),
Query::equal('resourceType', ['project']),
]);
if ($variable === false || $variable->isEmpty()) {
throw new Exception(Exception::VARIABLE_NOT_FOUND);
}
$dbForProject->deleteDocument('variables', $variable->getId());
$dbForProject->deleteCachedDocument('projects', $project->getId());
$response->noContent();
});
+2 -1
View File
@@ -399,7 +399,8 @@ Database::addFilter(
function (mixed $value, Document $document, Database $database) {
return $database
->find('variables', [
Query::equal('functionInternalId', [$document->getInternalId()]),
Query::equal('resourceInternalId', [$document->getInternalId()]),
Query::equal('resourceType', ['function']),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
+4 -3
View File
@@ -467,7 +467,8 @@ class DeletesV1 extends Worker
*/
Console::info("Deleting variables for function " . $functionId);
$this->deleteByGroup('variables', [
Query::equal('functionId', [$functionId])
Query::equal('resourceType', ['function']),
Query::equal('resourceInternalId', [$document->getInternalId()])
], $dbForProject);
/**
@@ -512,7 +513,7 @@ class DeletesV1 extends Worker
Query::equal('functionId', [$functionId])
], $dbForProject);
// TODO: Request executor to delete runtime
// TODO: @Meldiron Request executor to delete runtime
}
/**
@@ -552,7 +553,7 @@ class DeletesV1 extends Worker
}
});
// TODO: Request executor to delete runtime
// TODO: @Meldiron Request executor to delete runtime
}
+15 -3
View File
@@ -124,12 +124,24 @@ Server::setResource('execute', function () {
}
}
$vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) {
$vars = [];
// Shared vars
$vars = \array_merge($vars, \array_reduce($dbForProject->find('variables', [
Query::equal('resourceType', ['project']),
Query::limit(APP_LIMIT_SUBQUERY)
]), function (array $carry, Document $var) {
$carry[$var->getAttribute('key')] = $var->getAttribute('value') ?? '';
return $carry;
}, []));
// Function vars
$vars = \array_merge($vars, array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) {
$carry[$var->getAttribute('key')] = $var->getAttribute('value');
return $carry;
}, []);
}, []));
/** Collect environment variables */
// Appwrite vars
$vars = \array_merge($vars, [
'APPWRITE_FUNCTION_ID' => $functionId,
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'),
@@ -0,0 +1 @@
Create a new project variable. These variables can be accessed by every function.
@@ -0,0 +1 @@
Delete a project variable by its unique ID.
+1
View File
@@ -0,0 +1 @@
Get a project variable by its unique ID.
@@ -0,0 +1 @@
Get a list of all project variables.
@@ -0,0 +1 @@
Update project variable by its unique ID.
@@ -7,7 +7,9 @@ use Appwrite\Utopia\Database\Validator\Queries\Base;
class Variables extends Base
{
public const ALLOWED_ATTRIBUTES = [
'key'
'key',
'resourceType',
'resourceId'
];
/**
+1 -1
View File
@@ -44,7 +44,7 @@ class Rule extends Model
])
->addRule('resourceId', [
'type' => self::TYPE_STRING,
'description' => 'ID of resource for the action type. If resourceType is "api" or "url", leave empty. If resourceType is "function", provide ID of the function.',
'description' => 'ID of resource for the action type. If resourceType is "api" or "url", it is empty. If resourceType is "function", it is ID of the function.',
'default' => '',
'example' => 'myAwesomeFunction',
])
@@ -41,11 +41,17 @@ class Variable extends Model
'default' => '',
'example' => 'myPa$$word1',
])
->addRule('functionId', [
->addRule('resourceType', [
'type' => self::TYPE_STRING,
'description' => 'Function ID.',
'description' => 'Service to which the variable belongs. Possible values are "project", "function"',
'default' => '',
'example' => '5e5ea5c16897e',
'example' => 'function',
])
->addRule('resourceId', [
'type' => self::TYPE_STRING,
'description' => 'ID of resource to which the variable belongs. If resourceType is "project", it is empty. If resourceType is "function", it is ID of the function.',
'default' => '',
'example' => 'myAwesomeFunction',
])
;
}