1
0
Fork 0
mirror of synced 2024-06-01 18:39:57 +12:00
appwrite/app/controllers/api/functions.php

1051 lines
47 KiB
PHP
Raw Normal View History

2020-05-05 02:35:01 +12:00
<?php
2021-03-11 06:48:05 +13:00
use Ahc\Jwt\JWT;
use Appwrite\Auth\Auth;
2021-07-19 20:09:37 +12:00
use Appwrite\Database\Validator\CustomId;
2022-01-31 22:46:24 +13:00
use Appwrite\Event\Event;
2021-07-26 02:47:18 +12:00
use Utopia\Database\Validator\UID;
2021-01-22 21:28:33 +13:00
use Utopia\Storage\Storage;
use Utopia\Storage\Validator\File;
2021-01-28 02:15:44 +13:00
use Utopia\Storage\Validator\FileExt;
2021-01-22 21:28:33 +13:00
use Utopia\Storage\Validator\FileSize;
use Utopia\Storage\Validator\Upload;
2020-10-31 08:53:27 +13:00
use Appwrite\Utopia\Response;
2020-05-06 05:30:12 +12:00
use Appwrite\Task\Validator\Cron;
2020-07-13 09:18:52 +12:00
use Utopia\App;
2021-05-05 09:25:17 +12:00
use Utopia\Exception;
2021-06-13 02:33:23 +12:00
use Utopia\Database\Database;
2021-05-05 09:25:17 +12:00
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
2020-05-06 05:30:12 +12:00
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
2020-05-05 02:35:01 +12:00
use Utopia\Validator\Text;
use Utopia\Validator\Range;
2020-05-06 05:30:12 +12:00
use Utopia\Validator\WhiteList;
2020-07-15 04:13:18 +12:00
use Utopia\Config\Config;
2020-05-13 10:00:48 +12:00
use Cron\CronExpression;
use Utopia\CLI\Console;
2021-12-07 04:04:00 +13:00
use Utopia\Validator\Boolean;
2020-05-05 02:35:01 +12:00
include_once __DIR__ . '/../shared/api.php';
2020-07-13 09:18:52 +12:00
App::post('/v1/functions')
->groups(['api', 'functions'])
2020-05-05 02:35:01 +12:00
->desc('Create Function')
->label('scope', 'functions.write')
->label('event', 'functions.create')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
2020-05-05 02:35:01 +12:00
->label('sdk.namespace', 'functions')
->label('sdk.method', 'create')
2020-05-06 05:30:12 +12:00
->label('sdk.description', '/docs/references/functions/create-function.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION)
->param('functionId', '', new CustomId(), 'Function ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
2020-09-11 02:40:14 +12:00
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('runtime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution runtime.')
2022-01-22 07:02:02 +13:00
->param('vars', [], new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
2020-09-11 02:40:14 +12:00
->param('events', [], new ArrayList(new WhiteList(array_keys(Config::getParam('events')), true)), 'Events list.', true)
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
->param('timeout', 15, new Range(1, 900), 'Function maximum execution time in seconds.', true)
2020-12-27 04:20:08 +13:00
->inject('response')
->inject('dbForProject')
->action(function ($functionId, $name, $execute, $runtime, $vars, $events, $schedule, $timeout, $response, $dbForProject) {
2021-01-17 12:38:13 +13:00
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
2021-01-17 12:38:13 +13:00
$functionId = ($functionId == 'unique()') ? $dbForProject->getId() : $functionId;
$function = $dbForProject->createDocument('functions', new Document([
'$id' => $functionId,
2021-05-05 09:25:17 +12:00
'execute' => $execute,
2020-07-13 09:18:52 +12:00
'dateCreated' => time(),
'dateUpdated' => time(),
2020-10-31 08:53:27 +13:00
'status' => 'disabled',
2020-07-13 09:18:52 +12:00
'name' => $name,
'runtime' => $runtime,
'deployment' => '',
2020-07-13 09:18:52 +12:00
'vars' => $vars,
'events' => $events,
'schedule' => $schedule,
2021-05-05 09:25:17 +12:00
'schedulePrevious' => 0,
'scheduleNext' => 0,
2020-07-13 09:18:52 +12:00
'timeout' => $timeout,
'search' => implode(' ', [$functionId, $name, $runtime]),
2021-05-05 09:25:17 +12:00
]));
2020-05-06 05:30:12 +12:00
2021-05-27 22:09:14 +12:00
$response->setStatusCode(Response::STATUS_CODE_CREATED);
2021-07-26 02:47:18 +12:00
$response->dynamic($function, Response::MODEL_FUNCTION);
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
App::get('/v1/functions')
->groups(['api', 'functions'])
2020-05-06 05:30:12 +12:00
->desc('List Functions')
->label('scope', 'functions.read')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
2020-05-06 05:30:12 +12:00
->label('sdk.namespace', 'functions')
->label('sdk.method', 'list')
->label('sdk.description', '/docs/references/functions/list-functions.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION_LIST)
2020-09-11 02:40:14 +12:00
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
2021-12-15 00:21:44 +13:00
->param('limit', 25, new Range(0, 100), 'Maximum number of functions to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this value to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursor', '', new UID(), 'ID of the function used as the starting point for the query, excluding the function itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
2020-09-11 02:40:14 +12:00
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
2020-12-27 04:20:08 +13:00
->inject('response')
->inject('dbForProject')
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject) {
2021-01-17 12:38:13 +13:00
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
2020-07-13 09:18:52 +12:00
if (!empty($cursor)) {
$cursorFunction = $dbForProject->getDocument('functions', $cursor);
2021-08-07 00:36:05 +12:00
if ($cursorFunction->isEmpty()) {
throw new Exception("Function '{$cursor}' for the 'cursor' value not found.", 400);
2021-08-07 00:36:05 +12:00
}
}
2021-08-19 01:42:03 +12:00
$queries = [];
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
}
2020-07-13 09:18:52 +12:00
2020-10-31 08:53:27 +13:00
$response->dynamic(new Document([
'functions' => $dbForProject->find('functions', $queries, $limit, $offset, [], [$orderType], $cursorFunction ?? null, $cursorDirection),
'sum' => $dbForProject->count('functions', $queries, APP_LIMIT_COUNT),
2020-10-31 08:53:27 +13:00
]), Response::MODEL_FUNCTION_LIST);
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
2021-12-10 02:02:12 +13:00
App::get('/v1/functions/runtimes')
->groups(['api', 'functions'])
->desc('List the currently active function runtimes.')
->label('scope', 'functions.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'listRuntimes')
->label('sdk.description', '/docs/references/functions/list-runtimes.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_RUNTIME_LIST)
->inject('response')
->action(function ($response) {
/** @var Appwrite\Utopia\Response $response */
$runtimes = Config::getParam('runtimes');
$runtimes = array_map(function ($key) use ($runtimes) {
$runtimes[$key]['$id'] = $key;
return $runtimes[$key];
}, array_keys($runtimes));
$response->dynamic(new Document([
'sum' => count($runtimes),
'runtimes' => $runtimes
]), Response::MODEL_RUNTIME_LIST);
});
2020-07-13 09:18:52 +12:00
App::get('/v1/functions/:functionId')
->groups(['api', 'functions'])
2020-05-06 05:30:12 +12:00
->desc('Get Function')
->label('scope', 'functions.read')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
2020-05-06 05:30:12 +12:00
->label('sdk.namespace', 'functions')
->label('sdk.method', 'get')
->label('sdk.description', '/docs/references/functions/get-function.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION)
->param('functionId', '', new UID(), 'Function ID.')
2020-12-27 04:20:08 +13:00
->inject('response')
->inject('dbForProject')
->action(function ($functionId, $response, $dbForProject) {
2021-01-17 12:38:13 +13:00
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
2021-01-17 12:38:13 +13:00
$function = $dbForProject->getDocument('functions', $functionId);
2020-05-06 05:30:12 +12:00
2021-06-21 01:59:36 +12:00
if ($function->isEmpty()) {
2020-10-31 08:53:27 +13:00
throw new Exception('Function not found', 404);
2020-05-06 05:30:12 +12:00
}
2020-10-31 08:53:27 +13:00
$response->dynamic($function, Response::MODEL_FUNCTION);
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
2020-10-31 08:53:27 +13:00
App::get('/v1/functions/:functionId/usage')
->desc('Get Function Usage')
->groups(['api', 'functions'])
->label('scope', 'functions.read')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
2020-10-31 08:53:27 +13:00
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getUsage')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USAGE_FUNCTIONS)
->param('functionId', '', new UID(), 'Function ID.')
2020-10-31 08:53:27 +13:00
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d']), 'Date range.', true)
2020-12-27 04:20:08 +13:00
->inject('response')
->inject('dbForProject')
->action(function ($functionId, $range, $response, $dbForProject) {
2020-10-31 08:53:27 +13:00
/** @var Appwrite\Utopia\Response $response */
2021-07-26 02:47:18 +12:00
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForProject */
2020-10-31 08:53:27 +13:00
/** @var Utopia\Registry\Registry $register */
$function = $dbForProject->getDocument('functions', $functionId);
2020-10-31 08:53:27 +13:00
2021-06-21 01:59:36 +12:00
if ($function->isEmpty()) {
2020-10-31 08:53:27 +13:00
throw new Exception('Function not found', 404);
}
2021-01-13 19:25:43 +13:00
$usage = [];
2021-01-15 18:30:49 +13:00
if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
2021-10-28 11:17:15 +13:00
$periods = [
'24h' => [
'period' => '30m',
'limit' => 48,
],
'7d' => [
'period' => '1d',
'limit' => 7,
],
'30d' => [
'period' => '1d',
'limit' => 30,
],
'90d' => [
'period' => '1d',
'limit' => 90,
],
];
$metrics = [
"functions.$functionId.executions",
"functions.$functionId.failures",
"functions.$functionId.compute"
];
$stats = [];
2021-08-20 22:04:57 +12:00
Authorization::skip(function() use ($dbForProject, $periods, $range, $metrics, &$stats) {
foreach ($metrics as $metric) {
2021-10-28 11:17:15 +13:00
$limit = $periods[$range]['limit'];
$period = $periods[$range]['period'];
$requestDocs = $dbForProject->find('stats', [
new Query('period', Query::TYPE_EQUAL, [$period]),
new Query('metric', Query::TYPE_EQUAL, [$metric]),
], $limit, 0, ['time'], [Database::ORDER_DESC]);
$stats[$metric] = [];
foreach ($requestDocs as $requestDoc) {
$stats[$metric][] = [
'value' => $requestDoc->getAttribute('value'),
'date' => $requestDoc->getAttribute('time'),
];
}
// backfill metrics with empty values for graphs
$backfill = $limit - \count($requestDocs);
while ($backfill > 0) {
$last = $limit - $backfill - 1; // array index of last added metric
$diff = match($period) { // convert period to seconds for unix timestamp math
'30m' => 1800,
'1d' => 86400,
};
$stats[$metric][] = [
'value' => 0,
2021-10-28 11:17:51 +13:00
'date' => ($stats[$metric][$last]['date'] ?? \time()) - $diff, // time of last metric minus period
];
$backfill--;
}
$stats[$metric] = array_reverse($stats[$metric]);
}
});
$usage = new Document([
'range' => $range,
2021-10-27 02:19:28 +13:00
'functionsExecutions' => $stats["functions.$functionId.executions"],
'functionsFailures' => $stats["functions.$functionId.failures"],
'functionsCompute' => $stats["functions.$functionId.compute"]
]);
2020-10-31 08:53:27 +13:00
}
$response->dynamic($usage, Response::MODEL_USAGE_FUNCTIONS);
2020-12-27 04:20:08 +13:00
});
2020-10-31 08:53:27 +13:00
2020-07-13 09:18:52 +12:00
App::put('/v1/functions/:functionId')
->groups(['api', 'functions'])
2020-05-06 05:30:12 +12:00
->desc('Update Function')
->label('scope', 'functions.write')
->label('event', 'functions.update')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
2020-05-06 05:30:12 +12:00
->label('sdk.namespace', 'functions')
->label('sdk.method', 'update')
->label('sdk.description', '/docs/references/functions/update-function.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION)
->param('functionId', '', new UID(), 'Function ID.')
2020-09-11 02:40:14 +12:00
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('vars', [], new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
2020-09-11 02:40:14 +12:00
->param('events', [], new ArrayList(new WhiteList(array_keys(Config::getParam('events')), true)), 'Events list.', true)
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
->param('timeout', 15, new Range(1, 900), 'Maximum execution time in seconds.', true)
2020-12-27 04:20:08 +13:00
->inject('response')
->inject('dbForProject')
2021-01-17 12:38:13 +13:00
->inject('project')
2021-12-07 23:42:33 +13:00
->inject('user')
->action(function ($functionId, $name, $execute, $vars, $events, $schedule, $timeout, $response, $dbForProject, $project, $user) {
2021-01-17 12:38:13 +13:00
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
2021-07-26 02:47:18 +12:00
/** @var Utopia\Database\Document $project */
2022-01-11 03:13:23 +13:00
/** @var Appwrite\Auth\User $user */
2021-01-17 12:38:13 +13:00
$function = $dbForProject->getDocument('functions', $functionId);
2020-07-13 09:18:52 +12:00
2021-06-21 01:59:36 +12:00
if ($function->isEmpty()) {
2020-07-13 09:18:52 +12:00
throw new Exception('Function not found', 404);
}
2021-01-17 13:07:43 +13:00
$original = $function->getAttribute('schedule', '');
$cron = (!empty($function->getAttribute('deployment', null)) && !empty($schedule)) ? new CronExpression($schedule) : null;
$next = (!empty($function->getAttribute('deployment', null)) && !empty($schedule)) ? $cron->getNextRunDate()->format('U') : 0;
2020-07-13 09:18:52 +12:00
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
2021-05-05 09:25:17 +12:00
'execute' => $execute,
2020-07-13 09:18:52 +12:00
'dateUpdated' => time(),
'name' => $name,
'vars' => $vars,
'events' => $events,
'schedule' => $schedule,
2021-05-05 09:25:17 +12:00
'scheduleNext' => (int)$next,
2021-01-17 12:38:13 +13:00
'timeout' => $timeout,
'search' => implode(' ', [$functionId, $name, $function->getAttribute('runtime')]),
2021-05-05 09:25:17 +12:00
])));
2020-05-06 05:30:12 +12:00
2021-01-17 13:07:43 +13:00
if ($next && $schedule !== $original) {
2021-01-17 12:38:13 +13:00
ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [
'projectId' => $project->getId(),
2021-05-17 23:32:37 +12:00
'webhooks' => $project->getAttribute('webhooks', []),
2021-01-17 12:38:13 +13:00
'functionId' => $function->getId(),
2021-12-07 23:42:33 +13:00
'userId' => $user->getId(),
2021-01-17 12:38:13 +13:00
'executionId' => null,
'trigger' => 'schedule',
]); // Async task rescheduale
}
2020-10-31 08:53:27 +13:00
$response->dynamic($function, Response::MODEL_FUNCTION);
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
App::patch('/v1/functions/:functionId/deployment')
2020-07-13 09:18:52 +12:00
->groups(['api', 'functions'])
->desc('Update Function Deployment')
2020-05-07 05:35:56 +12:00
->label('scope', 'functions.write')
->label('event', 'functions.deployments.update')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
2020-05-07 05:35:56 +12:00
->label('sdk.namespace', 'functions')
->label('sdk.method', 'updateDeployment')
->label('sdk.description', '/docs/references/functions/update-function-deployment.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION)
->param('functionId', '', new UID(), 'Function ID.')
->param('deployment', '', new UID(), 'Deployment ID.')
2020-12-27 04:20:08 +13:00
->inject('response')
->inject('dbForProject')
2021-01-17 12:38:13 +13:00
->inject('project')
->action(function ($functionId, $deployment, $response, $dbForProject, $project) {
2021-01-17 12:38:13 +13:00
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
2021-07-26 02:47:18 +12:00
/** @var Utopia\Database\Document $project */
2021-01-17 12:38:13 +13:00
2022-01-06 22:45:56 +13:00
$function = $dbForProject->getDocument('functions', $functionId);
$deployment = $dbForProject->getDocument('deployments', $deployment);
2022-02-01 12:44:55 +13:00
var_dump($deployment);
2022-01-27 12:19:02 +13:00
$build = $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''));
if ($function->isEmpty()) {
throw new Exception('Function not found', 404);
}
2021-01-17 12:38:13 +13:00
if ($deployment->isEmpty()) {
throw new Exception('Deployment not found', 404);
}
if ($build->isEmpty()) {
throw new Exception('Build not found', 404);
2020-05-07 05:35:56 +12:00
}
if ($build->getAttribute('status') !== 'ready') {
throw new Exception('Build not ready', 400);
}
$schedule = $function->getAttribute('schedule', '');
$cron = (empty($function->getAttribute('deployment')) && !empty($schedule)) ? new CronExpression($schedule) : null;
$next = (empty($function->getAttribute('deployment')) && !empty($schedule)) ? $cron->getNextRunDate()->format('U') : 0;
2021-08-24 21:32:27 +12:00
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
'deployment' => $deployment->getId(),
'scheduleNext' => (int)$next,
])));
if ($next) { // Init first schedule
ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [
'projectId' => $project->getId(),
'webhooks' => $project->getAttribute('webhooks', []),
'functionId' => $function->getId(),
'executionId' => null,
'trigger' => 'schedule',
]); // Async task rescheduale
}
$response->dynamic($function, Response::MODEL_FUNCTION);
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
App::delete('/v1/functions/:functionId')
->groups(['api', 'functions'])
2020-05-06 05:30:12 +12:00
->desc('Delete Function')
->label('scope', 'functions.write')
->label('event', 'functions.delete')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
2020-05-06 05:30:12 +12:00
->label('sdk.namespace', 'functions')
->label('sdk.method', 'delete')
->label('sdk.description', '/docs/references/functions/delete-function.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('functionId', '', new UID(), 'Function ID.')
2020-12-27 04:20:08 +13:00
->inject('response')
->inject('dbForProject')
2020-12-27 04:20:08 +13:00
->inject('deletes')
2022-01-28 14:30:24 +13:00
->action(function ($functionId, $response, $dbForProject, $deletes) {
2020-10-31 08:53:27 +13:00
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
2020-10-31 08:53:27 +13:00
/** @var Appwrite\Event\Event $deletes */
$function = $dbForProject->getDocument('functions', $functionId);
2020-05-06 05:30:12 +12:00
2021-06-21 01:59:36 +12:00
if ($function->isEmpty()) {
2020-07-13 09:18:52 +12:00
throw new Exception('Function not found', 404);
}
2020-05-07 00:12:52 +12:00
if (!$dbForProject->deleteDocument('functions', $function->getId())) {
2020-07-13 09:18:52 +12:00
throw new Exception('Failed to remove function from DB', 500);
2020-05-05 02:35:01 +12:00
}
2020-05-06 05:30:12 +12:00
2020-10-31 08:53:27 +13:00
$deletes
2020-12-19 03:05:15 +13:00
->setParam('type', DELETE_TYPE_DOCUMENT)
2021-05-16 08:03:56 +12:00
->setParam('document', $function)
2020-10-31 08:53:27 +13:00
;
2020-07-13 09:18:52 +12:00
$response->noContent();
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
2022-01-25 12:05:17 +13:00
App::post('/v1/functions/:functionId/deployments')
2020-07-13 09:18:52 +12:00
->groups(['api', 'functions'])
2022-01-25 12:05:17 +13:00
->desc('Create Deployment')
2020-05-06 05:30:12 +12:00
->label('scope', 'functions.write')
2022-01-25 12:05:17 +13:00
->label('event', 'functions.deployments.create')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
2020-05-06 05:30:12 +12:00
->label('sdk.namespace', 'functions')
2022-01-25 12:05:17 +13:00
->label('sdk.method', 'createDeployment')
->label('sdk.description', '/docs/references/functions/create-deployment.md')
2021-01-30 03:29:53 +13:00
->label('sdk.packaging', true)
2020-11-12 10:02:24 +13:00
->label('sdk.request.type', 'multipart/form-data')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
2022-01-25 12:05:17 +13:00
->label('sdk.response.model', Response::MODEL_DEPLOYMENT)
->param('functionId', '', new UID(), 'Function ID.')
2021-09-01 21:48:56 +12:00
->param('entrypoint', '', new Text('1028'), 'Entrypoint File.')
2021-03-23 05:19:05 +13:00
->param('code', [], new File(), 'Gzip file with your code package. When used with the Appwrite CLI, pass the path to your code directory, and the CLI will automatically package your code. Use a path that is within the current directory.', false)
2022-01-24 22:37:14 +13:00
->param('deploy', false, new Boolean(true), 'Automatically deploy the function when it is finished building.', false)
2020-12-27 04:20:08 +13:00
->inject('request')
->inject('response')
->inject('dbForProject')
2020-12-27 04:20:08 +13:00
->inject('usage')
->inject('user')
->inject('project')
2022-01-24 22:37:14 +13:00
->action(function ($functionId, $entrypoint, $file, $deploy, $request, $response, $dbForProject, $usage, $user, $project) {
2021-01-17 12:38:13 +13:00
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
2021-01-17 12:38:13 +13:00
/** @var Appwrite\Event\Event $usage */
/** @var Appwrite\Auth\User $user */
2022-01-21 03:41:27 +13:00
/** @var Appwrite\Database\Document $project */
2021-01-17 12:38:13 +13:00
$function = $dbForProject->getDocument('functions', $functionId);
2020-07-13 09:18:52 +12:00
2021-06-21 01:59:36 +12:00
if ($function->isEmpty()) {
2020-07-13 09:18:52 +12:00
throw new Exception('Function not found', 404);
}
2020-07-15 08:33:52 +12:00
2021-02-01 08:11:03 +13:00
$file = $request->getFiles('code');
2020-07-15 09:20:46 +12:00
$device = Storage::getDevice('functions');
2021-01-28 02:15:44 +13:00
$fileExt = new FileExt([FileExt::TYPE_GZIP]);
2020-07-15 08:33:52 +12:00
$fileSize = new FileSize(App::getEnv('_APP_STORAGE_LIMIT', 0));
$upload = new Upload();
if (empty($file)) {
throw new Exception('No file sent', 400);
}
// Make sure we handle a single file and multiple files the same way
$file['name'] = (\is_array($file['name']) && isset($file['name'][0])) ? $file['name'][0] : $file['name'];
$file['tmp_name'] = (\is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name'];
$file['size'] = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
2021-01-28 07:08:46 +13:00
if (!$fileExt->isValid($file['name'])) { // Check if file type is allowed
2021-01-27 11:15:20 +13:00
throw new Exception('File type not allowed', 400);
}
2020-07-15 08:33:52 +12:00
if (!$fileSize->isValid($file['size'])) { // Check if file size is exceeding allowed limit
throw new Exception('File size not allowed', 400);
}
if (!$upload->isValid($file['tmp_name'])) {
throw new Exception('Invalid file', 403);
}
// Save to storage
$size = $device->getFileSize($file['tmp_name']);
$path = $device->getPath(\uniqid().'.'.\pathinfo($file['name'], PATHINFO_EXTENSION));
if (!$device->upload($file['tmp_name'], $path)) { // TODO deprecate 'upload' and replace with 'move'
throw new Exception('Failed moving file', 500);
}
2022-01-24 22:37:14 +13:00
if ((bool) $deploy) {
2022-01-25 12:05:17 +13:00
// Remove deploy for all other deployments.
2022-01-25 12:05:41 +13:00
$deployments = $dbForProject->find('deployments', [
2022-01-24 22:37:14 +13:00
new Query('deploy', Query::TYPE_EQUAL, [true]),
new Query('resourceId', Query::TYPE_EQUAL, [$functionId]),
new Query('resourceType', Query::TYPE_EQUAL, ['functions'])
]);
2022-01-25 12:05:17 +13:00
foreach ($deployments as $deployment) {
$deployment->setAttribute('deploy', false);
$dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
}
}
2020-07-13 09:18:52 +12:00
2022-01-25 12:05:17 +13:00
$deploymentId = $dbForProject->getId();
$deployment = $dbForProject->createDocument('deployments', new Document([
'$id' => $deploymentId,
2022-01-21 03:41:27 +13:00
'$read' => ['role:all'],
'$write' => ['role:all'],
'resourceId' => $function->getId(),
'resourceType' => 'functions',
2020-10-31 08:53:27 +13:00
'dateCreated' => time(),
2021-09-01 21:48:56 +12:00
'entrypoint' => $entrypoint,
2020-10-31 08:53:27 +13:00
'path' => $path,
'size' => $size,
2022-01-25 12:05:17 +13:00
'search' => implode(' ', [$deploymentId, $entrypoint]),
2022-01-24 22:37:14 +13:00
'deploy' => ($deploy === 'true'),
2021-05-05 09:25:17 +12:00
]));
2020-05-06 05:30:12 +12:00
2022-01-24 11:25:46 +13:00
// Enqueue a message to start the build
2022-01-31 22:46:24 +13:00
Resque::enqueue(Event::BUILDS_QUEUE_NAME, Event::BUILDS_CLASS_NAME, [
2022-01-24 11:25:46 +13:00
'projectId' => $project->getId(),
2022-01-21 03:41:27 +13:00
'functionId' => $function->getId(),
'deploymentId' => $deploymentId,
'type' => BUILD_TYPE_DEPLOYMENT
]);
$usage
2022-01-29 01:29:06 +13:00
->setParam('storage', $deployment->getAttribute('size', 0))
;
2021-05-27 22:09:14 +12:00
$response->setStatusCode(Response::STATUS_CODE_CREATED);
2022-01-25 12:05:17 +13:00
$response->dynamic($deployment, Response::MODEL_DEPLOYMENT);
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
2022-01-25 12:09:24 +13:00
App::get('/v1/functions/:functionId/deployments')
2020-07-13 09:18:52 +12:00
->groups(['api', 'functions'])
2022-01-25 12:09:24 +13:00
->desc('List Deployments')
2020-05-06 05:30:12 +12:00
->label('scope', 'functions.read')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
2020-05-06 05:30:12 +12:00
->label('sdk.namespace', 'functions')
2022-01-25 12:09:24 +13:00
->label('sdk.method', 'listDeployments')
->label('sdk.description', '/docs/references/functions/list-deployments.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
2022-01-25 12:09:24 +13:00
->label('sdk.response.model', Response::MODEL_DEPLOYMENT_LIST)
->param('functionId', '', new UID(), 'Function ID.')
2020-09-11 02:40:14 +12:00
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
2022-01-25 12:09:24 +13:00
->param('limit', 25, new Range(0, 100), 'Maximum number of deployments to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
2021-12-15 00:21:44 +13:00
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this value to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
2022-01-25 12:09:24 +13:00
->param('cursor', '', new UID(), 'ID of the deployment used as the starting point for the query, excluding the deployment itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
2020-09-11 02:40:14 +12:00
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
2020-12-27 04:20:08 +13:00
->inject('response')
->inject('dbForProject')
->action(function ($functionId, $search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject) {
2021-01-17 12:38:13 +13:00
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
2021-01-17 12:38:13 +13:00
$function = $dbForProject->getDocument('functions', $functionId);
2020-05-06 05:30:12 +12:00
2021-06-21 01:59:36 +12:00
if ($function->isEmpty()) {
2020-07-13 09:18:52 +12:00
throw new Exception('Function not found', 404);
}
2021-05-27 22:09:14 +12:00
if (!empty($cursor)) {
2022-01-25 12:09:24 +13:00
$cursorDeployment = $dbForProject->getDocument('deployments', $cursor);
2021-08-07 00:36:05 +12:00
2022-01-25 12:09:24 +13:00
if ($cursorDeployment->isEmpty()) {
throw new Exception("Deployment '{$cursor}' for the 'cursor' value not found.", 400);
2021-08-07 00:36:05 +12:00
}
}
2021-08-19 01:42:03 +12:00
$queries = [];
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
}
$queries[] = new Query('resourceId', Query::TYPE_EQUAL, [$function->getId()]);
$queries[] = new Query('resourceType', Query::TYPE_EQUAL, ['functions']);
2021-08-19 01:42:03 +12:00
2022-01-25 12:09:24 +13:00
$results = $dbForProject->find('deployments', $queries, $limit, $offset, [], [$orderType], $cursorDeployment ?? null, $cursorDirection);
$sum = $dbForProject->count('deployments', $queries, APP_LIMIT_COUNT);
2020-07-13 09:18:52 +12:00
2022-02-01 00:29:31 +13:00
foreach ($results as $result) {
$build = $dbForProject->getDocument('builds', $result->getAttribute('buildId'));
$result->setAttribute('status', $build->getAttribute('status', 'pending'));
$result->setAttribute('buildStderr', $build->getAttribute('stderr', ''));
$result->setAttribute('buildStdout', $build->getAttribute('stdout', ''));
}
2020-10-31 08:53:27 +13:00
$response->dynamic(new Document([
2022-01-25 12:09:24 +13:00
'deployments' => $results,
2021-05-05 09:25:17 +12:00
'sum' => $sum,
2022-01-25 12:09:24 +13:00
]), Response::MODEL_DEPLOYMENT_LIST);
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
2022-01-25 12:11:33 +13:00
App::get('/v1/functions/:functionId/deployments/:deploymentId')
2020-07-13 09:18:52 +12:00
->groups(['api', 'functions'])
2022-01-25 12:11:33 +13:00
->desc('Get Deployment')
2020-05-06 05:30:12 +12:00
->label('scope', 'functions.read')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
2020-05-06 05:30:12 +12:00
->label('sdk.namespace', 'functions')
2022-01-25 12:11:33 +13:00
->label('sdk.method', 'getDeployment')
->label('sdk.description', '/docs/references/functions/get-deployment.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
2022-01-25 12:11:33 +13:00
->label('sdk.response.model', Response::MODEL_DEPLOYMENT_LIST)
->param('functionId', '', new UID(), 'Function ID.')
2022-01-25 12:11:33 +13:00
->param('deploymentId', '', new UID(), 'Deployment ID.')
2020-12-27 04:20:08 +13:00
->inject('response')
->inject('dbForProject')
2022-01-25 12:11:33 +13:00
->action(function ($functionId, $deploymentId, $response, $dbForProject) {
2021-01-17 12:38:13 +13:00
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
2021-01-17 12:38:13 +13:00
$function = $dbForProject->getDocument('functions', $functionId);
2020-05-06 05:30:12 +12:00
2021-06-21 01:59:36 +12:00
if ($function->isEmpty()) {
2020-07-13 09:18:52 +12:00
throw new Exception('Function not found', 404);
}
2020-05-06 05:30:12 +12:00
2022-01-25 12:11:33 +13:00
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
2020-05-06 05:30:12 +12:00
if ($deployment->getAttribute('resourceId') !== $function->getId()) {
2022-01-25 12:11:33 +13:00
throw new Exception('Deployment not found', 404);
2020-07-13 09:18:52 +12:00
}
2020-05-06 05:30:12 +12:00
2022-01-25 12:11:33 +13:00
if ($deployment->isEmpty()) {
throw new Exception('Deployment not found', 404);
2020-05-06 05:30:12 +12:00
}
2022-01-25 12:11:33 +13:00
$response->dynamic($deployment, Response::MODEL_DEPLOYMENT);
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
2022-01-25 12:14:21 +13:00
App::delete('/v1/functions/:functionId/deployments/:deploymentId')
2020-07-13 09:18:52 +12:00
->groups(['api', 'functions'])
2022-01-25 12:14:21 +13:00
->desc('Delete Deployment')
2020-05-06 05:30:12 +12:00
->label('scope', 'functions.write')
2022-01-25 12:14:21 +13:00
->label('event', 'functions.deployments.delete')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
2020-05-06 05:30:12 +12:00
->label('sdk.namespace', 'functions')
2022-01-25 12:14:21 +13:00
->label('sdk.method', 'deleteDeployment')
->label('sdk.description', '/docs/references/functions/delete-deployment.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('functionId', '', new UID(), 'Function ID.')
2022-01-25 12:14:21 +13:00
->param('deploymentId', '', new UID(), 'Deployment ID.')
2020-12-27 04:20:08 +13:00
->inject('response')
->inject('dbForProject')
2020-12-27 04:20:08 +13:00
->inject('usage')
->inject('deletes')
2022-01-28 14:29:36 +13:00
->action(function ($functionId, $deploymentId, $response, $dbForProject, $usage, $deletes) {
2021-01-17 12:38:13 +13:00
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
2021-01-17 12:38:13 +13:00
/** @var Appwrite\Event\Event $usage */
2022-01-28 14:30:24 +13:00
/** @var Appwrite\Event\Event $deletes */
2021-01-17 12:38:13 +13:00
$function = $dbForProject->getDocument('functions', $functionId);
2021-06-21 01:59:36 +12:00
if ($function->isEmpty()) {
2020-07-13 09:18:52 +12:00
throw new Exception('Function not found', 404);
}
2022-01-25 12:14:21 +13:00
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
if ($deployment->isEmpty()) {
throw new Exception('Deployment not found', 404);
}
2022-02-01 12:44:55 +13:00
if ($deployment->getAttribute('resourceId') !== $function->getId()) {
throw new Exception('Deployment not found', 404);
}
if (!$dbForProject->deleteDocument('deployments', $deployment->getId())) {
throw new Exception('Failed to remove deployment from DB', 500);
2020-05-06 05:30:12 +12:00
}
2022-01-25 12:14:21 +13:00
if($function->getAttribute('deployment') === $deployment->getId()) { // Reset function deployment
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
2022-01-25 12:14:21 +13:00
'deployment' => '',
2021-05-05 09:25:17 +12:00
])));
2020-10-31 08:53:27 +13:00
}
2020-07-19 09:48:28 +12:00
$usage
2022-01-25 12:14:21 +13:00
->setParam('storage', $deployment->getAttribute('size', 0) * -1)
2020-07-19 09:48:28 +12:00
;
$deletes
->setParam('type', DELETE_TYPE_DOCUMENT)
->setParam('document', $deployment)
;
2020-07-13 09:18:52 +12:00
$response->noContent();
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
App::post('/v1/functions/:functionId/executions')
->groups(['api', 'functions'])
2020-05-06 05:30:12 +12:00
->desc('Create Execution')
2020-12-30 20:26:01 +13:00
->label('scope', 'execution.write')
->label('event', 'functions.executions.create')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
2020-05-06 05:30:12 +12:00
->label('sdk.namespace', 'functions')
->label('sdk.method', 'createExecution')
->label('sdk.description', '/docs/references/functions/create-execution.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_EXECUTION)
2021-01-12 11:21:56 +13:00
->label('abuse-limit', 60)
->label('abuse-time', 60)
->param('functionId', '', new UID(), 'Function ID.')
->param('data', '', new Text(8192), 'String of custom data to send to function.', true)
2021-12-07 04:04:00 +13:00
->param('async', true, new Boolean(), 'Execute code asynchronously. Default value is true.', true)
2020-12-27 04:20:08 +13:00
->inject('response')
->inject('project')
->inject('dbForProject')
->inject('user')
->action(function ($functionId, $data, $async, $response, $project, $dbForProject, $user) {
2020-10-30 02:50:49 +13:00
/** @var Appwrite\Utopia\Response $response */
2021-07-26 02:47:18 +12:00
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForProject */
2021-07-26 02:47:18 +12:00
/** @var Utopia\Database\Document $user */
2020-07-17 09:50:37 +12:00
$function = Authorization::skip(fn() => $dbForProject->getDocument('functions', $functionId));
2020-07-13 09:18:52 +12:00
2021-06-21 01:59:36 +12:00
if ($function->isEmpty()) {
2020-07-13 09:18:52 +12:00
throw new Exception('Function not found', 404);
}
2020-07-17 09:50:37 +12:00
2022-02-04 14:29:40 +13:00
$runtimes = Config::getParam('runtimes', []);
$key = $function->getAttribute('runtime', '');
$runtime = isset($runtimes[$key]) ? $runtimes[$key] : null;
if (\is_null($runtime)) {
throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported', 400);
}
2022-01-25 12:16:53 +13:00
$deployment = Authorization::skip(fn() => $dbForProject->getDocument('deployments', $function->getAttribute('deployment')));
2020-07-17 09:50:37 +12:00
if ($deployment->getAttribute('resourceId') !== $function->getId()) {
2022-01-25 12:16:53 +13:00
throw new Exception('Deployment not found. Deploy deployment before trying to execute a function', 404);
2020-07-17 09:50:37 +12:00
}
2022-01-25 12:16:53 +13:00
if ($deployment->isEmpty()) {
throw new Exception('Deployment not found. Deploy deployment before trying to execute a function', 404);
2020-07-17 09:50:37 +12:00
}
2020-12-30 12:00:44 +13:00
/** Check if build has completed */
$build = Authorization::skip(fn() => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
if ($build->isEmpty()) {
throw new Exception('Build not found', 404);
}
if ($build->getAttribute('status') !== 'ready') {
throw new Exception('Build not completed', 400);
}
$validator = new Authorization('execute');
2020-12-30 12:00:44 +13:00
2021-05-05 09:25:17 +12:00
if (!$validator->isValid($function->getAttribute('execute'))) { // Check if user has write access to execute function
2020-12-30 12:00:44 +13:00
throw new Exception($validator->getDescription(), 401);
}
$executionId = $dbForProject->getId();
$execution = Authorization::skip(fn() => $dbForProject->createDocument('executions', new Document([
'$id' => $executionId,
2021-06-21 01:59:36 +12:00
'$read' => (!$user->isEmpty()) ? ['user:' . $user->getId()] : [],
2022-01-21 03:41:27 +13:00
'$write' => ['role:all'],
2020-07-13 09:18:52 +12:00
'dateCreated' => time(),
'functionId' => $function->getId(),
2022-01-25 12:16:53 +13:00
'deploymentId' => $deployment->getId(),
2020-10-31 08:53:27 +13:00
'trigger' => 'http', // http / schedule / event
2020-07-17 20:14:16 +12:00
'status' => 'waiting', // waiting / processing / completed / failed
'statusCode' => 0,
2020-07-13 09:18:52 +12:00
'stdout' => '',
'stderr' => '',
2021-05-05 09:25:17 +12:00
'time' => 0.0,
'search' => implode(' ', [$functionId, $executionId]),
])));
2020-12-30 12:00:44 +13:00
2021-03-11 09:25:54 +13:00
$jwt = ''; // initialize
2021-06-21 01:59:36 +12:00
if (!$user->isEmpty()) { // If userId exists, generate a JWT for function
2021-10-08 04:35:17 +13:00
2021-07-23 02:49:52 +12:00
$sessions = $user->getAttribute('sessions', []);
$current = new Document();
2021-07-26 02:47:18 +12:00
foreach ($sessions as $session) { /** @var Utopia\Database\Document $session */
2021-07-23 02:49:52 +12:00
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$current = $session;
}
}
2021-07-23 02:49:52 +12:00
if(!$current->isEmpty()) {
2021-03-11 09:25:54 +13:00
$jwtObj = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$jwt = $jwtObj->encode([
2021-03-11 06:48:05 +13:00
'userId' => $user->getId(),
2021-07-23 02:49:52 +12:00
'sessionId' => $current->getId(),
2021-03-11 06:48:05 +13:00
]);
}
}
2021-08-24 21:32:27 +12:00
if ($async) {
Resque::enqueue('v1-functions', 'FunctionsV1', [
'projectId' => $project->getId(),
'deploymentId' => $deployment->getId(),
'buildId' => $deployment->getAttribute('buildId', ''),
'path' => $build->getAttribute('outputPath', ''),
'vars' => $function->getAttribute('vars', []),
'data' => $data,
'runtime' => $function->getAttribute('runtime', ''),
'timeout' => $function->getAttribute('timeout', 0),
2022-02-04 14:29:40 +13:00
'baseImage' => $runtime['image'],
2021-08-24 21:32:27 +12:00
'webhooks' => $project->getAttribute('webhooks', []),
'userId' => $user->getId(),
2021-08-24 21:32:27 +12:00
'functionId' => $function->getId(),
'executionId' => $execution->getId(),
'trigger' => 'http',
'jwt' => $jwt,
2021-08-24 21:32:27 +12:00
]);
2020-07-13 09:18:52 +12:00
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($execution, Response::MODEL_EXECUTION);
return $response;
2021-08-24 21:32:27 +12:00
}
2022-01-11 03:18:33 +13:00
/** Send variables */
2022-02-04 14:29:40 +13:00
$vars = \array_merge($function->getAttribute('vars', []), [
'ENTRYPOINT_NAME' => $deployment->getAttribute('entrypoint', ''),
'APPWRITE_FUNCTION_ID' => $function->getId(),
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(),
'APPWRITE_FUNCTION_TRIGGER' => 'http',
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'],
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'],
'APPWRITE_FUNCTION_DATA' => $data,
'APPWRITE_FUNCTION_USER_ID' => $user->getId(),
'APPWRITE_FUNCTION_JWT' => $jwt,
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId()
]);
2022-01-11 03:18:33 +13:00
// Directly execute function.
$ch = \curl_init();
2022-01-26 12:45:41 +13:00
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/functions/{$function->getId()}/executions");
2022-01-11 03:18:33 +13:00
\curl_setopt($ch, CURLOPT_POST, true);
\curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'deploymentId' => $deployment->getId(),
'buildId' => $deployment->getAttribute('buildId', ''),
'path' => $build->getAttribute('outputPath', ''),
2022-02-04 14:29:40 +13:00
'vars' => $vars,
2022-01-11 03:18:33 +13:00
'data' => $data,
'runtime' => $function->getAttribute('runtime', ''),
'timeout' => $function->getAttribute('timeout', 0),
2022-02-04 14:29:40 +13:00
'baseImage' => $runtime['image'],
2022-01-11 03:18:33 +13:00
'webhooks' => $project->getAttribute('webhooks', []),
'userId' => $user->getId(),
]));
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
\curl_setopt($ch, CURLOPT_TIMEOUT, App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900) + 200); // + 200 for safety margin
\curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'x-appwrite-project: '.$project->getId(),
'x-appwrite-executor-key: '. App::getEnv('_APP_EXECUTOR_SECRET', '')
]);
2021-08-24 21:32:27 +12:00
2022-01-11 03:18:33 +13:00
$responseExecute = \curl_exec($ch);
2021-08-24 21:32:27 +12:00
2022-01-11 03:18:33 +13:00
$error = \curl_error($ch);
if (!empty($error)) {
Console::error('Curl error: '.$error);
}
\curl_close($ch);
2021-08-24 21:32:27 +12:00
2022-02-04 14:29:40 +13:00
$responseExecute = json_decode($responseExecute, true);
$execution->setAttribute('status', $responseExecute['status']);
$execution->setAttribute('statusCode', $responseExecute['statusCode']);
$execution->setAttribute('stdout', $responseExecute['stdout']);
$execution->setAttribute('stderr', $responseExecute['stderr']);
$execution->setAttribute('time', $responseExecute['time']);
Authorization::skip(fn() => $dbForProject->updateDocument('executions', $executionId, $execution));
$responseExecute['response'] = ($responseExecute['status'] !== 'completed') ? $responseExecute['stderr'] : $responseExecute['stdout'];
2022-01-11 03:18:33 +13:00
$response
2022-01-27 12:19:02 +13:00
->setStatusCode(Response::STATUS_CODE_CREATED)
2022-02-04 14:29:40 +13:00
->dynamic(new Document($responseExecute), Response::MODEL_SYNC_EXECUTION);
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
App::get('/v1/functions/:functionId/executions')
->groups(['api', 'functions'])
2020-05-06 05:30:12 +12:00
->desc('List Executions')
2020-12-30 20:26:01 +13:00
->label('scope', 'execution.read')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
2020-05-06 05:30:12 +12:00
->label('sdk.namespace', 'functions')
->label('sdk.method', 'listExecutions')
->label('sdk.description', '/docs/references/functions/list-executions.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_EXECUTION_LIST)
->param('functionId', '', new UID(), 'Function ID.')
2021-12-15 00:21:44 +13:00
->param('limit', 25, new Range(0, 100), 'Maximum number of executions to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this value to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
2021-12-15 00:21:44 +13:00
->param('cursor', '', new UID(), 'ID of the execution used as the starting point for the query, excluding the execution itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
2020-12-27 04:20:08 +13:00
->inject('response')
->inject('dbForProject')
->action(function ($functionId, $limit, $offset, $search, $cursor, $cursorDirection, $response, $dbForProject) {
2021-01-17 12:38:13 +13:00
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
2020-05-06 05:30:12 +12:00
$function = Authorization::skip(fn() => $dbForProject->getDocument('functions', $functionId));
2020-05-06 05:30:12 +12:00
2021-06-21 01:59:36 +12:00
if ($function->isEmpty()) {
2020-07-13 09:18:52 +12:00
throw new Exception('Function not found', 404);
}
2021-05-27 22:09:14 +12:00
if (!empty($cursor)) {
$cursorExecution = $dbForProject->getDocument('executions', $cursor);
2021-08-07 00:36:05 +12:00
if ($cursorExecution->isEmpty()) {
throw new Exception("Execution '{$cursor}' for the 'cursor' value not found.", 400);
2021-08-07 00:36:05 +12:00
}
}
$queries = [
new Query('functionId', Query::TYPE_EQUAL, [$function->getId()])
];
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
}
$results = $dbForProject->find('executions', $queries, $limit, $offset, [], [Database::ORDER_DESC], $cursorExecution ?? null, $cursorDirection);
$sum = $dbForProject->count('executions', $queries, APP_LIMIT_COUNT);
2020-07-13 09:18:52 +12:00
2020-10-31 08:53:27 +13:00
$response->dynamic(new Document([
2021-05-27 22:09:14 +12:00
'executions' => $results,
2021-05-05 09:25:17 +12:00
'sum' => $sum,
2020-10-31 08:53:27 +13:00
]), Response::MODEL_EXECUTION_LIST);
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
App::get('/v1/functions/:functionId/executions/:executionId')
->groups(['api', 'functions'])
2020-05-06 05:30:12 +12:00
->desc('Get Execution')
2020-12-30 20:26:01 +13:00
->label('scope', 'execution.read')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
2020-05-06 05:30:12 +12:00
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getExecution')
->label('sdk.description', '/docs/references/functions/get-execution.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_EXECUTION)
->param('functionId', '', new UID(), 'Function ID.')
->param('executionId', '', new UID(), 'Execution ID.')
2020-12-27 04:20:08 +13:00
->inject('response')
->inject('dbForProject')
->action(function ($functionId, $executionId, $response, $dbForProject) {
2021-01-17 12:38:13 +13:00
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
$function = Authorization::skip(fn() => $dbForProject->getDocument('functions', $functionId));
2020-05-06 05:30:12 +12:00
2021-06-21 01:59:36 +12:00
if ($function->isEmpty()) {
2020-07-13 09:18:52 +12:00
throw new Exception('Function not found', 404);
}
2020-05-06 05:30:12 +12:00
$execution = $dbForProject->getDocument('executions', $executionId);
2020-05-06 05:30:12 +12:00
2020-10-28 08:46:15 +13:00
if ($execution->getAttribute('functionId') !== $function->getId()) {
2020-07-13 09:18:52 +12:00
throw new Exception('Execution not found', 404);
}
2020-05-06 05:30:12 +12:00
2021-06-21 01:59:36 +12:00
if ($execution->isEmpty()) {
2020-07-13 09:18:52 +12:00
throw new Exception('Execution not found', 404);
2020-05-06 05:30:12 +12:00
}
2020-07-13 09:18:52 +12:00
2020-10-31 08:53:27 +13:00
$response->dynamic($execution, Response::MODEL_EXECUTION);
2020-12-27 04:20:08 +13:00
});
App::post('/v1/builds/:buildId')
->groups(['api', 'functions'])
->desc('Retry Build')
->label('scope', 'functions.write')
2022-01-25 12:17:10 +13:00
->label('event', 'functions.deployments.update')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'retryBuild')
->label('sdk.description', '/docs/references/functions/retry-build.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('buildId', '', new UID(), 'Build unique ID.')
->inject('response')
2022-01-06 22:45:56 +13:00
->inject('dbForProject')
->inject('project')
2022-01-06 22:45:56 +13:00
->action(function ($buildId, $response, $dbForProject, $project) {
/** @var Appwrite\Utopia\Response $response */
2022-01-06 22:45:56 +13:00
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $project */
2022-01-24 12:05:07 +13:00
$build = Authorization::skip(fn() => $dbForProject->getDocument('builds', $buildId));
if ($build->isEmpty()) {
throw new Exception('Build not found', 404);
}
if ($build->getAttribute('status') !== 'failed') {
throw new Exception('Build not failed', 400);
}
// Enqueue a message to start the build
2022-01-31 22:46:24 +13:00
Resque::enqueue(Event::BUILDS_QUEUE_NAME, Event::BUILDS_CLASS_NAME, [
'projectId' => $project->getId(),
'buildId' => $buildId,
'type' => BUILD_TYPE_RETRY
]);
$response->noContent();
});