2020-05-04 14:35:01 +00:00
< ? php
2021-03-10 17:48:05 +00:00
use Ahc\Jwt\JWT ;
use Appwrite\Auth\Auth ;
2022-04-19 13:13:55 +00:00
use Appwrite\Event\Build ;
2022-05-24 14:28:27 +00:00
use Appwrite\Event\Delete ;
use Appwrite\Event\Event ;
2022-11-15 18:13:17 +00:00
use Appwrite\Event\Func ;
2023-10-25 09:46:45 +00:00
use Appwrite\Event\Usage ;
2023-09-12 03:24:11 +00:00
use Appwrite\Event\Validator\FunctionEvent ;
2022-02-18 12:36:24 +00:00
use Appwrite\Extend\Exception ;
2024-05-20 10:44:08 +00:00
use Appwrite\Extend\Exception as AppwriteException ;
2024-08-06 08:19:28 +00:00
use Appwrite\Functions\Validator\Headers ;
2024-08-14 10:43:16 +00:00
use Appwrite\Functions\Validator\RuntimeSpecification ;
2023-03-10 12:20:24 +00:00
use Appwrite\Messaging\Adapter\Realtime ;
2024-06-27 22:12:23 +00:00
use Appwrite\Platform\Tasks\ScheduleExecutions ;
2024-03-06 17:34:21 +00:00
use Appwrite\Task\Validator\Cron ;
use Appwrite\Utopia\Database\Validator\CustomId ;
use Appwrite\Utopia\Database\Validator\Queries\Deployments ;
use Appwrite\Utopia\Database\Validator\Queries\Executions ;
use Appwrite\Utopia\Database\Validator\Queries\Functions ;
use Appwrite\Utopia\Response ;
use Appwrite\Utopia\Response\Model\Rule ;
use Executor\Executor ;
use MaxMind\Db\Reader ;
use Utopia\App ;
use Utopia\CLI\Console ;
use Utopia\Config\Config ;
use Utopia\Database\Database ;
use Utopia\Database\DateTime ;
use Utopia\Database\Document ;
use Utopia\Database\Exception\Duplicate as DuplicateException ;
2024-02-12 16:02:04 +00:00
use Utopia\Database\Exception\Query as QueryException ;
2023-06-11 10:29:04 +00:00
use Utopia\Database\Helpers\ID ;
use Utopia\Database\Helpers\Permission ;
use Utopia\Database\Helpers\Role ;
2024-03-06 17:34:21 +00:00
use Utopia\Database\Query ;
use Utopia\Database\Validator\Authorization ;
2024-06-07 19:05:29 +00:00
use Utopia\Database\Validator\Datetime as DatetimeValidator ;
2024-10-17 05:41:24 +00:00
use Utopia\Database\Validator\Query\Cursor ;
2024-03-06 17:34:21 +00:00
use Utopia\Database\Validator\Roles ;
2021-07-25 14:47:18 +00:00
use Utopia\Database\Validator\UID ;
2022-05-24 14:28:27 +00:00
use Utopia\Storage\Device ;
2021-01-22 08:28:33 +00:00
use Utopia\Storage\Validator\File ;
2021-01-27 13:15:44 +00:00
use Utopia\Storage\Validator\FileExt ;
2021-01-22 08:28:33 +00:00
use Utopia\Storage\Validator\FileSize ;
use Utopia\Storage\Validator\Upload ;
2022-05-24 14:28:27 +00:00
use Utopia\Swoole\Request ;
2024-04-01 11:08:46 +00:00
use Utopia\System\System ;
2024-08-05 13:08:41 +00:00
use Utopia\Validator\AnyOf ;
2020-05-05 17:30:12 +00:00
use Utopia\Validator\ArrayList ;
2024-03-06 17:34:21 +00:00
use Utopia\Validator\Assoc ;
use Utopia\Validator\Boolean ;
2024-08-08 17:48:45 +00:00
use Utopia\Validator\Nullable ;
2020-05-04 14:35:01 +00:00
use Utopia\Validator\Range ;
2024-03-06 17:34:21 +00:00
use Utopia\Validator\Text ;
2020-05-05 17:30:12 +00:00
use Utopia\Validator\WhiteList ;
2023-08-09 15:53:58 +00:00
use Utopia\VCS\Adapter\Git\GitHub ;
2023-10-27 14:08:33 +00:00
use Utopia\VCS\Exception\RepositoryNotFound ;
2020-05-04 14:35:01 +00:00
include_once __DIR__ . '/../shared/api.php' ;
2024-10-22 11:00:10 +00:00
// $redeployVcs = function (Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Build $queueForBuilds, Document $template, GitHub $github) {
// $deploymentId = ID::unique();
// $entrypoint = $function->getAttribute('entrypoint', '');
// $providerInstallationId = $installation->getAttribute('providerInstallationId', '');
// $privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
// $githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
// $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
// $owner = $github->getOwnerName($providerInstallationId);
// $providerRepositoryId = $function->getAttribute('providerRepositoryId', '');
// try {
// $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
// if (empty($repositoryName)) {
// throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
// }
// } catch (RepositoryNotFound $e) {
// throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
// }
// $providerBranch = $function->getAttribute('providerBranch', 'main');
// $authorUrl = "https://github.com/$owner";
// $repositoryUrl = "https://github.com/$owner/$repositoryName";
// $branchUrl = "https://github.com/$owner/$repositoryName/tree/$providerBranch";
// $commitDetails = [];
// if ($template->isEmpty()) {
// try {
// $commitDetails = $github->getLatestCommit($owner, $repositoryName, $providerBranch);
// } catch (\Throwable $error) {
// Console::warning('Failed to get latest commit details');
// Console::warning($error->getMessage());
// Console::warning($error->getTraceAsString());
// }
// }
// $deployment = $dbForProject->createDocument('deployments', new Document([
// '$id' => $deploymentId,
// '$permissions' => [
// Permission::read(Role::any()),
// Permission::update(Role::any()),
// Permission::delete(Role::any()),
// ],
// 'resourceId' => $function->getId(),
// 'resourceInternalId' => $function->getInternalId(),
// 'resourceType' => 'functions',
// 'entrypoint' => $entrypoint,
// 'commands' => $function->getAttribute('commands', ''),
// 'type' => 'vcs',
// 'installationId' => $installation->getId(),
// 'installationInternalId' => $installation->getInternalId(),
// 'providerRepositoryId' => $providerRepositoryId,
// 'repositoryId' => $function->getAttribute('repositoryId', ''),
// 'repositoryInternalId' => $function->getAttribute('repositoryInternalId', ''),
// 'providerBranchUrl' => $branchUrl,
// 'providerRepositoryName' => $repositoryName,
// 'providerRepositoryOwner' => $owner,
// 'providerRepositoryUrl' => $repositoryUrl,
// 'providerCommitHash' => $commitDetails['commitHash'] ?? '',
// 'providerCommitAuthorUrl' => $authorUrl,
// 'providerCommitAuthor' => $commitDetails['commitAuthor'] ?? '',
// 'providerCommitMessage' => $commitDetails['commitMessage'] ?? '',
// 'providerCommitUrl' => $commitDetails['commitUrl'] ?? '',
// 'providerBranch' => $providerBranch,
// 'providerRootDirectory' => $function->getAttribute('providerRootDirectory', ''),
// 'search' => implode(' ', [$deploymentId, $entrypoint]),
// 'activate' => true,
// ]));
// $queueForBuilds
// ->setType(BUILD_TYPE_DEPLOYMENT)
// ->setResource($function)
// ->setDeployment($deployment)
// ->setTemplate($template);
// };
// App::post('/v1/functions')
// ->groups(['api', 'functions'])
// ->desc('Create function')
// ->label('scope', 'functions.write')
// ->label('event', 'functions.[functionId].create')
// ->label('audits.event', 'function.create')
// ->label('audits.resource', 'function/{response.$id}')
// ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
// ->label('sdk.namespace', 'functions')
// ->label('sdk.method', 'create')
// ->label('sdk.description', '/docs/references/functions/create-function.md')
// ->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 a custom ID or generate a random ID with `ID.unique()`. 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.')
// ->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
// ->param('runtime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution runtime.')
// ->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of role strings with execution permissions. By default no user is granted with any execute permissions. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true)
// ->param('events', [], new ArrayList(new FunctionEvent(), 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) System::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.', true)
// ->param('enabled', true, new Boolean(), 'Is function enabled? When set to \'disabled\', users cannot access the function but Server SDKs with and API key can still access the function. No data is lost when this is toggled.', true)
// ->param('logging', true, new Boolean(), 'Whether executions will be logged. When set to false, executions will not be logged, but will reduce resource used by your Appwrite project.', true)
// ->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File. This path is relative to the "providerRootDirectory".', true)
// ->param('commands', '', new Text(8192, 0), 'Build Commands.', true)
// ->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for API key auto-generated for every execution. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.', true)
// ->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for VCS (Version Control System) deployment.', true)
// ->param('providerRepositoryId', '', new Text(128, 0), 'Repository ID of the repo linked to the function.', true)
// ->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function.', true)
// ->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the function? In silent mode, comments will not be made on commits and pull requests.', true)
// ->param('providerRootDirectory', '', new Text(128, 0), 'Path to function code in the linked repo.', true)
// ->param('templateRepository', '', new Text(128, 0), 'Repository name of the template.', true)
// ->param('templateOwner', '', new Text(128, 0), 'The name of the owner of the template.', true)
// ->param('templateRootDirectory', '', new Text(128, 0), 'Path to function code in the template repo.', true)
// ->param('templateVersion', '', new Text(128, 0), 'Version (tag) for the repo linked to the function template.', true)
// ->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
// $plan,
// Config::getParam('runtime-specifications', []),
// App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT),
// App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT)
// ), 'Runtime specification for the function and builds.', true, ['plan'])
// ->inject('request')
// ->inject('response')
// ->inject('dbForProject')
// ->inject('project')
// ->inject('user')
// ->inject('queueForEvents')
// ->inject('queueForBuilds')
// ->inject('dbForConsole')
// ->inject('gitHub')
// ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
// $functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
// $allowList = \array_filter(\explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', '')));
// if (!empty($allowList) && !\in_array($runtime, $allowList)) {
// throw new Exception(Exception::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $runtime . '" is not supported');
// }
// // build from template
// $template = new Document([]);
// if (
// !empty($templateRepository)
// && !empty($templateOwner)
// && !empty($templateRootDirectory)
// && !empty($templateVersion)
// ) {
// $template->setAttribute('repositoryName', $templateRepository)
// ->setAttribute('ownerName', $templateOwner)
// ->setAttribute('rootDirectory', $templateRootDirectory)
// ->setAttribute('version', $templateVersion);
// }
// $installation = $dbForConsole->getDocument('installations', $installationId);
// if (!empty($installationId) && $installation->isEmpty()) {
// throw new Exception(Exception::INSTALLATION_NOT_FOUND);
// }
// if (!empty($providerRepositoryId) && (empty($installationId) || empty($providerBranch))) {
// throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'When connecting to VCS (Version Control System), you need to provide "installationId" and "providerBranch".');
// }
// $function = $dbForProject->createDocument('functions', new Document([
// '$id' => $functionId,
// 'execute' => $execute,
// 'enabled' => $enabled,
// 'live' => true,
// 'logging' => $logging,
// 'name' => $name,
// 'runtime' => $runtime,
// 'deploymentInternalId' => '',
// 'deployment' => '',
// 'events' => $events,
// 'schedule' => $schedule,
// 'scheduleInternalId' => '',
// 'scheduleId' => '',
// 'timeout' => $timeout,
// 'entrypoint' => $entrypoint,
// 'commands' => $commands,
// 'scopes' => $scopes,
// 'search' => implode(' ', [$functionId, $name, $runtime]),
// 'version' => 'v4',
// 'installationId' => $installation->getId(),
// 'installationInternalId' => $installation->getInternalId(),
// 'providerRepositoryId' => $providerRepositoryId,
// 'repositoryId' => '',
// 'repositoryInternalId' => '',
// 'providerBranch' => $providerBranch,
// 'providerRootDirectory' => $providerRootDirectory,
// 'providerSilentMode' => $providerSilentMode,
// 'specification' => $specification
// ]));
// $schedule = Authorization::skip(
// fn () => $dbForConsole->createDocument('schedules', new Document([
// 'region' => System::getEnv('_APP_REGION', 'default'), // Todo replace with projects region
// 'resourceType' => 'function',
// 'resourceId' => $function->getId(),
// 'resourceInternalId' => $function->getInternalId(),
// 'resourceUpdatedAt' => DateTime::now(),
// 'projectId' => $project->getId(),
// 'schedule' => $function->getAttribute('schedule'),
// 'active' => false,
// ]))
// );
// $function->setAttribute('scheduleId', $schedule->getId());
// $function->setAttribute('scheduleInternalId', $schedule->getInternalId());
// // Git connect logic
// if (!empty($providerRepositoryId)) {
// $teamId = $project->getAttribute('teamId', '');
// $repository = $dbForConsole->createDocument('repositories', new Document([
// '$id' => ID::unique(),
// '$permissions' => [
// Permission::read(Role::team(ID::custom($teamId))),
// Permission::update(Role::team(ID::custom($teamId), 'owner')),
// Permission::update(Role::team(ID::custom($teamId), 'developer')),
// Permission::delete(Role::team(ID::custom($teamId), 'owner')),
// Permission::delete(Role::team(ID::custom($teamId), 'developer')),
// ],
// 'installationId' => $installation->getId(),
// 'installationInternalId' => $installation->getInternalId(),
// 'projectId' => $project->getId(),
// 'projectInternalId' => $project->getInternalId(),
// 'providerRepositoryId' => $providerRepositoryId,
// 'resourceId' => $function->getId(),
// 'resourceInternalId' => $function->getInternalId(),
// 'resourceType' => 'function',
// 'providerPullRequestIds' => []
// ]));
// $function->setAttribute('repositoryId', $repository->getId());
// $function->setAttribute('repositoryInternalId', $repository->getInternalId());
// }
// $function = $dbForProject->updateDocument('functions', $function->getId(), $function);
// if (!empty($providerRepositoryId)) {
// // Deploy VCS
// $redeployVcs($request, $function, $project, $installation, $dbForProject, $queueForBuilds, $template, $github);
// } elseif (!$template->isEmpty()) {
// // Deploy non-VCS from template
// $deploymentId = ID::unique();
// $deployment = $dbForProject->createDocument('deployments', new Document([
// '$id' => $deploymentId,
// '$permissions' => [
// Permission::read(Role::any()),
// Permission::update(Role::any()),
// Permission::delete(Role::any()),
// ],
// 'resourceId' => $function->getId(),
// 'resourceInternalId' => $function->getInternalId(),
// 'resourceType' => 'functions',
// 'entrypoint' => $function->getAttribute('entrypoint', ''),
// 'commands' => $function->getAttribute('commands', ''),
// 'type' => 'manual',
// 'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint', '')]),
// 'activate' => true,
// ]));
// $queueForBuilds
// ->setType(BUILD_TYPE_DEPLOYMENT)
// ->setResource($function)
// ->setDeployment($deployment)
// ->setTemplate($template);
// }
// $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
// if (!empty($functionsDomain)) {
// $ruleId = ID::unique();
// $routeSubdomain = ID::unique();
// $domain = "{$routeSubdomain}.{$functionsDomain}";
// $rule = Authorization::skip(
// fn () => $dbForConsole->createDocument('rules', new Document([
// '$id' => $ruleId,
// 'projectId' => $project->getId(),
// 'projectInternalId' => $project->getInternalId(),
// 'domain' => $domain,
// 'resourceType' => 'function',
// 'resourceId' => $function->getId(),
// 'resourceInternalId' => $function->getInternalId(),
// 'status' => 'verified',
// 'certificateId' => '',
// ]))
// );
// /** Trigger Webhook */
// $ruleModel = new Rule();
// $ruleCreate =
// $queueForEvents
// ->setClass(Event::WEBHOOK_CLASS_NAME)
// ->setQueue(Event::WEBHOOK_QUEUE_NAME);
// $ruleCreate
// ->setProject($project)
// ->setEvent('rules.[ruleId].create')
// ->setParam('ruleId', $rule->getId())
// ->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules())))
// ->trigger();
// /** Trigger Functions */
// $ruleCreate
// ->setClass(Event::FUNCTIONS_CLASS_NAME)
// ->setQueue(Event::FUNCTIONS_QUEUE_NAME)
// ->trigger();
// /** Trigger realtime event */
// $allEvents = Event::generateEvents('rules.[ruleId].create', [
// 'ruleId' => $rule->getId(),
// ]);
// $target = Realtime::fromPayload(
// // Pass first, most verbose event pattern
// event: $allEvents[0],
// payload: $rule,
// project: $project
// );
// Realtime::send(
// projectId: 'console',
// payload: $rule->getArrayCopy(),
// events: $allEvents,
// channels: $target['channels'],
// roles: $target['roles']
// );
// Realtime::send(
// projectId: $project->getId(),
// payload: $rule->getArrayCopy(),
// events: $allEvents,
// channels: $target['channels'],
// roles: $target['roles']
// );
// }
// $queueForEvents->setParam('functionId', $function->getId());
// $response
// ->setStatusCode(Response::STATUS_CODE_CREATED)
// ->dynamic($function, Response::MODEL_FUNCTION);
// });
2020-07-12 21:18:52 +00:00
App :: get ( '/v1/functions' )
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'List functions' )
2020-05-05 17:30:12 +00:00
-> label ( 'scope' , 'functions.read' )
2021-04-16 07:22:17 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-05 17:30:12 +00:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'list' )
-> label ( 'sdk.description' , '/docs/references/functions/list-functions.md' )
2020-11-11 21:02:24 +00: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 )
2023-03-29 19:38:39 +00:00
-> param ( 'queries' , [], new Functions (), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). 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 ( ', ' , Functions :: ALLOWED_ATTRIBUTES ), true )
2020-09-10 14:40:14 +00:00
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-08-23 09:02:06 +00:00
-> action ( function ( array $queries , string $search , Response $response , Database $dbForProject ) {
2020-07-12 21:18:52 +00:00
2024-02-12 16:02:04 +00:00
try {
$queries = Query :: parseQueries ( $queries );
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
}
2021-08-06 12:36:05 +00:00
2022-08-11 23:53:52 +00:00
if ( ! empty ( $search )) {
2022-08-23 09:02:06 +00:00
$queries [] = Query :: search ( 'search' , $search );
2021-08-06 12:36:05 +00:00
}
2024-02-12 09:55:45 +00:00
/**
2024-02-12 10:03:31 +00:00
* Get cursor document if there was a cursor query , we use array_filter and reset for reference $cursor to $queries
2024-02-12 09:55:45 +00:00
*/
2023-08-22 03:25:55 +00:00
$cursor = \array_filter ( $queries , function ( $query ) {
2023-12-06 14:10:40 +00:00
return \in_array ( $query -> getMethod (), [ Query :: TYPE_CURSOR_AFTER , Query :: TYPE_CURSOR_BEFORE ]);
2023-08-22 03:25:55 +00:00
});
2022-08-30 23:31:43 +00:00
$cursor = reset ( $cursor );
2022-08-30 11:55:23 +00:00
if ( $cursor ) {
2022-08-23 09:02:06 +00:00
/** @var Query $cursor */
2024-10-17 05:41:24 +00:00
$validator = new Cursor ();
if ( ! $validator -> isValid ( $cursor )) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $validator -> getDescription ());
}
2022-08-23 09:02:06 +00:00
$functionId = $cursor -> getValue ();
$cursorDocument = $dbForProject -> getDocument ( 'functions' , $functionId );
2021-08-18 13:42:03 +00:00
2022-08-11 23:53:52 +00:00
if ( $cursorDocument -> isEmpty ()) {
2022-08-23 09:02:06 +00:00
throw new Exception ( Exception :: GENERAL_CURSOR_NOT_FOUND , " Function ' { $functionId } ' for the 'cursor' value not found. " );
2022-08-11 23:53:52 +00:00
}
2022-08-23 09:02:06 +00:00
$cursor -> setValue ( $cursorDocument );
2021-08-18 13:42:03 +00:00
}
2020-07-12 21:18:52 +00:00
2022-08-23 09:02:06 +00:00
$filterQueries = Query :: groupByType ( $queries )[ 'filters' ];
2020-10-30 19:53:27 +00:00
$response -> dynamic ( new Document ([
2022-08-23 09:02:06 +00:00
'functions' => $dbForProject -> find ( 'functions' , $queries ),
2022-08-11 23:53:52 +00:00
'total' => $dbForProject -> count ( 'functions' , $filterQueries , APP_LIMIT_COUNT ),
2020-10-30 19:53:27 +00:00
]), Response :: MODEL_FUNCTION_LIST );
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
2021-12-09 13:02:12 +00:00
App :: get ( '/v1/functions/runtimes' )
-> groups ([ 'api' , 'functions' ])
2022-03-02 12:21:03 +00:00
-> desc ( 'List runtimes' )
2021-12-09 13:02:12 +00:00
-> 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' )
2022-05-24 14:28:27 +00:00
-> action ( function ( Response $response ) {
2021-12-09 13:02:12 +00:00
$runtimes = Config :: getParam ( 'runtimes' );
2024-04-01 11:02:47 +00:00
$allowList = \array_filter ( \explode ( ',' , System :: getEnv ( '_APP_FUNCTIONS_RUNTIMES' , '' )));
2024-02-25 05:49:25 +00:00
$allowed = [];
2024-08-09 11:59:43 +00:00
foreach ( $runtimes as $id => $runtime ) {
if ( ! empty ( $allowList ) && ! \in_array ( $id , $allowList )) {
2024-02-25 05:49:25 +00:00
continue ;
}
2024-08-09 11:59:43 +00:00
$runtime [ '$id' ] = $id ;
2024-08-09 11:56:55 +00:00
$allowed [] = $runtime ;
2024-02-25 05:49:25 +00:00
}
2021-12-09 13:02:12 +00:00
2022-04-13 12:39:31 +00:00
$response -> dynamic ( new Document ([
2024-02-25 05:49:25 +00:00
'total' => count ( $allowed ),
'runtimes' => $allowed
2021-12-09 13:02:12 +00:00
]), Response :: MODEL_RUNTIME_LIST );
});
2024-07-31 11:27:32 +00:00
App :: get ( '/v1/functions/specifications' )
2024-07-12 09:25:57 +00:00
-> groups ([ 'api' , 'functions' ])
2024-08-20 04:23:11 +00:00
-> desc ( 'List available function runtime specifications' )
2024-07-12 09:25:57 +00:00
-> label ( 'scope' , 'functions.read' )
2024-08-01 06:30:24 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_ADMIN ])
2024-07-12 09:25:57 +00:00
-> label ( 'sdk.namespace' , 'functions' )
2024-08-20 04:23:11 +00:00
-> label ( 'sdk.method' , 'listSpecifications' )
-> label ( 'sdk.description' , '/docs/references/functions/list-specifications.md' )
2024-07-12 09:25:57 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
2024-07-31 11:27:32 +00:00
-> label ( 'sdk.response.model' , Response :: MODEL_SPECIFICATION_LIST )
2024-07-12 09:25:57 +00:00
-> inject ( 'response' )
2024-07-16 05:26:53 +00:00
-> inject ( 'plan' )
2024-08-06 09:54:46 +00:00
-> action ( function ( Response $response , array $plan ) {
2024-07-31 11:27:32 +00:00
$allRuntimeSpecs = Config :: getParam ( 'runtime-specifications' , []);
2024-07-31 06:21:07 +00:00
2024-07-31 11:27:32 +00:00
$runtimeSpecs = [];
2024-08-01 06:30:24 +00:00
foreach ( $allRuntimeSpecs as $spec ) {
$spec [ 'enabled' ] = true ;
2024-07-31 06:21:07 +00:00
2024-08-06 09:54:46 +00:00
if ( array_key_exists ( 'runtimeSpecifications' , $plan )) {
2024-08-02 08:00:42 +00:00
$spec [ 'enabled' ] = in_array ( $spec [ 'slug' ], $plan [ 'runtimeSpecifications' ]);
}
2024-07-31 06:21:07 +00:00
2024-07-31 11:27:32 +00:00
// Only add specs that are within the limits set by environment variables
2024-10-27 16:22:45 +00:00
if ( $spec [ 'cpus' ] <= System :: getEnv ( '_APP_COMPUTE_CPUS' , 1 ) && $spec [ 'memory' ] <= System :: getEnv ( '_APP_COMPUTE_MEMORY' , 512 )) {
2024-07-31 11:27:32 +00:00
$runtimeSpecs [] = $spec ;
2024-07-31 06:21:07 +00:00
}
}
2024-07-12 09:25:57 +00:00
$response -> dynamic ( new Document ([
2024-07-31 11:27:32 +00:00
'specifications' => $runtimeSpecs ,
2024-08-19 04:18:45 +00:00
'total' => count ( $runtimeSpecs )
2024-07-31 11:27:32 +00:00
]), Response :: MODEL_SPECIFICATION_LIST );
2024-07-12 09:25:57 +00:00
});
2020-07-12 21:18:52 +00:00
App :: get ( '/v1/functions/:functionId' )
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'Get function' )
2020-05-05 17:30:12 +00:00
-> label ( 'scope' , 'functions.read' )
2021-04-16 07:22:17 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-05 17:30:12 +00:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'get' )
-> label ( 'sdk.description' , '/docs/references/functions/get-function.md' )
2020-11-11 21:02:24 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_FUNCTION )
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-05-24 14:28:27 +00:00
-> action ( function ( string $functionId , Response $response , Database $dbForProject ) {
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-05-05 17:30:12 +00:00
2021-06-20 13:59:36 +00:00
if ( $function -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-05-05 17:30:12 +00:00
}
2020-10-30 19:53:27 +00:00
$response -> dynamic ( $function , Response :: MODEL_FUNCTION );
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
2020-10-30 19:53:27 +00:00
App :: get ( '/v1/functions/:functionId/usage' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Get function usage' )
2023-09-27 17:10:21 +00:00
-> groups ([ 'api' , 'functions' , 'usage' ])
2020-10-30 19:53:27 +00:00
-> label ( 'scope' , 'functions.read' )
2021-04-16 07:22:17 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_ADMIN ])
2020-10-30 19:53:27 +00:00
-> label ( 'sdk.namespace' , 'functions' )
2022-07-17 10:30:58 +00:00
-> label ( 'sdk.method' , 'getFunctionUsage' )
2021-08-26 19:12:36 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
2024-01-09 03:10:43 +00:00
-> label ( 'sdk.response.model' , Response :: MODEL_USAGE_FUNCTION )
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2023-11-08 09:09:32 +00:00
-> param ( 'range' , '30d' , new WhiteList ([ '24h' , '30d' , '90d' ]), 'Date range.' , true )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-05-24 14:28:27 +00:00
-> action ( function ( string $functionId , string $range , Response $response , Database $dbForProject ) {
2020-10-30 19:53:27 +00:00
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-10-30 19:53:27 +00:00
2021-06-20 13:59:36 +00:00
if ( $function -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-10-30 19:53:27 +00:00
}
2022-04-13 12:39:31 +00:00
2023-10-25 07:39:59 +00:00
$periods = Config :: getParam ( 'usage' , []);
$stats = $usage = [];
$days = $periods [ $range ];
$metrics = [
str_replace ([ '{resourceType}' , '{resourceInternalId}' ], [ 'functions' , $function -> getInternalId ()], METRIC_FUNCTION_ID_DEPLOYMENTS ),
str_replace ([ '{resourceType}' , '{resourceInternalId}' ], [ 'functions' , $function -> getInternalId ()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE ),
str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_BUILDS ),
str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_BUILDS_STORAGE ),
str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_BUILDS_COMPUTE ),
str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_EXECUTIONS ),
str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE ),
2024-07-30 08:53:28 +00:00
str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_BUILDS_MB_SECONDS ),
str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS )
2023-10-25 07:39:59 +00:00
];
2023-11-08 09:09:32 +00:00
Authorization :: skip ( function () use ( $dbForProject , $days , $metrics , & $stats ) {
foreach ( $metrics as $metric ) {
2024-02-01 10:21:50 +00:00
$result = $dbForProject -> findOne ( 'stats' , [
2023-11-01 18:44:06 +00:00
Query :: equal ( 'metric' , [ $metric ]),
Query :: equal ( 'period' , [ 'inf' ])
]);
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'total' ] = $result [ 'value' ] ? ? 0 ;
2023-10-25 07:39:59 +00:00
$limit = $days [ 'limit' ];
$period = $days [ 'period' ];
2024-02-01 10:21:50 +00:00
$results = $dbForProject -> find ( 'stats' , [
2023-10-25 07:39:59 +00:00
Query :: equal ( 'metric' , [ $metric ]),
2023-10-25 12:06:54 +00:00
Query :: equal ( 'period' , [ $period ]),
2023-10-25 07:39:59 +00:00
Query :: limit ( $limit ),
Query :: orderDesc ( 'time' ),
]);
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'data' ] = [];
2023-10-25 07:39:59 +00:00
foreach ( $results as $result ) {
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'data' ][ $result -> getAttribute ( 'time' )] = [
'value' => $result -> getAttribute ( 'value' ),
2023-10-25 07:39:59 +00:00
];
}
}
});
2023-06-11 10:29:04 +00:00
2023-10-25 07:39:59 +00:00
$format = match ( $days [ 'period' ]) {
'1h' => 'Y-m-d\TH:00:00.000P' ,
'1d' => 'Y-m-d\T00:00:00.000P' ,
};
2024-03-06 17:34:21 +00:00
foreach ( $metrics as $metric ) {
$usage [ $metric ][ 'total' ] = $stats [ $metric ][ 'total' ];
$usage [ $metric ][ 'data' ] = [];
$leap = time () - ( $days [ 'limit' ] * $days [ 'factor' ]);
while ( $leap < time ()) {
$leap += $days [ 'factor' ];
$formatDate = date ( $format , $leap );
$usage [ $metric ][ 'data' ][] = [
'value' => $stats [ $metric ][ 'data' ][ $formatDate ][ 'value' ] ? ? 0 ,
'date' => $formatDate ,
];
}
2023-06-11 10:29:04 +00:00
}
2021-08-26 19:12:36 +00:00
2023-10-25 07:39:59 +00:00
$response -> dynamic ( new Document ([
'range' => $range ,
2023-11-08 09:09:32 +00:00
'deploymentsTotal' => $usage [ $metrics [ 0 ]][ 'total' ],
'deploymentsStorageTotal' => $usage [ $metrics [ 1 ]][ 'total' ],
'buildsTotal' => $usage [ $metrics [ 2 ]][ 'total' ],
'buildsStorageTotal' => $usage [ $metrics [ 3 ]][ 'total' ],
'buildsTimeTotal' => $usage [ $metrics [ 4 ]][ 'total' ],
'executionsTotal' => $usage [ $metrics [ 5 ]][ 'total' ],
'executionsTimeTotal' => $usage [ $metrics [ 6 ]][ 'total' ],
'deployments' => $usage [ $metrics [ 0 ]][ 'data' ],
'deploymentsStorage' => $usage [ $metrics [ 1 ]][ 'data' ],
'builds' => $usage [ $metrics [ 2 ]][ 'data' ],
'buildsStorage' => $usage [ $metrics [ 3 ]][ 'data' ],
'buildsTime' => $usage [ $metrics [ 4 ]][ 'data' ],
'executions' => $usage [ $metrics [ 5 ]][ 'data' ],
'executionsTime' => $usage [ $metrics [ 6 ]][ 'data' ],
2024-07-30 08:53:28 +00:00
'buildsMbSecondsTotal' => $usage [ $metrics [ 7 ]][ 'total' ],
'buildsMbSeconds' => $usage [ $metrics [ 7 ]][ 'data' ],
'executionsMbSeconds' => $usage [ $metrics [ 8 ]][ 'data' ],
'executionsMbSecondsTotal' => $usage [ $metrics [ 8 ]][ 'total' ]
2023-10-25 07:39:59 +00:00
]), Response :: MODEL_USAGE_FUNCTION );
2022-07-17 10:30:58 +00:00
});
App :: get ( '/v1/functions/usage' )
2023-10-03 16:50:48 +00:00
-> desc ( 'Get functions usage' )
2024-10-24 13:21:40 +00:00
-> groups ([ 'api' , 'functions' , 'usage' ])
2022-07-17 10:30:58 +00:00
-> label ( 'scope' , 'functions.read' )
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_ADMIN ])
-> 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 )
2023-11-08 09:09:32 +00:00
-> param ( 'range' , '30d' , new WhiteList ([ '24h' , '30d' , '90d' ]), 'Date range.' , true )
2022-07-17 10:30:58 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2022-07-17 10:39:44 +00:00
-> action ( function ( string $range , Response $response , Database $dbForProject ) {
2022-07-17 10:30:58 +00:00
2023-10-25 07:39:59 +00:00
$periods = Config :: getParam ( 'usage' , []);
$stats = $usage = [];
$days = $periods [ $range ];
$metrics = [
METRIC_FUNCTIONS ,
METRIC_DEPLOYMENTS ,
METRIC_DEPLOYMENTS_STORAGE ,
METRIC_BUILDS ,
METRIC_BUILDS_STORAGE ,
METRIC_BUILDS_COMPUTE ,
METRIC_EXECUTIONS ,
METRIC_EXECUTIONS_COMPUTE ,
2024-07-30 08:53:28 +00:00
METRIC_BUILDS_MB_SECONDS ,
METRIC_EXECUTIONS_MB_SECONDS ,
2023-10-25 07:39:59 +00:00
];
2023-11-08 09:09:32 +00:00
Authorization :: skip ( function () use ( $dbForProject , $days , $metrics , & $stats ) {
foreach ( $metrics as $metric ) {
2024-02-01 10:21:50 +00:00
$result = $dbForProject -> findOne ( 'stats' , [
2023-11-01 18:44:06 +00:00
Query :: equal ( 'metric' , [ $metric ]),
Query :: equal ( 'period' , [ 'inf' ])
]);
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'total' ] = $result [ 'value' ] ? ? 0 ;
2023-10-25 07:39:59 +00:00
$limit = $days [ 'limit' ];
$period = $days [ 'period' ];
2024-02-01 10:21:50 +00:00
$results = $dbForProject -> find ( 'stats' , [
2023-10-25 07:39:59 +00:00
Query :: equal ( 'metric' , [ $metric ]),
2023-10-25 12:06:54 +00:00
Query :: equal ( 'period' , [ $period ]),
2023-10-25 07:39:59 +00:00
Query :: limit ( $limit ),
Query :: orderDesc ( 'time' ),
]);
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'data' ] = [];
2023-10-25 07:39:59 +00:00
foreach ( $results as $result ) {
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'data' ][ $result -> getAttribute ( 'time' )] = [
'value' => $result -> getAttribute ( 'value' ),
2023-10-25 07:39:59 +00:00
];
}
}
});
2023-06-11 10:29:04 +00:00
2023-10-25 07:39:59 +00:00
$format = match ( $days [ 'period' ]) {
'1h' => 'Y-m-d\TH:00:00.000P' ,
'1d' => 'Y-m-d\T00:00:00.000P' ,
};
2024-03-06 17:34:21 +00:00
foreach ( $metrics as $metric ) {
$usage [ $metric ][ 'total' ] = $stats [ $metric ][ 'total' ];
$usage [ $metric ][ 'data' ] = [];
$leap = time () - ( $days [ 'limit' ] * $days [ 'factor' ]);
while ( $leap < time ()) {
$leap += $days [ 'factor' ];
$formatDate = date ( $format , $leap );
$usage [ $metric ][ 'data' ][] = [
'value' => $stats [ $metric ][ 'data' ][ $formatDate ][ 'value' ] ? ? 0 ,
'date' => $formatDate ,
];
}
2023-06-11 10:29:04 +00:00
}
2023-10-25 07:39:59 +00:00
$response -> dynamic ( new Document ([
'range' => $range ,
2023-11-08 09:09:32 +00:00
'functionsTotal' => $usage [ $metrics [ 0 ]][ 'total' ],
'deploymentsTotal' => $usage [ $metrics [ 1 ]][ 'total' ],
'deploymentsStorageTotal' => $usage [ $metrics [ 2 ]][ 'total' ],
'buildsTotal' => $usage [ $metrics [ 3 ]][ 'total' ],
'buildsStorageTotal' => $usage [ $metrics [ 4 ]][ 'total' ],
'buildsTimeTotal' => $usage [ $metrics [ 5 ]][ 'total' ],
'executionsTotal' => $usage [ $metrics [ 6 ]][ 'total' ],
'executionsTimeTotal' => $usage [ $metrics [ 7 ]][ 'total' ],
'functions' => $usage [ $metrics [ 0 ]][ 'data' ],
'deployments' => $usage [ $metrics [ 1 ]][ 'data' ],
'deploymentsStorage' => $usage [ $metrics [ 2 ]][ 'data' ],
'builds' => $usage [ $metrics [ 3 ]][ 'data' ],
'buildsStorage' => $usage [ $metrics [ 4 ]][ 'data' ],
'buildsTime' => $usage [ $metrics [ 5 ]][ 'data' ],
'executions' => $usage [ $metrics [ 6 ]][ 'data' ],
'executionsTime' => $usage [ $metrics [ 7 ]][ 'data' ],
2024-07-30 08:53:28 +00:00
'buildsMbSecondsTotal' => $usage [ $metrics [ 8 ]][ 'total' ],
'buildsMbSeconds' => $usage [ $metrics [ 8 ]][ 'data' ],
'executionsMbSeconds' => $usage [ $metrics [ 9 ]][ 'data' ],
'executionsMbSecondsTotal' => $usage [ $metrics [ 9 ]][ 'total' ],
2023-10-25 07:39:59 +00:00
]), Response :: MODEL_USAGE_FUNCTIONS );
2020-12-26 15:20:08 +00:00
});
2020-10-30 19:53:27 +00:00
2024-10-22 11:00:10 +00:00
// App::put('/v1/functions/:functionId')
// ->groups(['api', 'functions'])
// ->desc('Update function')
// ->label('scope', 'functions.write')
// ->label('event', 'functions.[functionId].update')
// ->label('audits.event', 'function.update')
// ->label('audits.resource', 'function/{response.$id}')
// ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
// ->label('sdk.namespace', 'functions')
// ->label('sdk.method', 'update')
// ->label('sdk.description', '/docs/references/functions/update-function.md')
// ->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('name', '', new Text(128), 'Function name. Max length: 128 chars.')
// ->param('runtime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution runtime.', true)
// ->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of role strings with execution permissions. By default no user is granted with any execute permissions. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true)
// ->param('events', [], new ArrayList(new FunctionEvent(), 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) System::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Maximum execution time in seconds.', true)
// ->param('enabled', true, new Boolean(), 'Is function enabled? When set to \'disabled\', users cannot access the function but Server SDKs with and API key can still access the function. No data is lost when this is toggled.', true)
// ->param('logging', true, new Boolean(), 'Whether executions will be logged. When set to false, executions will not be logged, but will reduce resource used by your Appwrite project.', true)
// ->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File. This path is relative to the "providerRootDirectory".', true)
// ->param('commands', '', new Text(8192, 0), 'Build Commands.', true)
// ->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for API Key auto-generated for every execution. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.', true)
// ->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for VCS (Version Controle System) deployment.', true)
// ->param('providerRepositoryId', null, new Nullable(new Text(128, 0)), 'Repository ID of the repo linked to the function', true)
// ->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function', true)
// ->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the function? In silent mode, comments will not be made on commits and pull requests.', true)
// ->param('providerRootDirectory', '', new Text(128, 0), 'Path to function code in the linked repo.', true)
// ->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
// $plan,
// Config::getParam('runtime-specifications', []),
// App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT),
// App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT)
// ), 'Runtime specification for the function and builds.', true, ['plan'])
// ->inject('request')
// ->inject('response')
// ->inject('dbForProject')
// ->inject('project')
// ->inject('queueForEvents')
// ->inject('queueForBuilds')
// ->inject('dbForConsole')
// ->inject('gitHub')
// ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, ?string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
// // TODO: If only branch changes, re-deploy
// $function = $dbForProject->getDocument('functions', $functionId);
// if ($function->isEmpty()) {
// throw new Exception(Exception::FUNCTION_NOT_FOUND);
// }
// $installation = $dbForConsole->getDocument('installations', $installationId);
// if (!empty($installationId) && $installation->isEmpty()) {
// throw new Exception(Exception::INSTALLATION_NOT_FOUND);
// }
// if (!empty($providerRepositoryId) && (empty($installationId) || empty($providerBranch))) {
// throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'When connecting to VCS (Version Control System), you need to provide "installationId" and "providerBranch".');
// }
// if ($function->isEmpty()) {
// throw new Exception(Exception::FUNCTION_NOT_FOUND);
// }
// if (empty($runtime)) {
// $runtime = $function->getAttribute('runtime');
// }
// $enabled ??= $function->getAttribute('enabled', true);
// $repositoryId = $function->getAttribute('repositoryId', '');
// $repositoryInternalId = $function->getAttribute('repositoryInternalId', '');
// if (empty($entrypoint)) {
// $entrypoint = $function->getAttribute('entrypoint', '');
// }
// $isConnected = !empty($function->getAttribute('providerRepositoryId', ''));
// // Git disconnect logic. Disconnecting only when providerRepositoryId is empty, allowing for continue updates without disconnecting git
// if ($isConnected && ($providerRepositoryId !== null && empty($providerRepositoryId))) {
// $repositories = $dbForConsole->find('repositories', [
// Query::equal('projectInternalId', [$project->getInternalId()]),
// Query::equal('resourceInternalId', [$function->getInternalId()]),
// Query::equal('resourceType', ['function']),
// Query::limit(100),
// ]);
// foreach ($repositories as $repository) {
// $dbForConsole->deleteDocument('repositories', $repository->getId());
// }
// $providerRepositoryId = '';
// $installationId = '';
// $providerBranch = '';
// $providerRootDirectory = '';
// $providerSilentMode = true;
// $repositoryId = '';
// $repositoryInternalId = '';
// }
// // Git connect logic
// if (!$isConnected && !empty($providerRepositoryId)) {
// $teamId = $project->getAttribute('teamId', '');
// $repository = $dbForConsole->createDocument('repositories', new Document([
// '$id' => ID::unique(),
// '$permissions' => [
// Permission::read(Role::team(ID::custom($teamId))),
// Permission::update(Role::team(ID::custom($teamId), 'owner')),
// Permission::update(Role::team(ID::custom($teamId), 'developer')),
// Permission::delete(Role::team(ID::custom($teamId), 'owner')),
// Permission::delete(Role::team(ID::custom($teamId), 'developer')),
// ],
// 'installationId' => $installation->getId(),
// 'installationInternalId' => $installation->getInternalId(),
// 'projectId' => $project->getId(),
// 'projectInternalId' => $project->getInternalId(),
// 'providerRepositoryId' => $providerRepositoryId,
// 'resourceId' => $function->getId(),
// 'resourceInternalId' => $function->getInternalId(),
// 'resourceType' => 'function',
// 'providerPullRequestIds' => []
// ]));
// $repositoryId = $repository->getId();
// $repositoryInternalId = $repository->getInternalId();
// }
// $live = true;
// if (
// $function->getAttribute('name') !== $name ||
// $function->getAttribute('entrypoint') !== $entrypoint ||
// $function->getAttribute('commands') !== $commands ||
// $function->getAttribute('providerRootDirectory') !== $providerRootDirectory ||
// $function->getAttribute('runtime') !== $runtime
// ) {
// $live = false;
// }
// $spec = Config::getParam('runtime-specifications')[$specification] ?? [];
// // Enforce Cold Start if spec limits change.
// if ($function->getAttribute('specification') !== $specification && !empty($function->getAttribute('deployment'))) {
// $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
// try {
// $executor->deleteRuntime($project->getId(), $function->getAttribute('deployment'));
// } catch (\Throwable $th) {
// // Don't throw if the deployment doesn't exist
// if ($th->getCode() !== 404) {
// throw $th;
// }
// }
// }
// $function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
// 'execute' => $execute,
// 'name' => $name,
// 'runtime' => $runtime,
// 'events' => $events,
// 'schedule' => $schedule,
// 'timeout' => $timeout,
// 'enabled' => $enabled,
// 'live' => $live,
// 'logging' => $logging,
// 'entrypoint' => $entrypoint,
// 'commands' => $commands,
// 'scopes' => $scopes,
// 'installationId' => $installation->getId(),
// 'installationInternalId' => $installation->getInternalId(),
// 'providerRepositoryId' => $providerRepositoryId,
// 'repositoryId' => $repositoryId,
// 'repositoryInternalId' => $repositoryInternalId,
// 'providerBranch' => $providerBranch,
// 'providerRootDirectory' => $providerRootDirectory,
// 'providerSilentMode' => $providerSilentMode,
// 'specification' => $specification,
// 'search' => implode(' ', [$functionId, $name, $runtime]),
// ])));
// // Redeploy logic
// if (!$isConnected && !empty($providerRepositoryId)) {
// $redeployVcs($request, $function, $project, $installation, $dbForProject, $queueForBuilds, new Document(), $github);
// }
// // Inform scheduler if function is still active
// $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId'));
// $schedule
// ->setAttribute('resourceUpdatedAt', DateTime::now())
// ->setAttribute('schedule', $function->getAttribute('schedule'))
// ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
// Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
// $queueForEvents->setParam('functionId', $function->getId());
// $response->dynamic($function, Response::MODEL_FUNCTION);
// });
2020-07-12 21:18:52 +00:00
2023-08-21 10:21:18 +00:00
App :: get ( '/v1/functions/:functionId/deployments/:deploymentId/download' )
-> groups ([ 'api' , 'functions' ])
2024-07-01 19:35:55 +00:00
-> desc ( 'Download deployment' )
2023-08-21 10:21:18 +00:00
-> label ( 'scope' , 'functions.read' )
2024-08-27 11:56:15 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
2023-08-21 10:21:18 +00:00
-> label ( 'sdk.namespace' , 'functions' )
2024-08-08 07:07:27 +00:00
-> label ( 'sdk.method' , 'getDeploymentDownload' )
-> label ( 'sdk.description' , '/docs/references/functions/get-deployment-download.md' )
2023-08-21 10:21:18 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , '*/*' )
-> label ( 'sdk.methodType' , 'location' )
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
-> param ( 'deploymentId' , '' , new UID (), 'Deployment ID.' )
-> inject ( 'response' )
-> inject ( 'request' )
-> inject ( 'dbForProject' )
2024-02-20 14:10:51 +00:00
-> inject ( 'deviceForFunctions' )
-> action ( function ( string $functionId , string $deploymentId , Response $response , Request $request , Database $dbForProject , Device $deviceForFunctions ) {
2023-08-21 10:21:18 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
}
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
if ( $deployment -> isEmpty ()) {
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
}
if ( $deployment -> getAttribute ( 'resourceId' ) !== $function -> getId ()) {
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
}
$path = $deployment -> getAttribute ( 'path' , '' );
2024-02-20 14:10:51 +00:00
if ( ! $deviceForFunctions -> exists ( $path )) {
2023-08-21 10:21:18 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
}
$response
-> setContentType ( 'application/gzip' )
-> addHeader ( 'Expires' , \date ( 'D, d M Y H:i:s' , \time () + ( 60 * 60 * 24 * 45 )) . ' GMT' ) // 45 days cache
-> addHeader ( 'X-Peak' , \memory_get_peak_usage ())
2023-10-27 14:08:33 +00:00
-> addHeader ( 'Content-Disposition' , 'attachment; filename="' . $deploymentId . '.tar.gz"' );
2023-08-21 10:21:18 +00:00
2024-02-20 14:10:51 +00:00
$size = $deviceForFunctions -> getFileSize ( $path );
2023-08-21 10:21:18 +00:00
$rangeHeader = $request -> getHeader ( 'range' );
if ( ! empty ( $rangeHeader )) {
$start = $request -> getRangeStart ();
$end = $request -> getRangeEnd ();
$unit = $request -> getRangeUnit ();
if ( $end === null ) {
$end = min (( $start + MAX_OUTPUT_CHUNK_SIZE - 1 ), ( $size - 1 ));
}
if ( $unit !== 'bytes' || $start >= $end || $end >= $size ) {
throw new Exception ( Exception :: STORAGE_INVALID_RANGE );
}
$response
-> addHeader ( 'Accept-Ranges' , 'bytes' )
-> addHeader ( 'Content-Range' , 'bytes ' . $start . '-' . $end . '/' . $size )
-> addHeader ( 'Content-Length' , $end - $start + 1 )
-> setStatusCode ( Response :: STATUS_CODE_PARTIALCONTENT );
2024-02-20 14:10:51 +00:00
$response -> send ( $deviceForFunctions -> read ( $path , $start , ( $end - $start + 1 )));
2023-08-21 10:21:18 +00:00
}
if ( $size > APP_STORAGE_READ_BUFFER ) {
for ( $i = 0 ; $i < ceil ( $size / MAX_OUTPUT_CHUNK_SIZE ); $i ++ ) {
$response -> chunk (
2024-02-20 14:10:51 +00:00
$deviceForFunctions -> read (
2023-08-21 10:21:18 +00:00
$path ,
( $i * MAX_OUTPUT_CHUNK_SIZE ),
min ( MAX_OUTPUT_CHUNK_SIZE , $size - ( $i * MAX_OUTPUT_CHUNK_SIZE ))
),
(( $i + 1 ) * MAX_OUTPUT_CHUNK_SIZE ) >= $size
);
}
} else {
2024-02-20 14:10:51 +00:00
$response -> send ( $deviceForFunctions -> read ( $path ));
2023-08-21 10:21:18 +00:00
}
});
2022-02-15 09:16:32 +00:00
App :: patch ( '/v1/functions/:functionId/deployments/:deploymentId' )
2020-07-12 21:18:52 +00:00
-> groups ([ 'api' , 'functions' ])
2024-07-01 19:35:55 +00:00
-> desc ( 'Update deployment' )
2020-05-06 17:35:56 +00:00
-> label ( 'scope' , 'functions.write' )
2022-04-13 12:39:31 +00:00
-> label ( 'event' , 'functions.[functionId].deployments.[deploymentId].update' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'deployment.update' )
2022-08-13 07:34:03 +00:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2021-04-16 07:22:17 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-06 17:35:56 +00:00
-> label ( 'sdk.namespace' , 'functions' )
2022-01-24 22:59:02 +00:00
-> label ( 'sdk.method' , 'updateDeployment' )
-> label ( 'sdk.description' , '/docs/references/functions/update-function-deployment.md' )
2020-11-11 21:02:24 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_FUNCTION )
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2022-02-15 09:16:32 +00:00
-> param ( 'deploymentId' , '' , new UID (), 'Deployment ID.' )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
2022-11-06 21:41:33 +00:00
-> inject ( 'dbForConsole' )
2023-10-02 14:02:48 +00:00
-> action ( function ( string $functionId , string $deploymentId , Response $response , Database $dbForProject , Event $queueForEvents , Database $dbForConsole ) {
2021-01-16 23:38:13 +00:00
2022-01-06 09:45:56 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2022-02-15 09:16:32 +00:00
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
2022-01-26 23:19:02 +00:00
$build = $dbForProject -> getDocument ( 'builds' , $deployment -> getAttribute ( 'buildId' , '' ));
2021-09-06 00:37:20 +00:00
2021-12-08 15:08:53 +00:00
if ( $function -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2021-12-08 15:08:53 +00:00
}
2021-01-16 23:38:13 +00:00
2022-01-24 22:59:02 +00:00
if ( $deployment -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
2021-12-08 15:08:53 +00:00
}
2021-09-06 00:37:20 +00:00
2021-12-08 15:08:53 +00:00
if ( $build -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: BUILD_NOT_FOUND );
2020-05-06 17:35:56 +00:00
}
2021-12-08 15:08:53 +00:00
if ( $build -> getAttribute ( 'status' ) !== 'ready' ) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: BUILD_NOT_READY );
2021-09-06 00:37:20 +00:00
}
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> updateDocument ( 'functions' , $function -> getId (), new Document ( array_merge ( $function -> getArrayCopy (), [
2023-06-11 10:29:04 +00:00
'deploymentInternalId' => $deployment -> getInternalId (),
'deployment' => $deployment -> getId (),
2021-12-08 15:08:53 +00:00
])));
2023-07-28 07:56:07 +00:00
// Inform scheduler if function is still active
2022-11-16 10:33:11 +00:00
$schedule = $dbForConsole -> getDocument ( 'schedules' , $function -> getAttribute ( 'scheduleId' ));
2023-06-11 10:29:04 +00:00
$schedule
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
-> setAttribute ( 'schedule' , $function -> getAttribute ( 'schedule' ))
-> setAttribute ( 'active' , ! empty ( $function -> getAttribute ( 'schedule' )) && ! empty ( $function -> getAttribute ( 'deployment' )));
2022-11-15 14:33:43 +00:00
Authorization :: skip ( fn () => $dbForConsole -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
2022-11-06 21:41:33 +00:00
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-04-13 12:39:31 +00:00
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'deploymentId' , $deployment -> getId ());
2021-12-08 15:08:53 +00:00
$response -> dynamic ( $function , Response :: MODEL_FUNCTION );
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
App :: delete ( '/v1/functions/:functionId' )
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'Delete function' )
2020-05-05 17:30:12 +00:00
-> label ( 'scope' , 'functions.write' )
2022-04-13 12:39:31 +00:00
-> label ( 'event' , 'functions.[functionId].delete' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'function.delete' )
2022-08-09 14:38:54 +00:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2021-04-16 07:22:17 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-05 17:30:12 +00:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'delete' )
-> label ( 'sdk.description' , '/docs/references/functions/delete-function.md' )
2020-11-11 21:02:24 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForDeletes' )
-> inject ( 'queueForEvents' )
2022-11-04 05:12:08 +00:00
-> inject ( 'dbForConsole' )
2023-10-02 14:02:48 +00:00
-> action ( function ( string $functionId , Response $response , Database $dbForProject , Delete $queueForDeletes , Event $queueForEvents , Database $dbForConsole ) {
2020-10-30 19:53:27 +00:00
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-05-05 17:30:12 +00:00
2021-06-20 13:59:36 +00:00
if ( $function -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2020-05-06 12:12:52 +00:00
2021-12-27 12:45:23 +00:00
if ( ! $dbForProject -> deleteDocument ( 'functions' , $function -> getId ())) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'Failed to remove function from DB' );
2020-05-04 14:35:01 +00:00
}
2020-05-05 17:30:12 +00:00
2023-07-28 07:56:07 +00:00
// Inform scheduler to no longer run function
2022-11-16 10:33:11 +00:00
$schedule = $dbForConsole -> getDocument ( 'schedules' , $function -> getAttribute ( 'scheduleId' ));
2022-11-15 16:14:52 +00:00
$schedule
2022-11-06 21:41:33 +00:00
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
2023-06-11 10:29:04 +00:00
-> setAttribute ( 'active' , false );
2022-11-15 14:33:43 +00:00
Authorization :: skip ( fn () => $dbForConsole -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
2022-11-04 05:12:08 +00:00
2022-12-20 16:11:30 +00:00
$queueForDeletes
2022-04-17 20:34:32 +00:00
-> setType ( DELETE_TYPE_DOCUMENT )
-> setDocument ( $function );
2022-04-13 12:39:31 +00:00
2022-12-20 16:11:30 +00:00
$queueForEvents -> setParam ( 'functionId' , $function -> getId ());
2020-10-30 19:53:27 +00:00
2020-07-12 21:18:52 +00:00
$response -> noContent ();
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
2024-10-22 11:00:10 +00:00
// App::post('/v1/functions/:functionId/deployments')
// ->groups(['api', 'functions'])
// ->desc('Create deployment')
// ->label('scope', 'functions.write')
// ->label('event', 'functions.[functionId].deployments.[deploymentId].create')
// ->label('audits.event', 'deployment.create')
// ->label('audits.resource', 'function/{request.functionId}')
// ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
// ->label('sdk.namespace', 'functions')
// ->label('sdk.method', 'createDeployment')
// ->label('sdk.methodType', 'upload')
// ->label('sdk.description', '/docs/references/functions/create-deployment.md')
// ->label('sdk.packaging', true)
// ->label('sdk.request.type', 'multipart/form-data')
// ->label('sdk.response.code', Response::STATUS_CODE_ACCEPTED)
// ->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
// ->label('sdk.response.model', Response::MODEL_DEPLOYMENT)
// ->param('functionId', '', new UID(), 'Function ID.')
// ->param('entrypoint', null, new Text(1028), 'Entrypoint File.', true)
// ->param('commands', null, new Text(8192, 0), 'Build Commands.', true)
// ->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.', skipValidation: true)
// ->param('activate', false, new Boolean(true), 'Automatically activate the deployment when it is finished building.')
// ->inject('request')
// ->inject('response')
// ->inject('dbForProject')
// ->inject('queueForEvents')
// ->inject('project')
// ->inject('deviceForFunctions')
// ->inject('deviceForLocal')
// ->inject('queueForBuilds')
// ->action(function (string $functionId, ?string $entrypoint, ?string $commands, mixed $code, mixed $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceForFunctions, Device $deviceForLocal, Build $queueForBuilds) {
// $activate = \strval($activate) === 'true' || \strval($activate) === '1';
// $function = $dbForProject->getDocument('functions', $functionId);
// if ($function->isEmpty()) {
// throw new Exception(Exception::FUNCTION_NOT_FOUND);
// }
// if ($entrypoint === null) {
// $entrypoint = $function->getAttribute('entrypoint', '');
// }
// if ($commands === null) {
// $commands = $function->getAttribute('commands', '');
// }
// if (empty($entrypoint)) {
// throw new Exception(Exception::FUNCTION_ENTRYPOINT_MISSING);
// }
// $file = $request->getFiles('code');
// // GraphQL multipart spec adds files with index keys
// if (empty($file)) {
// $file = $request->getFiles(0);
// }
// if (empty($file)) {
// throw new Exception(Exception::STORAGE_FILE_EMPTY, 'No file sent');
// }
// $fileExt = new FileExt([FileExt::TYPE_GZIP]);
// $fileSizeValidator = new FileSize(System::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000'));
// $upload = new Upload();
// // Make sure we handle a single file and multiple files the same way
// $fileName = (\is_array($file['name']) && isset($file['name'][0])) ? $file['name'][0] : $file['name'];
// $fileTmpName = (\is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name'];
// $fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
// if (!$fileExt->isValid($file['name'])) { // Check if file type is allowed
// throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED);
// }
// $contentRange = $request->getHeader('content-range');
// $deploymentId = ID::unique();
// $chunk = 1;
// $chunks = 1;
// if (!empty($contentRange)) {
// $start = $request->getContentRangeStart();
// $end = $request->getContentRangeEnd();
// $fileSize = $request->getContentRangeSize();
// $deploymentId = $request->getHeader('x-appwrite-id', $deploymentId);
// // TODO make `end >= $fileSize` in next breaking version
// if (is_null($start) || is_null($end) || is_null($fileSize) || $end > $fileSize) {
// throw new Exception(Exception::STORAGE_INVALID_CONTENT_RANGE);
// }
// // TODO remove the condition that checks `$end === $fileSize` in next breaking version
// if ($end === $fileSize - 1 || $end === $fileSize) {
// //if it's a last chunks the chunk size might differ, so we set the $chunks and $chunk to notify it's last chunk
// $chunks = $chunk = -1;
// } else {
// // Calculate total number of chunks based on the chunk size i.e ($rangeEnd - $rangeStart)
// $chunks = (int) ceil($fileSize / ($end + 1 - $start));
// $chunk = (int) ($start / ($end + 1 - $start)) + 1;
// }
// }
// if (!$fileSizeValidator->isValid($fileSize)) { // Check if file size is exceeding allowed limit
// throw new Exception(Exception::STORAGE_INVALID_FILE_SIZE);
// }
// if (!$upload->isValid($fileTmpName)) {
// throw new Exception(Exception::STORAGE_INVALID_FILE);
// }
// // Save to storage
// $fileSize ??= $deviceForLocal->getFileSize($fileTmpName);
// $path = $deviceForFunctions->getPath($deploymentId . '.' . \pathinfo($fileName, PATHINFO_EXTENSION));
// $deployment = $dbForProject->getDocument('deployments', $deploymentId);
// $metadata = ['content_type' => $deviceForLocal->getFileMimeType($fileTmpName)];
// if (!$deployment->isEmpty()) {
// $chunks = $deployment->getAttribute('chunksTotal', 1);
// $metadata = $deployment->getAttribute('metadata', []);
// if ($chunk === -1) {
// $chunk = $chunks;
// }
// }
// $chunksUploaded = $deviceForFunctions->upload($fileTmpName, $path, $chunk, $chunks, $metadata);
// if (empty($chunksUploaded)) {
// throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed moving file');
// }
// $type = $request->getHeader('x-sdk-language') === 'cli' ? 'cli' : 'manual';
// if ($chunksUploaded === $chunks) {
// if ($activate) {
// // Remove deploy for all other deployments.
// $activeDeployments = $dbForProject->find('deployments', [
// Query::equal('activate', [true]),
// Query::equal('resourceId', [$functionId]),
// Query::equal('resourceType', ['functions'])
// ]);
// foreach ($activeDeployments as $activeDeployment) {
// $activeDeployment->setAttribute('activate', false);
// $dbForProject->updateDocument('deployments', $activeDeployment->getId(), $activeDeployment);
// }
// }
// $fileSize = $deviceForFunctions->getFileSize($path);
// if ($deployment->isEmpty()) {
// $deployment = $dbForProject->createDocument('deployments', new Document([
// '$id' => $deploymentId,
// '$permissions' => [
// Permission::read(Role::any()),
// Permission::update(Role::any()),
// Permission::delete(Role::any()),
// ],
// 'resourceInternalId' => $function->getInternalId(),
// 'resourceId' => $function->getId(),
// 'resourceType' => 'functions',
// 'buildInternalId' => '',
// 'entrypoint' => $entrypoint,
// 'commands' => $commands,
// 'path' => $path,
// 'size' => $fileSize,
// 'search' => implode(' ', [$deploymentId, $entrypoint]),
// 'activate' => $activate,
// 'metadata' => $metadata,
// 'type' => $type
// ]));
// } else {
// $deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('size', $fileSize)->setAttribute('metadata', $metadata));
// }
// // Start the build
// $queueForBuilds
// ->setType(BUILD_TYPE_DEPLOYMENT)
// ->setResource($function)
// ->setDeployment($deployment);
// } else {
// if ($deployment->isEmpty()) {
// $deployment = $dbForProject->createDocument('deployments', new Document([
// '$id' => $deploymentId,
// '$permissions' => [
// Permission::read(Role::any()),
// Permission::update(Role::any()),
// Permission::delete(Role::any()),
// ],
// 'resourceInternalId' => $function->getInternalId(),
// 'resourceId' => $function->getId(),
// 'resourceType' => 'functions',
// 'buildInternalId' => '',
// 'entrypoint' => $entrypoint,
// 'commands' => $commands,
// 'path' => $path,
// 'size' => $fileSize,
// 'chunksTotal' => $chunks,
// 'chunksUploaded' => $chunksUploaded,
// 'search' => implode(' ', [$deploymentId, $entrypoint]),
// 'activate' => $activate,
// 'metadata' => $metadata,
// 'type' => $type
// ]));
// } else {
// $deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('chunksUploaded', $chunksUploaded)->setAttribute('metadata', $metadata));
// }
// }
// $metadata = null;
// $queueForEvents
// ->setParam('functionId', $function->getId())
// ->setParam('deploymentId', $deployment->getId());
// $response
// ->setStatusCode(Response::STATUS_CODE_ACCEPTED)
// ->dynamic($deployment, Response::MODEL_DEPLOYMENT);
// });
2020-07-12 21:18:52 +00:00
2022-01-24 23:09:24 +00:00
App :: get ( '/v1/functions/:functionId/deployments' )
2020-07-12 21:18:52 +00:00
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'List deployments' )
2020-05-05 17:30:12 +00:00
-> label ( 'scope' , 'functions.read' )
2021-04-16 07:22:17 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-05 17:30:12 +00:00
-> label ( 'sdk.namespace' , 'functions' )
2022-01-24 23:09:24 +00:00
-> label ( 'sdk.method' , 'listDeployments' )
-> label ( 'sdk.description' , '/docs/references/functions/list-deployments.md' )
2020-11-11 21:02:24 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
2022-01-24 23:09:24 +00:00
-> label ( 'sdk.response.model' , Response :: MODEL_DEPLOYMENT_LIST )
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2023-03-29 19:38:39 +00:00
-> param ( 'queries' , [], new Deployments (), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). 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 ( ', ' , Deployments :: ALLOWED_ATTRIBUTES ), true )
2020-09-10 14:40:14 +00:00
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-08-23 09:12:48 +00:00
-> action ( function ( string $functionId , array $queries , string $search , Response $response , Database $dbForProject ) {
2021-01-16 23:38:13 +00:00
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-05-05 17:30:12 +00:00
2021-06-20 13:59:36 +00:00
if ( $function -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2021-05-27 10:09:14 +00:00
2024-02-12 16:02:04 +00:00
try {
$queries = Query :: parseQueries ( $queries );
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
}
2021-08-06 12:36:05 +00:00
2022-08-11 23:53:52 +00:00
if ( ! empty ( $search )) {
2022-08-23 09:12:48 +00:00
$queries [] = Query :: search ( 'search' , $search );
2021-08-06 12:36:05 +00:00
}
2022-08-23 09:12:48 +00:00
// Set resource queries
2024-04-29 10:24:22 +00:00
$queries [] = Query :: equal ( 'resourceInternalId' , [ $function -> getInternalId ()]);
2022-08-23 09:12:48 +00:00
$queries [] = Query :: equal ( 'resourceType' , [ 'functions' ]);
2021-08-18 13:42:03 +00:00
2024-02-12 09:55:45 +00:00
/**
2024-02-12 10:03:31 +00:00
* Get cursor document if there was a cursor query , we use array_filter and reset for reference $cursor to $queries
2024-02-12 09:55:45 +00:00
*/
2023-08-22 03:25:55 +00:00
$cursor = \array_filter ( $queries , function ( $query ) {
2023-12-06 14:10:40 +00:00
return \in_array ( $query -> getMethod (), [ Query :: TYPE_CURSOR_AFTER , Query :: TYPE_CURSOR_BEFORE ]);
2023-08-22 03:25:55 +00:00
});
2022-08-30 23:31:43 +00:00
$cursor = reset ( $cursor );
2022-08-30 11:55:23 +00:00
if ( $cursor ) {
2022-08-23 09:12:48 +00:00
/** @var Query $cursor */
2024-10-17 05:41:24 +00:00
$validator = new Cursor ();
if ( ! $validator -> isValid ( $cursor )) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $validator -> getDescription ());
}
2022-08-23 09:12:48 +00:00
$deploymentId = $cursor -> getValue ();
$cursorDocument = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
2021-08-18 13:42:03 +00:00
2022-08-11 23:53:52 +00:00
if ( $cursorDocument -> isEmpty ()) {
2022-08-23 09:12:48 +00:00
throw new Exception ( Exception :: GENERAL_CURSOR_NOT_FOUND , " Deployment ' { $deploymentId } ' for the 'cursor' value not found. " );
2022-08-11 23:53:52 +00:00
}
2021-08-18 13:42:03 +00:00
2022-08-23 09:12:48 +00:00
$cursor -> setValue ( $cursorDocument );
2021-08-18 13:42:03 +00:00
}
2022-08-23 09:12:48 +00:00
$filterQueries = Query :: groupByType ( $queries )[ 'filters' ];
2021-08-18 13:42:03 +00:00
2022-08-23 09:12:48 +00:00
$results = $dbForProject -> find ( 'deployments' , $queries );
2022-08-11 23:53:52 +00:00
$total = $dbForProject -> count ( 'deployments' , $filterQueries , APP_LIMIT_COUNT );
2020-07-12 21:18:52 +00:00
2022-01-31 11:29:31 +00:00
foreach ( $results as $result ) {
2022-02-16 11:43:21 +00:00
$build = $dbForProject -> getDocument ( 'builds' , $result -> getAttribute ( 'buildId' , '' ));
2022-02-16 16:00:45 +00:00
$result -> setAttribute ( 'status' , $build -> getAttribute ( 'status' , 'processing' ));
2023-08-05 14:50:28 +00:00
$result -> setAttribute ( 'buildLogs' , $build -> getAttribute ( 'logs' , '' ));
2022-10-10 15:24:40 +00:00
$result -> setAttribute ( 'buildTime' , $build -> getAttribute ( 'duration' , 0 ));
2024-08-19 11:06:58 +00:00
$result -> setAttribute ( 'buildSize' , $build -> getAttribute ( 'size' , 0 ));
$result -> setAttribute ( 'size' , $result -> getAttribute ( 'size' , 0 ));
2021-12-06 14:12:41 +00:00
}
2020-10-30 19:53:27 +00:00
$response -> dynamic ( new Document ([
2022-01-24 23:09:24 +00:00
'deployments' => $results ,
2022-02-27 09:57:09 +00:00
'total' => $total ,
2022-01-24 23:09:24 +00:00
]), Response :: MODEL_DEPLOYMENT_LIST );
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
2022-01-24 23:11:33 +00:00
App :: get ( '/v1/functions/:functionId/deployments/:deploymentId' )
2020-07-12 21:18:52 +00:00
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'Get deployment' )
2020-05-05 17:30:12 +00:00
-> label ( 'scope' , 'functions.read' )
2021-04-16 07:22:17 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-05 17:30:12 +00:00
-> label ( 'sdk.namespace' , 'functions' )
2022-01-24 23:11:33 +00:00
-> label ( 'sdk.method' , 'getDeployment' )
-> label ( 'sdk.description' , '/docs/references/functions/get-deployment.md' )
2020-11-11 21:02:24 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
2022-07-07 04:49:44 +00:00
-> label ( 'sdk.response.model' , Response :: MODEL_DEPLOYMENT )
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2022-01-24 23:11:33 +00:00
-> param ( 'deploymentId' , '' , new UID (), 'Deployment ID.' )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-05-24 14:28:27 +00:00
-> action ( function ( string $functionId , string $deploymentId , Response $response , Database $dbForProject ) {
2021-01-16 23:38:13 +00:00
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-05-05 17:30:12 +00:00
2021-06-20 13:59:36 +00:00
if ( $function -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2020-05-05 17:30:12 +00:00
2022-01-24 23:11:33 +00:00
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
2020-05-05 17:30:12 +00:00
2022-01-25 16:51:05 +00:00
if ( $deployment -> getAttribute ( 'resourceId' ) !== $function -> getId ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2020-05-05 17:30:12 +00:00
2022-01-24 23:11:33 +00:00
if ( $deployment -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
2020-05-05 17:30:12 +00:00
}
2022-10-31 05:11:55 +00:00
$build = $dbForProject -> getDocument ( 'builds' , $deployment -> getAttribute ( 'buildId' , '' ));
2023-05-28 11:39:48 +00:00
$deployment -> setAttribute ( 'status' , $build -> getAttribute ( 'status' , 'waiting' ));
2023-08-05 14:50:28 +00:00
$deployment -> setAttribute ( 'buildLogs' , $build -> getAttribute ( 'logs' , '' ));
2023-06-22 10:59:41 +00:00
$deployment -> setAttribute ( 'buildTime' , $build -> getAttribute ( 'duration' , 0 ));
2024-08-19 11:06:58 +00:00
$deployment -> setAttribute ( 'buildSize' , $build -> getAttribute ( 'size' , 0 ));
$deployment -> setAttribute ( 'size' , $deployment -> getAttribute ( 'size' , 0 ));
2022-10-31 05:11:55 +00:00
2022-01-24 23:11:33 +00:00
$response -> dynamic ( $deployment , Response :: MODEL_DEPLOYMENT );
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
2022-01-24 23:14:21 +00:00
App :: delete ( '/v1/functions/:functionId/deployments/:deploymentId' )
2020-07-12 21:18:52 +00:00
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'Delete deployment' )
2020-05-05 17:30:12 +00:00
-> label ( 'scope' , 'functions.write' )
2022-04-13 12:39:31 +00:00
-> label ( 'event' , 'functions.[functionId].deployments.[deploymentId].delete' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'deployment.delete' )
2022-08-13 07:34:03 +00:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2021-04-16 07:22:17 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-05 17:30:12 +00:00
-> label ( 'sdk.namespace' , 'functions' )
2022-01-24 23:14:21 +00:00
-> label ( 'sdk.method' , 'deleteDeployment' )
-> label ( 'sdk.description' , '/docs/references/functions/delete-deployment.md' )
2020-11-11 21:02:24 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2022-01-24 23:14:21 +00:00
-> param ( 'deploymentId' , '' , new UID (), 'Deployment ID.' )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForDeletes' )
-> inject ( 'queueForEvents' )
2024-02-20 14:10:51 +00:00
-> inject ( 'deviceForFunctions' )
-> action ( function ( string $functionId , string $deploymentId , Response $response , Database $dbForProject , Delete $queueForDeletes , Event $queueForEvents , Device $deviceForFunctions ) {
2021-01-16 23:38:13 +00:00
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2021-06-20 13:59:36 +00:00
if ( $function -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2022-04-13 12:39:31 +00:00
2022-01-24 23:14:21 +00:00
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
if ( $deployment -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
2021-09-13 10:50:45 +00:00
}
2022-01-31 23:44:55 +00:00
if ( $deployment -> getAttribute ( 'resourceId' ) !== $function -> getId ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
2021-09-13 10:50:45 +00:00
}
2023-06-14 08:57:30 +00:00
if ( ! $dbForProject -> deleteDocument ( 'deployments' , $deployment -> getId ())) {
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'Failed to remove deployment from DB' );
}
if ( ! empty ( $deployment -> getAttribute ( 'path' , '' ))) {
2024-02-20 14:10:51 +00:00
if ( ! ( $deviceForFunctions -> delete ( $deployment -> getAttribute ( 'path' , '' )))) {
2023-06-14 08:57:30 +00:00
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'Failed to remove deployment from storage' );
2020-07-18 21:48:28 +00:00
}
2020-05-05 17:30:12 +00:00
}
2022-04-13 12:39:31 +00:00
if ( $function -> getAttribute ( 'deployment' ) === $deployment -> getId ()) { // Reset function deployment
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> updateDocument ( 'functions' , $function -> getId (), new Document ( array_merge ( $function -> getArrayCopy (), [
2022-01-24 23:14:21 +00:00
'deployment' => '' ,
2023-06-11 14:08:48 +00:00
'deploymentInternalId' => '' ,
2021-05-04 21:25:17 +00:00
])));
2020-10-30 19:53:27 +00:00
}
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-04-13 12:39:31 +00:00
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'deploymentId' , $deployment -> getId ());
2020-07-18 21:48:28 +00:00
2022-12-20 16:11:30 +00:00
$queueForDeletes
2022-04-17 20:34:32 +00:00
-> setType ( DELETE_TYPE_DOCUMENT )
-> setDocument ( $deployment );
2022-01-28 01:29:20 +00:00
2020-07-12 21:18:52 +00:00
$response -> noContent ();
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
2024-05-29 12:18:52 +00:00
App :: post ( '/v1/functions/:functionId/deployments/:deploymentId/build' )
-> alias ( '/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId' )
2022-09-29 08:42:46 +00:00
-> groups ([ 'api' , 'functions' ])
2024-06-02 17:44:41 +00:00
-> desc ( 'Rebuild deployment' )
2022-09-29 08:42:46 +00:00
-> label ( 'scope' , 'functions.write' )
-> label ( 'event' , 'functions.[functionId].deployments.[deploymentId].update' )
-> label ( 'audits.event' , 'deployment.update' )
-> label ( 'audits.resource' , 'function/{request.functionId}' )
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'createBuild' )
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
-> param ( 'deploymentId' , '' , new UID (), 'Deployment ID.' )
2024-06-02 17:44:41 +00:00
-> param ( 'buildId' , '' , new UID (), 'Build unique ID.' , true ) // added as optional param for backward compatibility
2023-06-22 10:59:41 +00:00
-> inject ( 'request' )
2022-09-29 08:42:46 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
-> inject ( 'project' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
-> inject ( 'queueForBuilds' )
2024-07-25 08:36:11 +00:00
-> inject ( 'deviceForFunctions' )
-> action ( function ( string $functionId , string $deploymentId , string $buildId , Request $request , Response $response , Database $dbForProject , Document $project , Event $queueForEvents , Build $queueForBuilds , Device $deviceForFunctions ) {
2022-09-29 08:42:46 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
}
2023-08-19 06:15:47 +00:00
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
2022-09-29 08:42:46 +00:00
if ( $deployment -> isEmpty ()) {
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
}
2024-07-25 08:36:11 +00:00
$path = $deployment -> getAttribute ( 'path' );
2024-09-05 02:25:11 +00:00
if ( empty ( $path ) || ! $deviceForFunctions -> exists ( $path )) {
2024-07-25 08:36:11 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
}
2023-08-20 09:43:05 +00:00
$deploymentId = ID :: unique ();
2022-09-29 08:42:46 +00:00
2024-07-25 08:36:11 +00:00
$destination = $deviceForFunctions -> getPath ( $deploymentId . '.' . \pathinfo ( 'code.tar.gz' , PATHINFO_EXTENSION ));
$deviceForFunctions -> transfer ( $path , $destination , $deviceForFunctions );
2023-09-13 19:18:50 +00:00
$deployment -> removeAttribute ( '$internalId' );
2023-08-19 06:15:47 +00:00
$deployment = $dbForProject -> createDocument ( 'deployments' , $deployment -> setAttributes ([
2024-07-25 08:36:11 +00:00
'$internalId' => '' ,
2023-08-20 09:43:05 +00:00
'$id' => $deploymentId ,
2023-08-19 06:15:47 +00:00
'buildId' => '' ,
'buildInternalId' => '' ,
2024-07-25 08:36:11 +00:00
'path' => $destination ,
2023-08-19 06:15:47 +00:00
'entrypoint' => $function -> getAttribute ( 'entrypoint' ),
'commands' => $function -> getAttribute ( 'commands' , '' ),
2023-08-20 09:43:05 +00:00
'search' => implode ( ' ' , [ $deploymentId , $function -> getAttribute ( 'entrypoint' )]),
2023-08-19 06:15:47 +00:00
]));
2022-09-29 08:42:46 +00:00
2022-12-20 16:11:30 +00:00
$queueForBuilds
2023-08-19 06:15:47 +00:00
-> setType ( BUILD_TYPE_DEPLOYMENT )
2022-09-29 08:42:46 +00:00
-> setResource ( $function )
2024-02-20 11:40:55 +00:00
-> setDeployment ( $deployment );
2022-09-29 08:42:46 +00:00
2023-09-27 15:51:17 +00:00
$queueForEvents
2023-08-17 21:37:52 +00:00
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'deploymentId' , $deployment -> getId ());
2022-09-29 08:42:46 +00:00
$response -> noContent ();
});
2024-05-27 12:51:28 +00:00
App :: patch ( '/v1/functions/:functionId/deployments/:deploymentId/build' )
2024-02-18 12:31:25 +00:00
-> groups ([ 'api' , 'functions' ])
2024-05-30 20:23:29 +00:00
-> desc ( 'Cancel deployment' )
2024-02-18 12:31:25 +00:00
-> label ( 'scope' , 'functions.write' )
-> label ( 'audits.event' , 'deployment.update' )
-> label ( 'audits.resource' , 'function/{request.functionId}' )
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
-> label ( 'sdk.namespace' , 'functions' )
2024-05-30 20:23:29 +00:00
-> label ( 'sdk.method' , 'updateDeploymentBuild' )
2024-02-18 17:55:57 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_BUILD )
2024-02-18 12:31:25 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
-> param ( 'deploymentId' , '' , new UID (), 'Deployment ID.' )
-> inject ( 'response' )
-> inject ( 'dbForProject' )
-> inject ( 'project' )
-> inject ( 'queueForEvents' )
2024-03-04 20:30:06 +00:00
-> action ( function ( string $functionId , string $deploymentId , Response $response , Database $dbForProject , Document $project , Event $queueForEvents ) {
2024-02-18 12:31:25 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
}
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
if ( $deployment -> isEmpty ()) {
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
}
2024-03-04 20:30:06 +00:00
$build = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'builds' , $deployment -> getAttribute ( 'buildId' , '' )));
2024-02-18 12:31:25 +00:00
if ( $build -> isEmpty ()) {
2024-05-30 20:23:29 +00:00
$buildId = ID :: unique ();
$build = $dbForProject -> createDocument ( 'builds' , new Document ([
'$id' => $buildId ,
'$permissions' => [],
'startTime' => DateTime :: now (),
'deploymentInternalId' => $deployment -> getInternalId (),
'deploymentId' => $deployment -> getId (),
2024-06-12 10:21:48 +00:00
'status' => 'canceled' ,
2024-05-30 20:23:29 +00:00
'path' => '' ,
'runtime' => $function -> getAttribute ( 'runtime' ),
'source' => $deployment -> getAttribute ( 'path' , '' ),
'sourceType' => '' ,
'logs' => '' ,
'duration' => 0 ,
'size' => 0
]));
2024-02-18 12:31:25 +00:00
2024-05-30 20:23:29 +00:00
$deployment -> setAttribute ( 'buildId' , $build -> getId ());
$deployment -> setAttribute ( 'buildInternalId' , $build -> getInternalId ());
$deployment = $dbForProject -> updateDocument ( 'deployments' , $deployment -> getId (), $deployment );
} else {
if ( \in_array ( $build -> getAttribute ( 'status' ), [ 'ready' , 'failed' ])) {
throw new Exception ( Exception :: BUILD_ALREADY_COMPLETED );
}
2024-06-11 14:50:50 +00:00
2024-06-11 14:48:55 +00:00
$startTime = new \DateTime ( $build -> getAttribute ( 'startTime' ));
$endTime = new \DateTime ( 'now' );
$duration = $endTime -> getTimestamp () - $startTime -> getTimestamp ();
2024-06-07 11:23:33 +00:00
$build = $dbForProject -> updateDocument ( 'builds' , $build -> getId (), $build -> setAttributes ([
'endTime' => DateTime :: now (),
'duration' => $duration ,
2024-06-12 10:21:48 +00:00
'status' => 'canceled'
2024-06-07 11:23:33 +00:00
]));
2024-04-30 10:16:58 +00:00
}
2024-08-22 10:51:08 +00:00
$dbForProject -> purgeCachedDocument ( 'deployments' , $deployment -> getId ());
try {
$executor = new Executor ( App :: getEnv ( '_APP_EXECUTOR_HOST' ));
$executor -> deleteRuntime ( $project -> getId (), $deploymentId . " -build " );
} catch ( \Throwable $th ) {
// Don't throw if the deployment doesn't exist
if ( $th -> getCode () !== 404 ) {
throw $th ;
}
}
2024-02-18 12:31:25 +00:00
$queueForEvents
-> setParam ( 'functionId' , $function -> getId ())
2023-08-17 21:37:52 +00:00
-> setParam ( 'deploymentId' , $deployment -> getId ());
2024-02-18 17:55:57 +00:00
$response -> dynamic ( $build , Response :: MODEL_BUILD );
2022-09-29 08:42:46 +00:00
});
2020-07-12 21:18:52 +00:00
App :: post ( '/v1/functions/:functionId/executions' )
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'Create execution' )
2020-12-30 07:26:01 +00:00
-> label ( 'scope' , 'execution.write' )
2022-04-13 12:39:31 +00:00
-> label ( 'event' , 'functions.[functionId].executions.[executionId].create' )
2021-04-16 07:22:17 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
2020-05-05 17:30:12 +00:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'createExecution' )
-> label ( 'sdk.description' , '/docs/references/functions/create-execution.md' )
2020-11-11 21:02:24 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_CREATED )
2024-08-14 18:16:46 +00:00
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_MULTIPART )
2020-11-11 21:02:24 +00:00
-> label ( 'sdk.response.model' , Response :: MODEL_EXECUTION )
2024-08-20 10:46:03 +00:00
-> label ( 'sdk.request.type' , Response :: CONTENT_TYPE_JSON )
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2024-10-08 01:17:34 +00:00
-> param ( 'body' , '' , new Text ( 10485760 , 0 ), 'HTTP body of execution. Default value is empty string.' , true )
2024-09-20 19:00:59 +00:00
-> param ( 'async' , false , new Boolean ( true ), 'Execute code in the background. Default value is false.' , true )
2023-09-06 08:16:01 +00:00
-> param ( 'path' , '/' , new Text ( 2048 ), 'HTTP path of execution. Path can include query params. Default value is /' , true )
2023-07-01 06:46:21 +00:00
-> param ( 'method' , 'POST' , new Whitelist ([ 'GET' , 'POST' , 'PUT' , 'PATCH' , 'DELETE' , 'OPTIONS' ], true ), 'HTTP method of execution. Default value is GET.' , true )
2024-08-15 12:12:15 +00:00
-> param ( 'headers' , [], new AnyOf ([ new Assoc (), new Text ( 65535 )], AnyOf :: TYPE_MIXED ), 'HTTP headers of execution. Defaults to empty.' , true )
2024-08-16 09:11:13 +00:00
-> param ( 'scheduledAt' , null , new DatetimeValidator ( true , DateTimeValidator :: PRECISION_MINUTES , 60 ), 'Scheduled execution time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.' , true )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2024-08-05 13:08:41 +00:00
-> inject ( 'request' )
2020-12-26 15:20:08 +00:00
-> inject ( 'project' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2024-06-07 19:05:29 +00:00
-> inject ( 'dbForConsole' )
2021-03-10 16:58:46 +00:00
-> inject ( 'user' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
2023-10-25 07:39:59 +00:00
-> inject ( 'queueForUsage' )
2022-11-16 10:33:11 +00:00
-> inject ( 'queueForFunctions' )
2023-06-22 10:59:41 +00:00
-> inject ( 'geodb' )
2024-09-25 09:01:12 +00:00
-> action ( function ( string $functionId , string $body , mixed $async , string $path , string $method , mixed $headers , ? string $scheduledAt , Response $response , Request $request , Document $project , Database $dbForProject , Database $dbForConsole , Document $user , Event $queueForEvents , Usage $queueForUsage , Func $queueForFunctions , Reader $geodb ) {
2024-09-25 08:59:51 +00:00
$async = \strval ( $async ) === 'true' || \strval ( $async ) === '1' ;
2024-06-07 19:05:29 +00:00
2024-09-05 02:25:11 +00:00
if ( ! $async && ! is_null ( $scheduledAt )) {
2024-06-13 08:24:51 +00:00
throw new Exception ( Exception :: GENERAL_BAD_REQUEST , 'Scheduled executions must run asynchronously. Set scheduledAt to a future date, or set async to true.' );
2024-06-07 19:05:29 +00:00
}
2020-07-16 21:50:37 +00:00
2024-08-05 13:08:41 +00:00
/**
* @ var array < string , mixed > $headers
*/
$assocParams = [ 'headers' ];
foreach ( $assocParams as $assocParam ) {
if ( ! empty ( 'headers' ) && ! is_array ( $$assocParam )) {
$$assocParam = \json_decode ( $$assocParam , true );
}
}
2024-08-06 17:31:09 +00:00
$booleanParams = [ 'async' ];
foreach ( $booleanParams as $booleamParam ) {
if ( ! empty ( $$booleamParam ) && ! is_bool ( $$booleamParam )) {
$$booleamParam = $$booleamParam === " true " ? true : false ;
}
}
2024-08-05 13:08:41 +00:00
// 'headers' validator
2024-08-06 08:19:28 +00:00
$validator = new Headers ();
2024-08-05 13:08:41 +00:00
if ( ! $validator -> isValid ( $headers )) {
throw new Exception ( $validator -> getDescription (), 400 );
}
2022-04-13 12:39:31 +00:00
$function = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'functions' , $functionId ));
2020-07-12 21:18:52 +00:00
2023-08-16 21:58:25 +00:00
$isAPIKey = Auth :: isAppUser ( Authorization :: getRoles ());
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: getRoles ());
2023-07-31 18:24:21 +00:00
2023-08-16 21:58:25 +00:00
if ( $function -> isEmpty () || ( ! $function -> getAttribute ( 'enabled' ) && ! $isAPIKey && ! $isPrivilegedUser )) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2020-07-16 21:50:37 +00:00
2023-09-04 17:53:25 +00:00
$version = $function -> getAttribute ( 'version' , 'v2' );
$runtimes = Config :: getParam ( $version === 'v2' ? 'runtimes-v2' : 'runtimes' , []);
2024-10-27 16:22:45 +00:00
$spec = Config :: getParam ( 'runtime-specifications' )[ $function -> getAttribute ( 'specification' , APP_COMPUTE_SPECIFICATION_DEFAULT )];
2022-02-05 19:49:57 +00:00
$runtime = ( isset ( $runtimes [ $function -> getAttribute ( 'runtime' , '' )])) ? $runtimes [ $function -> getAttribute ( 'runtime' , '' )] : null ;
2022-02-04 01:29:40 +00:00
if ( \is_null ( $runtime )) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_RUNTIME_UNSUPPORTED , 'Runtime "' . $function -> getAttribute ( 'runtime' , '' ) . '" is not supported' );
2022-02-04 01:29:40 +00:00
}
2022-04-13 12:39:31 +00:00
$deployment = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'deployments' , $function -> getAttribute ( 'deployment' , '' )));
2020-07-16 21:50:37 +00:00
2022-01-25 16:51:05 +00:00
if ( $deployment -> getAttribute ( 'resourceId' ) !== $function -> getId ()) {
2022-08-14 06:56:12 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND , 'Deployment not found. Create a deployment before trying to execute a function' );
2020-07-16 21:50:37 +00:00
}
2022-01-24 23:16:53 +00:00
if ( $deployment -> isEmpty ()) {
2022-08-14 06:56:12 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND , 'Deployment not found. Create a deployment before trying to execute a function' );
2020-07-16 21:50:37 +00:00
}
2020-12-29 23:00:44 +00:00
2022-02-03 00:05:03 +00:00
/** Check if build has completed */
2022-04-13 12:39:31 +00:00
$build = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'builds' , $deployment -> getAttribute ( 'buildId' , '' )));
2022-02-03 00:05:03 +00:00
if ( $build -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: BUILD_NOT_FOUND );
2022-02-03 00:05:03 +00:00
}
if ( $build -> getAttribute ( 'status' ) !== 'ready' ) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: BUILD_NOT_READY );
2022-02-03 00:05:03 +00:00
}
2022-01-18 06:38:10 +00:00
$validator = new Authorization ( 'execute' );
2020-12-29 23:00:44 +00:00
2021-05-04 21:25:17 +00:00
if ( ! $validator -> isValid ( $function -> getAttribute ( 'execute' ))) { // Check if user has write access to execute function
2022-07-26 14:56:59 +00:00
throw new Exception ( Exception :: USER_UNAUTHORIZED , $validator -> getDescription ());
2020-12-29 23:00:44 +00:00
}
2024-01-29 11:21:51 +00:00
$jwt = '' ; // initialize
2021-06-20 13:59:36 +00:00
if ( ! $user -> isEmpty ()) { // If userId exists, generate a JWT for function
2021-07-22 14:49:52 +00:00
$sessions = $user -> getAttribute ( 'sessions' , []);
$current = new Document ();
2021-03-10 16:58:46 +00:00
2022-04-13 12:39:31 +00:00
foreach ( $sessions as $session ) {
/** @var Utopia\Database\Document $session */
2021-07-22 14:49:52 +00:00
if ( $session -> getAttribute ( 'secret' ) == Auth :: hash ( Auth :: $secret )) { // If current session delete the cookies too
$current = $session ;
2021-03-10 16:58:46 +00:00
}
}
2022-04-13 12:39:31 +00:00
if ( ! $current -> isEmpty ()) {
2024-05-28 09:25:54 +00:00
$jwtExpiry = $function -> getAttribute ( 'timeout' , 900 );
2024-05-29 07:51:51 +00:00
$jwtObj = new JWT ( System :: getEnv ( '_APP_OPENSSL_KEY_V1' ), 'HS256' , $jwtExpiry , 0 );
2024-01-29 11:21:51 +00:00
$jwt = $jwtObj -> encode ([
2021-03-10 17:48:05 +00:00
'userId' => $user -> getId (),
2021-07-22 14:49:52 +00:00
'sessionId' => $current -> getId (),
2021-03-10 17:48:05 +00:00
]);
2021-03-10 16:58:46 +00:00
}
}
2024-01-29 11:15:07 +00:00
$jwtExpiry = $function -> getAttribute ( 'timeout' , 900 );
2024-05-29 07:51:51 +00:00
$jwtObj = new JWT ( System :: getEnv ( '_APP_OPENSSL_KEY_V1' ), 'HS256' , $jwtExpiry , 0 );
2024-05-06 09:55:59 +00:00
$apiKey = $jwtObj -> encode ([
2024-01-29 11:15:07 +00:00
'projectId' => $project -> getId (),
2024-05-06 09:55:59 +00:00
'scopes' => $function -> getAttribute ( 'scopes' , [])
2024-01-29 11:15:07 +00:00
]);
2024-05-14 11:58:31 +00:00
$headers [ 'x-appwrite-key' ] = API_KEY_DYNAMIC . '_' . $apiKey ;
2023-06-22 10:59:41 +00:00
$headers [ 'x-appwrite-trigger' ] = 'http' ;
$headers [ 'x-appwrite-user-id' ] = $user -> getId () ? ? '' ;
2024-01-29 11:21:51 +00:00
$headers [ 'x-appwrite-user-jwt' ] = $jwt ? ? '' ;
2023-06-22 10:59:41 +00:00
$headers [ 'x-appwrite-country-code' ] = '' ;
$headers [ 'x-appwrite-continent-code' ] = '' ;
$headers [ 'x-appwrite-continent-eu' ] = 'false' ;
$ip = $headers [ 'x-real-ip' ] ? ? '' ;
if ( ! empty ( $ip )) {
$record = $geodb -> get ( $ip );
if ( $record ) {
$eu = Config :: getParam ( 'locale-eu' );
2023-06-23 07:01:51 +00:00
$headers [ 'x-appwrite-country-code' ] = $record [ 'country' ][ 'iso_code' ] ? ? '' ;
2023-06-22 10:59:41 +00:00
$headers [ 'x-appwrite-continent-code' ] = $record [ 'continent' ][ 'code' ] ? ? '' ;
$headers [ 'x-appwrite-continent-eu' ] = ( \in_array ( $record [ 'country' ][ 'iso_code' ], $eu )) ? 'true' : 'false' ;
}
}
2023-07-30 07:17:23 +00:00
$headersFiltered = [];
foreach ( $headers as $key => $value ) {
2023-08-11 13:34:57 +00:00
if ( \in_array ( \strtolower ( $key ), FUNCTION_ALLOWLIST_HEADERS_REQUEST )) {
2023-08-09 15:53:58 +00:00
$headersFiltered [] = [ 'name' => $key , 'value' => $value ];
2023-07-30 07:17:23 +00:00
}
}
2023-07-29 16:20:20 +00:00
$executionId = ID :: unique ();
2024-06-07 19:05:29 +00:00
$status = $async ? 'waiting' : 'processing' ;
2024-09-05 02:25:11 +00:00
if ( ! is_null ( $scheduledAt )) {
2024-06-07 19:05:29 +00:00
$status = 'scheduled' ;
}
2023-07-29 16:20:20 +00:00
$execution = new Document ([
'$id' => $executionId ,
'$permissions' => ! $user -> isEmpty () ? [ Permission :: read ( Role :: user ( $user -> getId ()))] : [],
'functionInternalId' => $function -> getInternalId (),
'functionId' => $function -> getId (),
'deploymentInternalId' => $deployment -> getInternalId (),
'deploymentId' => $deployment -> getId (),
2024-06-17 12:44:12 +00:00
'trigger' => ( ! is_null ( $scheduledAt )) ? 'schedule' : 'http' ,
2024-06-27 22:12:23 +00:00
'status' => $status , // waiting / processing / completed / failed / scheduled
2023-07-29 16:20:20 +00:00
'responseStatusCode' => 0 ,
'responseHeaders' => [],
'requestPath' => $path ,
'requestMethod' => $method ,
2023-07-30 07:17:23 +00:00
'requestHeaders' => $headersFiltered ,
2023-07-29 16:20:20 +00:00
'errors' => '' ,
'logs' => '' ,
'duration' => 0.0 ,
'search' => implode ( ' ' , [ $functionId , $executionId ]),
]);
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-04-13 12:39:31 +00:00
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'executionId' , $execution -> getId ())
2022-06-22 10:51:49 +00:00
-> setContext ( 'function' , $function );
2022-04-13 12:39:31 +00:00
2021-08-24 09:32:27 +00:00
if ( $async ) {
2024-09-05 02:25:11 +00:00
if ( is_null ( $scheduledAt )) {
2024-07-22 10:36:18 +00:00
$execution = Authorization :: skip ( fn () => $dbForProject -> createDocument ( 'executions' , $execution ));
2024-06-07 19:05:29 +00:00
$queueForFunctions
-> setType ( 'http' )
-> setExecution ( $execution )
-> setFunction ( $function )
-> setBody ( $body )
-> setHeaders ( $headers )
-> setPath ( $path )
-> setMethod ( $method )
-> setJWT ( $jwt )
-> setProject ( $project )
-> setUser ( $user )
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'executionId' , $execution -> getId ())
-> trigger ();
} else {
2024-06-28 21:42:55 +00:00
$data = [
2024-06-26 11:30:23 +00:00
'headers' => $headers ,
'path' => $path ,
'method' => $method ,
'body' => $body ,
2024-09-07 10:20:23 +00:00
'userId' => $user -> getId ()
2024-06-26 11:30:23 +00:00
];
2024-07-22 10:36:18 +00:00
$schedule = $dbForConsole -> createDocument ( 'schedules' , new Document ([
2024-06-07 19:05:29 +00:00
'region' => System :: getEnv ( '_APP_REGION' , 'default' ),
2024-06-27 22:12:23 +00:00
'resourceType' => ScheduleExecutions :: getSupportedResource (),
2024-06-17 12:44:12 +00:00
'resourceId' => $execution -> getId (),
'resourceInternalId' => $execution -> getInternalId (),
2024-06-07 19:05:29 +00:00
'resourceUpdatedAt' => DateTime :: now (),
'projectId' => $project -> getId (),
'schedule' => $scheduledAt ,
2024-06-28 21:42:55 +00:00
'data' => $data ,
2024-06-07 19:05:29 +00:00
'active' => true ,
]));
2024-07-22 10:36:18 +00:00
$execution = $execution
-> setAttribute ( 'scheduleId' , $schedule -> getId ())
-> setAttribute ( 'scheduleInternalId' , $schedule -> getInternalId ())
-> setAttribute ( 'scheduledAt' , $scheduledAt );
$execution = Authorization :: skip ( fn () => $dbForProject -> createDocument ( 'executions' , $execution ));
2024-06-07 19:05:29 +00:00
}
2020-07-12 21:18:52 +00:00
2022-09-07 11:02:36 +00:00
return $response
-> setStatusCode ( Response :: STATUS_CODE_ACCEPTED )
-> dynamic ( $execution , Response :: MODEL_EXECUTION );
2021-08-24 09:32:27 +00:00
}
2022-01-10 14:18:33 +00:00
2023-03-28 13:21:42 +00:00
$durationStart = \microtime ( true );
2022-08-10 13:43:05 +00:00
2023-03-11 16:06:02 +00:00
$vars = [];
2023-09-11 10:22:16 +00:00
// V2 vars
if ( $version === 'v2' ) {
$vars = \array_merge ( $vars , [
'APPWRITE_FUNCTION_TRIGGER' => $headers [ 'x-appwrite-trigger' ] ? ? '' ,
'APPWRITE_FUNCTION_DATA' => $body ? ? '' ,
'APPWRITE_FUNCTION_USER_ID' => $headers [ 'x-appwrite-user-id' ] ? ? '' ,
'APPWRITE_FUNCTION_JWT' => $headers [ 'x-appwrite-user-jwt' ] ? ? ''
]);
}
2023-03-11 16:06:02 +00:00
// Shared vars
2023-09-05 08:21:36 +00:00
foreach ( $function -> getAttribute ( 'varsProject' , []) as $var ) {
2023-08-18 06:55:44 +00:00
$vars [ $var -> getAttribute ( 'key' )] = $var -> getAttribute ( 'value' , '' );
}
2022-08-10 13:43:05 +00:00
2023-03-11 16:06:02 +00:00
// Function vars
2023-09-05 08:21:36 +00:00
foreach ( $function -> getAttribute ( 'vars' , []) as $var ) {
$vars [ $var -> getAttribute ( 'key' )] = $var -> getAttribute ( 'value' , '' );
}
2023-03-11 16:06:02 +00:00
2024-05-06 11:27:28 +00:00
$protocol = System :: getEnv ( '_APP_OPTIONS_FORCE_HTTPS' ) == 'disabled' ? 'http' : 'https' ;
$hostname = System :: getEnv ( '_APP_DOMAIN' );
2024-01-29 11:15:07 +00:00
$endpoint = $protocol . '://' . $hostname . " /v1 " ;
2023-03-11 16:06:02 +00:00
// Appwrite vars
2022-08-10 13:43:05 +00:00
$vars = \array_merge ( $vars , [
2024-05-09 11:50:45 +00:00
'APPWRITE_FUNCTION_API_ENDPOINT' => $endpoint ,
2023-06-22 10:59:41 +00:00
'APPWRITE_FUNCTION_ID' => $functionId ,
2022-09-30 09:52:35 +00:00
'APPWRITE_FUNCTION_NAME' => $function -> getAttribute ( 'name' ),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment -> getId (),
'APPWRITE_FUNCTION_PROJECT_ID' => $project -> getId (),
2022-09-19 11:58:41 +00:00
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime [ 'name' ] ? ? '' ,
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime [ 'version' ] ? ? '' ,
2024-10-27 16:22:45 +00:00
'APPWRITE_COMPUTE_CPUS' => $spec [ 'cpus' ] ? ? APP_COMPUTE_CPUS_DEFAULT ,
'APPWRITE_COMPUTE_MEMORY' => $spec [ 'memory' ] ? ? APP_COMPUTE_MEMORY_DEFAULT ,
2024-07-15 07:10:11 +00:00
'APPWRITE_VERSION' => APP_VERSION_STABLE ,
'APPWRITE_REGION' => $project -> getAttribute ( 'region' ),
2024-09-06 10:50:29 +00:00
'APPWRITE_DEPLOYMENT_TYPE' => $deployment -> getAttribute ( 'type' , '' ),
'APPWRITE_VCS_REPOSITORY_ID' => $deployment -> getAttribute ( 'providerRepositoryId' , '' ),
'APPWRITE_VCS_REPOSITORY_NAME' => $deployment -> getAttribute ( 'providerRepositoryName' , '' ),
'APPWRITE_VCS_REPOSITORY_OWNER' => $deployment -> getAttribute ( 'providerRepositoryOwner' , '' ),
'APPWRITE_VCS_REPOSITORY_URL' => $deployment -> getAttribute ( 'providerRepositoryUrl' , '' ),
'APPWRITE_VCS_REPOSITORY_BRANCH' => $deployment -> getAttribute ( 'providerBranch' , '' ),
'APPWRITE_VCS_REPOSITORY_BRANCH_URL' => $deployment -> getAttribute ( 'providerBranchUrl' , '' ),
'APPWRITE_VCS_COMMIT_HASH' => $deployment -> getAttribute ( 'providerCommitHash' , '' ),
'APPWRITE_VCS_COMMIT_MESSAGE' => $deployment -> getAttribute ( 'providerCommitMessage' , '' ),
'APPWRITE_VCS_COMMIT_URL' => $deployment -> getAttribute ( 'providerCommitUrl' , '' ),
'APPWRITE_VCS_COMMIT_AUTHOR_NAME' => $deployment -> getAttribute ( 'providerCommitAuthor' , '' ),
'APPWRITE_VCS_COMMIT_AUTHOR_URL' => $deployment -> getAttribute ( 'providerCommitAuthorUrl' , '' ),
'APPWRITE_VCS_ROOT_DIRECTORY' => $deployment -> getAttribute ( 'providerRootDirectory' , '' ),
2022-02-04 01:29:40 +00:00
]);
2022-02-03 00:05:03 +00:00
2022-02-05 19:49:57 +00:00
/** Execute function */
2024-04-01 11:02:47 +00:00
$executor = new Executor ( System :: getEnv ( '_APP_EXECUTOR_HOST' ));
2022-02-15 17:39:03 +00:00
try {
2023-09-04 17:53:25 +00:00
$version = $function -> getAttribute ( 'version' , 'v2' );
2023-07-24 06:23:23 +00:00
$command = $runtime [ 'startCommand' ];
2023-09-04 17:53:25 +00:00
$command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"' ;
2022-02-15 17:39:03 +00:00
$executionResponse = $executor -> createExecution (
projectId : $project -> getId (),
deploymentId : $deployment -> getId (),
2023-06-22 10:59:41 +00:00
body : \strlen ( $body ) > 0 ? $body : null ,
2022-11-08 08:49:45 +00:00
variables : $vars ,
2022-02-15 17:39:03 +00:00
timeout : $function -> getAttribute ( 'timeout' , 0 ),
2022-11-08 08:49:45 +00:00
image : $runtime [ 'image' ],
2023-03-15 06:08:43 +00:00
source : $build -> getAttribute ( 'path' , '' ),
2022-02-15 17:39:03 +00:00
entrypoint : $deployment -> getAttribute ( 'entrypoint' , '' ),
2023-09-04 17:53:25 +00:00
version : $version ,
2023-02-14 11:01:38 +00:00
path : $path ,
method : $method ,
headers : $headers ,
2023-10-15 17:41:09 +00:00
runtimeEntrypoint : $command ,
2024-10-27 16:22:45 +00:00
cpus : $spec [ 'cpus' ] ? ? APP_COMPUTE_CPUS_DEFAULT ,
memory : $spec [ 'memory' ] ? ? APP_COMPUTE_MEMORY_DEFAULT ,
2024-06-25 09:33:07 +00:00
logging : $function -> getAttribute ( 'logging' , true ),
2023-10-15 17:41:09 +00:00
requestTimeout : 30
2022-02-15 17:39:03 +00:00
);
2023-07-30 07:17:23 +00:00
$headersFiltered = [];
foreach ( $executionResponse [ 'headers' ] as $key => $value ) {
2023-08-11 13:34:57 +00:00
if ( \in_array ( \strtolower ( $key ), FUNCTION_ALLOWLIST_HEADERS_RESPONSE )) {
2023-08-09 15:53:58 +00:00
$headersFiltered [] = [ 'name' => $key , 'value' => $value ];
2023-07-30 07:17:23 +00:00
}
}
2023-07-29 16:20:20 +00:00
2022-02-15 17:39:03 +00:00
/** Update execution status */
2024-08-08 08:38:15 +00:00
$status = $executionResponse [ 'statusCode' ] >= 500 ? 'failed' : 'completed' ;
2023-02-02 19:21:00 +00:00
$execution -> setAttribute ( 'status' , $status );
2023-07-29 16:20:20 +00:00
$execution -> setAttribute ( 'responseStatusCode' , $executionResponse [ 'statusCode' ]);
2023-07-30 07:17:23 +00:00
$execution -> setAttribute ( 'responseHeaders' , $headersFiltered );
2023-02-14 11:01:38 +00:00
$execution -> setAttribute ( 'logs' , $executionResponse [ 'logs' ]);
$execution -> setAttribute ( 'errors' , $executionResponse [ 'errors' ]);
2022-09-09 12:02:04 +00:00
$execution -> setAttribute ( 'duration' , $executionResponse [ 'duration' ]);
2022-02-15 17:39:03 +00:00
} catch ( \Throwable $th ) {
2023-03-28 13:21:42 +00:00
$durationEnd = \microtime ( true );
2022-07-02 14:25:44 +00:00
$execution
2023-03-28 13:21:42 +00:00
-> setAttribute ( 'duration' , $durationEnd - $durationStart )
2022-07-02 14:25:44 +00:00
-> setAttribute ( 'status' , 'failed' )
2023-07-29 16:20:20 +00:00
-> setAttribute ( 'responseStatusCode' , 500 )
2023-02-15 08:36:20 +00:00
-> setAttribute ( 'errors' , $th -> getMessage () . '\nError Code: ' . $th -> getCode ());
2022-02-15 17:39:03 +00:00
Console :: error ( $th -> getMessage ());
2024-05-20 10:44:08 +00:00
if ( $th instanceof AppwriteException ) {
throw $th ;
}
2023-12-14 04:49:16 +00:00
} finally {
2023-12-12 19:21:47 +00:00
$queueForUsage
-> addMetric ( METRIC_EXECUTIONS , 1 )
-> addMetric ( str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_EXECUTIONS ), 1 )
2023-12-14 04:49:16 +00:00
-> addMetric ( METRIC_EXECUTIONS_COMPUTE , ( int )( $execution -> getAttribute ( 'duration' ) * 1000 )) // per project
-> addMetric ( str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE ), ( int )( $execution -> getAttribute ( 'duration' ) * 1000 )) // per function
2024-10-27 16:22:45 +00:00
-> addMetric ( METRIC_EXECUTIONS_MB_SECONDS , ( int )(( $spec [ 'memory' ] ? ? APP_COMPUTE_MEMORY_DEFAULT ) * $execution -> getAttribute ( 'duration' , 0 ) * ( $spec [ 'cpus' ] ? ? APP_COMPUTE_CPUS_DEFAULT )))
-> addMetric ( str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS ), ( int )(( $spec [ 'memory' ] ? ? APP_COMPUTE_MEMORY_DEFAULT ) * $execution -> getAttribute ( 'duration' , 0 ) * ( $spec [ 'cpus' ] ? ? APP_COMPUTE_CPUS_DEFAULT )))
2023-12-12 19:21:47 +00:00
;
2022-02-04 01:29:40 +00:00
2024-07-02 11:38:59 +00:00
$execution = Authorization :: skip ( fn () => $dbForProject -> createDocument ( 'executions' , $execution ));
2023-10-05 13:15:39 +00:00
}
2022-08-14 08:44:17 +00:00
$roles = Authorization :: getRoles ();
$isPrivilegedUser = Auth :: isPrivilegedUser ( $roles );
$isAppUser = Auth :: isAppUser ( $roles );
2022-09-06 00:28:57 +00:00
2022-08-16 05:23:51 +00:00
if ( ! $isPrivilegedUser && ! $isAppUser ) {
2023-02-14 11:01:38 +00:00
$execution -> setAttribute ( 'logs' , '' );
$execution -> setAttribute ( 'errors' , '' );
2022-08-16 04:34:49 +00:00
}
2022-07-13 08:57:57 +00:00
2023-08-06 13:11:30 +00:00
$headers = [];
2023-08-16 06:19:42 +00:00
foreach (( $executionResponse [ 'headers' ] ? ? []) as $key => $value ) {
2023-08-09 15:53:58 +00:00
$headers [] = [ 'name' => $key , 'value' => $value ];
2023-08-06 13:11:30 +00:00
}
2023-08-16 06:19:42 +00:00
$execution -> setAttribute ( 'responseBody' , $executionResponse [ 'body' ] ? ? '' );
2023-08-06 13:11:30 +00:00
$execution -> setAttribute ( 'responseHeaders' , $headers );
2023-02-27 10:20:37 +00:00
2024-08-11 17:36:05 +00:00
$acceptTypes = \explode ( ', ' , $request -> getHeader ( 'accept' ));
2024-08-05 13:08:41 +00:00
foreach ( $acceptTypes as $acceptType ) {
2024-09-05 02:25:11 +00:00
if ( \str_starts_with ( $acceptType , 'application/json' ) || \str_starts_with ( $acceptType , 'application/*' )) {
2024-08-11 17:36:05 +00:00
$response -> setContentType ( Response :: CONTENT_TYPE_JSON );
break ;
} elseif ( \str_starts_with ( $acceptType , 'multipart/form-data' ) || \str_starts_with ( $acceptType , 'multipart/*' )) {
$response -> setContentType ( Response :: CONTENT_TYPE_MULTIPART );
2024-08-05 13:08:41 +00:00
break ;
}
}
2024-08-07 13:15:53 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_CREATED )
-> dynamic ( $execution , Response :: MODEL_EXECUTION );
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
App :: get ( '/v1/functions/:functionId/executions' )
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'List executions' )
2020-12-30 07:26:01 +00:00
-> label ( 'scope' , 'execution.read' )
2021-04-16 07:22:17 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
2020-05-05 17:30:12 +00:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'listExecutions' )
-> label ( 'sdk.description' , '/docs/references/functions/list-executions.md' )
2020-11-11 21:02:24 +00: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 )
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2023-03-29 19:38:39 +00:00
-> param ( 'queries' , [], new Executions (), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). 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 ( ', ' , Executions :: ALLOWED_ATTRIBUTES ), true )
2021-09-27 10:12:42 +00:00
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-09-09 08:49:18 +00:00
-> inject ( 'mode' )
-> action ( function ( string $functionId , array $queries , string $search , Response $response , Database $dbForProject , string $mode ) {
2022-04-13 12:39:31 +00:00
$function = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'functions' , $functionId ));
2020-05-05 17:30:12 +00:00
2023-08-16 21:58:25 +00:00
$isAPIKey = Auth :: isAppUser ( Authorization :: getRoles ());
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: getRoles ());
2023-07-31 18:24:21 +00:00
2023-08-16 21:58:25 +00:00
if ( $function -> isEmpty () || ( ! $function -> getAttribute ( 'enabled' ) && ! $isAPIKey && ! $isPrivilegedUser )) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2021-05-27 10:09:14 +00:00
2024-02-12 16:02:04 +00:00
try {
$queries = Query :: parseQueries ( $queries );
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
}
2021-08-06 12:36:05 +00:00
2022-08-11 23:53:52 +00:00
if ( ! empty ( $search )) {
2022-08-23 09:26:34 +00:00
$queries [] = Query :: search ( 'search' , $search );
2021-08-06 12:36:05 +00:00
}
2022-08-23 09:26:34 +00:00
// Set internal queries
$queries [] = Query :: equal ( 'functionId' , [ $function -> getId ()]);
2021-09-27 10:12:42 +00:00
2024-02-12 09:55:45 +00:00
/**
2024-02-12 10:03:31 +00:00
* Get cursor document if there was a cursor query , we use array_filter and reset for reference $cursor to $queries
2024-02-12 09:55:45 +00:00
*/
2023-08-22 03:25:55 +00:00
$cursor = \array_filter ( $queries , function ( $query ) {
2023-12-06 14:10:40 +00:00
return \in_array ( $query -> getMethod (), [ Query :: TYPE_CURSOR_AFTER , Query :: TYPE_CURSOR_BEFORE ]);
2023-08-22 03:25:55 +00:00
});
2022-08-30 23:31:43 +00:00
$cursor = reset ( $cursor );
2022-08-30 11:55:23 +00:00
if ( $cursor ) {
2022-08-23 09:26:34 +00:00
/** @var Query $cursor */
2024-10-17 05:41:24 +00:00
$validator = new Cursor ();
if ( ! $validator -> isValid ( $cursor )) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $validator -> getDescription ());
}
2022-08-23 09:26:34 +00:00
$executionId = $cursor -> getValue ();
$cursorDocument = $dbForProject -> getDocument ( 'executions' , $executionId );
2021-08-06 12:36:05 +00:00
2022-08-11 23:53:52 +00:00
if ( $cursorDocument -> isEmpty ()) {
2022-08-23 09:26:34 +00:00
throw new Exception ( Exception :: GENERAL_CURSOR_NOT_FOUND , " Execution ' { $executionId } ' for the 'cursor' value not found. " );
2021-08-06 12:36:05 +00:00
}
2022-08-23 09:26:34 +00:00
$cursor -> setValue ( $cursorDocument );
2021-09-27 10:12:42 +00:00
}
2022-08-23 09:26:34 +00:00
$filterQueries = Query :: groupByType ( $queries )[ 'filters' ];
$results = $dbForProject -> find ( 'executions' , $queries );
2022-08-11 23:53:52 +00:00
$total = $dbForProject -> count ( 'executions' , $filterQueries , APP_LIMIT_COUNT );
2020-07-12 21:18:52 +00:00
2022-08-14 08:54:11 +00:00
$roles = Authorization :: getRoles ();
$isPrivilegedUser = Auth :: isPrivilegedUser ( $roles );
$isAppUser = Auth :: isAppUser ( $roles );
2022-08-16 04:34:49 +00:00
if ( ! $isPrivilegedUser && ! $isAppUser ) {
2022-08-14 08:54:11 +00:00
$results = array_map ( function ( $execution ) {
2023-02-14 11:01:38 +00:00
$execution -> setAttribute ( 'logs' , '' );
$execution -> setAttribute ( 'errors' , '' );
2022-08-14 09:23:41 +00:00
return $execution ;
2022-08-14 08:54:11 +00:00
}, $results );
}
2020-07-12 21:18:52 +00:00
2020-10-30 19:53:27 +00:00
$response -> dynamic ( new Document ([
2021-05-27 10:09:14 +00:00
'executions' => $results ,
2022-02-27 09:57:09 +00:00
'total' => $total ,
2020-10-30 19:53:27 +00:00
]), Response :: MODEL_EXECUTION_LIST );
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
App :: get ( '/v1/functions/:functionId/executions/:executionId' )
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'Get execution' )
2020-12-30 07:26:01 +00:00
-> label ( 'scope' , 'execution.read' )
2021-04-16 07:22:17 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
2020-05-05 17:30:12 +00:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'getExecution' )
-> label ( 'sdk.description' , '/docs/references/functions/get-execution.md' )
2020-11-11 21:02:24 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_EXECUTION )
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
-> param ( 'executionId' , '' , new UID (), 'Execution ID.' )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-09-09 08:49:18 +00:00
-> inject ( 'mode' )
-> action ( function ( string $functionId , string $executionId , Response $response , Database $dbForProject , string $mode ) {
2022-04-13 12:39:31 +00:00
$function = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'functions' , $functionId ));
2020-05-05 17:30:12 +00:00
2023-08-16 21:58:25 +00:00
$isAPIKey = Auth :: isAppUser ( Authorization :: getRoles ());
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: getRoles ());
2023-07-31 18:24:21 +00:00
2023-08-16 21:58:25 +00:00
if ( $function -> isEmpty () || ( ! $function -> getAttribute ( 'enabled' ) && ! $isAPIKey && ! $isPrivilegedUser )) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2020-05-05 17:30:12 +00:00
2021-12-27 12:45:23 +00:00
$execution = $dbForProject -> getDocument ( 'executions' , $executionId );
2020-05-05 17:30:12 +00:00
2020-10-27 19:46:15 +00:00
if ( $execution -> getAttribute ( 'functionId' ) !== $function -> getId ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: EXECUTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2020-05-05 17:30:12 +00:00
2021-06-20 13:59:36 +00:00
if ( $execution -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: EXECUTION_NOT_FOUND );
2020-05-05 17:30:12 +00:00
}
2020-07-12 21:18:52 +00:00
2022-08-14 08:44:17 +00:00
$roles = Authorization :: getRoles ();
$isPrivilegedUser = Auth :: isPrivilegedUser ( $roles );
$isAppUser = Auth :: isAppUser ( $roles );
2022-08-16 05:23:51 +00:00
if ( ! $isPrivilegedUser && ! $isAppUser ) {
2023-02-14 11:01:38 +00:00
$execution -> setAttribute ( 'logs' , '' );
$execution -> setAttribute ( 'errors' , '' );
2020-05-05 17:30:12 +00:00
}
2020-07-12 21:18:52 +00:00
2020-10-30 19:53:27 +00:00
$response -> dynamic ( $execution , Response :: MODEL_EXECUTION );
2020-12-26 15:20:08 +00:00
});
2021-12-06 14:12:41 +00:00
2024-06-27 15:17:14 +00:00
App :: delete ( '/v1/functions/:functionId/executions/:executionId' )
-> groups ([ 'api' , 'functions' ])
-> desc ( 'Delete execution' )
-> label ( 'scope' , 'execution.write' )
-> label ( 'event' , 'functions.[functionId].executions.[executionId].delete' )
-> label ( 'audits.event' , 'executions.delete' )
-> label ( 'audits.resource' , 'function/{request.functionId}' )
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'deleteExecution' )
-> label ( 'sdk.description' , '/docs/references/functions/delete-execution.md' )
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
-> param ( 'executionId' , '' , new UID (), 'Execution ID.' )
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2024-06-27 22:12:23 +00:00
-> inject ( 'dbForConsole' )
2024-06-27 15:17:14 +00:00
-> inject ( 'queueForEvents' )
2024-06-27 22:12:23 +00:00
-> action ( function ( string $functionId , string $executionId , Response $response , Database $dbForProject , Database $dbForConsole , Event $queueForEvents ) {
2024-06-27 15:17:14 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
}
$execution = $dbForProject -> getDocument ( 'executions' , $executionId );
if ( $execution -> isEmpty ()) {
throw new Exception ( Exception :: EXECUTION_NOT_FOUND );
}
if ( $execution -> getAttribute ( 'functionId' ) !== $function -> getId ()) {
throw new Exception ( Exception :: EXECUTION_NOT_FOUND );
}
2024-06-27 22:12:23 +00:00
$status = $execution -> getAttribute ( 'status' );
2024-06-27 15:17:14 +00:00
2024-06-27 22:12:23 +00:00
if ( ! in_array ( $status , [ 'completed' , 'failed' , 'scheduled' ])) {
2024-06-27 15:17:14 +00:00
throw new Exception ( Exception :: EXECUTION_IN_PROGRESS );
}
if ( ! $dbForProject -> deleteDocument ( 'executions' , $execution -> getId ())) {
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'Failed to remove execution from DB' );
}
2024-06-27 22:12:23 +00:00
if ( $status === 'scheduled' ) {
2024-07-01 14:23:39 +00:00
$schedule = $dbForConsole -> findOne ( 'schedules' , [
2024-06-27 22:12:23 +00:00
Query :: equal ( 'resourceId' , [ $execution -> getId ()]),
Query :: equal ( 'resourceType' , [ ScheduleExecutions :: getSupportedResource ()]),
Query :: equal ( 'active' , [ true ]),
]);
2024-07-01 14:23:39 +00:00
if ( $schedule && ! $schedule -> isEmpty ()) {
2024-06-27 22:12:23 +00:00
$schedule
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
-> setAttribute ( 'active' , false );
Authorization :: skip ( fn () => $dbForConsole -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
}
}
2024-06-27 15:17:14 +00:00
$queueForEvents
-> setParam ( 'functionId' , $function -> getId ())
2024-07-03 07:02:41 +00:00
-> setParam ( 'executionId' , $execution -> getId ())
-> setPayload ( $response -> output ( $execution , Response :: MODEL_EXECUTION ));
2024-06-27 15:17:14 +00:00
$response -> noContent ();
});
2022-08-01 15:13:47 +00:00
// Variables
2022-08-09 15:29:24 +00:00
App :: post ( '/v1/functions/:functionId/variables' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create variable' )
2022-08-01 15:13:47 +00:00
-> groups ([ 'api' , 'functions' ])
-> label ( 'scope' , 'functions.write' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'variable.create' )
2022-09-04 08:13:44 +00:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2022-08-03 13:32:50 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'createVariable' )
2022-08-09 12:11:50 +00:00
-> label ( 'sdk.description' , '/docs/references/functions/create-variable.md' )
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_CREATED )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_VARIABLE )
2022-09-19 10:05:42 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function unique ID.' , false )
2022-08-30 07:27:44 +00:00
-> param ( 'key' , null , new Text ( Database :: LENGTH_KEY ), 'Variable key. Max length: ' . Database :: LENGTH_KEY . ' chars.' , false )
2023-08-12 19:08:44 +00:00
-> param ( 'value' , null , new Text ( 8192 , 0 ), 'Variable value. Max length: 8192 chars.' , false )
2024-10-24 08:59:47 +00:00
-> param ( 'secret' , false , new Boolean (), 'Is secret? Secret variables can only be updated or deleted, they cannot be read.' , true )
2022-08-01 15:13:47 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-06-11 10:29:04 +00:00
-> inject ( 'dbForConsole' )
2024-10-24 08:59:47 +00:00
-> action ( function ( string $functionId , string $key , string $value , bool $secret , Response $response , Database $dbForProject , Database $dbForConsole ) {
2022-08-01 15:13:47 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
2022-08-30 07:27:54 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
2022-08-26 13:38:39 +00:00
$variableId = ID :: unique ();
2022-08-01 15:13:47 +00:00
$variable = new Document ([
2022-08-26 13:38:39 +00:00
'$id' => $variableId ,
2022-08-24 15:07:18 +00:00
'$permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
2023-03-11 16:06:02 +00:00
'resourceInternalId' => $function -> getInternalId (),
'resourceId' => $function -> getId (),
'resourceType' => 'function' ,
2022-08-01 15:13:47 +00:00
'key' => $key ,
2022-08-26 13:38:39 +00:00
'value' => $value ,
2024-10-21 14:33:57 +00:00
'secret' => $secret ,
2023-03-11 16:06:02 +00:00
'search' => implode ( ' ' , [ $variableId , $function -> getId (), $key , 'function' ]),
2022-08-01 15:13:47 +00:00
]);
try {
$variable = $dbForProject -> createDocument ( 'variables' , $variable );
} catch ( DuplicateException $th ) {
2022-08-30 07:28:01 +00:00
throw new Exception ( Exception :: VARIABLE_ALREADY_EXISTS );
2022-08-01 15:13:47 +00:00
}
2023-06-22 10:59:41 +00:00
$dbForProject -> updateDocument ( 'functions' , $function -> getId (), $function -> setAttribute ( 'live' , false ));
2022-08-01 15:13:47 +00:00
2023-07-28 07:56:07 +00:00
// Inform scheduler to pull the latest changes
2023-06-11 10:29:04 +00:00
$schedule = $dbForConsole -> getDocument ( 'schedules' , $function -> getAttribute ( 'scheduleId' ));
$schedule
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
-> setAttribute ( 'schedule' , $function -> getAttribute ( 'schedule' ))
-> setAttribute ( 'active' , ! empty ( $function -> getAttribute ( 'schedule' )) && ! empty ( $function -> getAttribute ( 'deployment' )));
Authorization :: skip ( fn () => $dbForConsole -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
2022-09-07 11:11:10 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_CREATED )
-> dynamic ( $variable , Response :: MODEL_VARIABLE );
2022-08-01 15:13:47 +00:00
});
2022-08-09 15:29:24 +00:00
App :: get ( '/v1/functions/:functionId/variables' )
2023-10-02 14:02:48 +00:00
-> desc ( 'List variables' )
2022-08-01 15:13:47 +00:00
-> groups ([ 'api' , 'functions' ])
-> label ( 'scope' , 'functions.read' )
2022-08-03 13:32:50 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'listVariables' )
2022-08-09 12:11:50 +00:00
-> label ( 'sdk.description' , '/docs/references/functions/list-variables.md' )
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_VARIABLE_LIST )
2022-09-19 10:05:42 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function unique ID.' , false )
2022-08-01 15:13:47 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2022-09-12 09:02:14 +00:00
-> action ( function ( string $functionId , Response $response , Database $dbForProject ) {
2022-08-01 15:13:47 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
2022-08-30 07:28:48 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
$response -> dynamic ( new Document ([
2023-09-05 08:21:36 +00:00
'variables' => $function -> getAttribute ( 'vars' , []),
'total' => \count ( $function -> getAttribute ( 'vars' , [])),
2022-08-01 15:13:47 +00:00
]), Response :: MODEL_VARIABLE_LIST );
});
2022-08-09 15:29:24 +00:00
App :: get ( '/v1/functions/:functionId/variables/:variableId' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Get variable' )
2022-08-01 15:13:47 +00:00
-> groups ([ 'api' , 'functions' ])
-> label ( 'scope' , 'functions.read' )
2022-08-03 13:32:50 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'getVariable' )
2022-08-09 12:11:50 +00:00
-> label ( 'sdk.description' , '/docs/references/functions/get-variable.md' )
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_VARIABLE )
2022-09-19 10:05:42 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function unique ID.' , false )
-> param ( 'variableId' , '' , new UID (), 'Variable unique ID.' , false )
2022-08-01 15:13:47 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2022-08-02 10:05:58 +00:00
-> action ( function ( string $functionId , string $variableId , Response $response , Database $dbForProject ) {
2022-08-01 15:13:47 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
2022-08-30 07:28:37 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
2023-07-24 13:12:36 +00:00
$variable = $dbForProject -> getDocument ( 'variables' , $variableId );
2023-08-18 06:55:44 +00:00
if (
$variable === false ||
$variable -> isEmpty () ||
$variable -> getAttribute ( 'resourceInternalId' ) !== $function -> getInternalId () ||
$variable -> getAttribute ( 'resourceType' ) !== 'function'
) {
2023-07-24 13:12:36 +00:00
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
}
2022-08-01 15:13:47 +00:00
if ( $variable === false || $variable -> isEmpty ()) {
2022-08-30 07:29:09 +00:00
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
$response -> dynamic ( $variable , Response :: MODEL_VARIABLE );
});
2022-08-09 15:29:24 +00:00
App :: put ( '/v1/functions/:functionId/variables/:variableId' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Update variable' )
2022-08-01 15:13:47 +00:00
-> groups ([ 'api' , 'functions' ])
-> label ( 'scope' , 'functions.write' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'variable.update' )
2022-09-04 08:13:44 +00:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2022-08-03 13:32:50 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'updateVariable' )
2022-08-09 12:11:50 +00:00
-> label ( 'sdk.description' , '/docs/references/functions/update-variable.md' )
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_VARIABLE )
2022-09-19 10:05:42 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function unique ID.' , false )
-> param ( 'variableId' , '' , new UID (), 'Variable unique ID.' , false )
2022-08-31 07:33:43 +00:00
-> param ( 'key' , null , new Text ( 255 ), 'Variable key. Max length: 255 chars.' , false )
2023-08-12 19:08:44 +00:00
-> param ( 'value' , null , new Text ( 8192 , 0 ), 'Variable value. Max length: 8192 chars.' , true )
2022-08-01 15:13:47 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-06-11 10:29:04 +00:00
-> inject ( 'dbForConsole' )
-> action ( function ( string $functionId , string $variableId , string $key , ? string $value , Response $response , Database $dbForProject , Database $dbForConsole ) {
2022-08-01 15:13:47 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
2022-08-30 07:29:20 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
2023-07-24 13:12:36 +00:00
$variable = $dbForProject -> getDocument ( 'variables' , $variableId );
if ( $variable === false || $variable -> isEmpty () || $variable -> getAttribute ( 'resourceInternalId' ) !== $function -> getInternalId () || $variable -> getAttribute ( 'resourceType' ) !== 'function' ) {
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
}
2022-08-01 15:13:47 +00:00
if ( $variable === false || $variable -> isEmpty ()) {
2022-08-30 07:29:30 +00:00
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
$variable
2022-08-31 07:33:43 +00:00
-> setAttribute ( 'key' , $key )
2022-08-01 15:13:47 +00:00
-> setAttribute ( 'value' , $value ? ? $variable -> getAttribute ( 'value' ))
2023-03-28 13:21:42 +00:00
-> setAttribute ( 'search' , implode ( ' ' , [ $variableId , $function -> getId (), $key , 'function' ]));
2022-08-01 15:13:47 +00:00
try {
$dbForProject -> updateDocument ( 'variables' , $variable -> getId (), $variable );
} catch ( DuplicateException $th ) {
2022-08-30 07:28:09 +00:00
throw new Exception ( Exception :: VARIABLE_ALREADY_EXISTS );
2022-08-01 15:13:47 +00:00
}
2023-06-22 10:59:41 +00:00
$dbForProject -> updateDocument ( 'functions' , $function -> getId (), $function -> setAttribute ( 'live' , false ));
2022-08-01 15:13:47 +00:00
2023-07-28 07:56:07 +00:00
// Inform scheduler to pull the latest changes
2023-06-11 10:29:04 +00:00
$schedule = $dbForConsole -> getDocument ( 'schedules' , $function -> getAttribute ( 'scheduleId' ));
$schedule
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
-> setAttribute ( 'schedule' , $function -> getAttribute ( 'schedule' ))
-> setAttribute ( 'active' , ! empty ( $function -> getAttribute ( 'schedule' )) && ! empty ( $function -> getAttribute ( 'deployment' )));
Authorization :: skip ( fn () => $dbForConsole -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
2022-08-01 15:13:47 +00:00
$response -> dynamic ( $variable , Response :: MODEL_VARIABLE );
});
2022-08-09 15:29:24 +00:00
App :: delete ( '/v1/functions/:functionId/variables/:variableId' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Delete variable' )
2022-08-01 15:13:47 +00:00
-> groups ([ 'api' , 'functions' ])
-> label ( 'scope' , 'functions.write' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'variable.delete' )
2022-09-04 08:13:44 +00:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2022-08-03 13:32:50 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'deleteVariable' )
2022-08-09 12:11:50 +00:00
-> label ( 'sdk.description' , '/docs/references/functions/delete-variable.md' )
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
2022-09-19 10:05:42 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function unique ID.' , false )
-> param ( 'variableId' , '' , new UID (), 'Variable unique ID.' , false )
2022-08-01 15:13:47 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-06-11 10:29:04 +00:00
-> inject ( 'dbForConsole' )
-> action ( function ( string $functionId , string $variableId , Response $response , Database $dbForProject , Database $dbForConsole ) {
2022-08-01 15:13:47 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
2022-08-30 07:28:20 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
2023-07-24 13:12:36 +00:00
$variable = $dbForProject -> getDocument ( 'variables' , $variableId );
if ( $variable === false || $variable -> isEmpty () || $variable -> getAttribute ( 'resourceInternalId' ) !== $function -> getInternalId () || $variable -> getAttribute ( 'resourceType' ) !== 'function' ) {
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
}
2022-08-01 15:13:47 +00:00
if ( $variable === false || $variable -> isEmpty ()) {
2022-08-30 07:28:27 +00:00
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
$dbForProject -> deleteDocument ( 'variables' , $variable -> getId ());
2023-06-11 10:29:04 +00:00
2023-06-22 10:59:41 +00:00
$dbForProject -> updateDocument ( 'functions' , $function -> getId (), $function -> setAttribute ( 'live' , false ));
2022-08-01 15:13:47 +00:00
2023-07-28 07:56:07 +00:00
// Inform scheduler to pull the latest changes
2023-06-11 10:29:04 +00:00
$schedule = $dbForConsole -> getDocument ( 'schedules' , $function -> getAttribute ( 'scheduleId' ));
$schedule
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
-> setAttribute ( 'schedule' , $function -> getAttribute ( 'schedule' ))
-> setAttribute ( 'active' , ! empty ( $function -> getAttribute ( 'schedule' )) && ! empty ( $function -> getAttribute ( 'deployment' )));
Authorization :: skip ( fn () => $dbForConsole -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
2022-08-01 15:13:47 +00:00
$response -> noContent ();
});
2024-07-19 12:09:18 +00:00
App :: get ( '/v1/functions/templates' )
2024-08-13 12:59:37 +00:00
-> groups ([ 'api' ])
2024-07-29 20:55:18 +00:00
-> desc ( 'List function templates' )
2024-07-29 12:25:58 +00:00
-> label ( 'scope' , 'public' )
2024-07-19 12:09:18 +00:00
-> label ( 'sdk.namespace' , 'functions' )
2024-10-24 13:21:40 +00:00
-> label ( 'sdk.method' , 'listTemplates' )
2024-08-27 11:56:15 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_ADMIN ])
2024-07-30 09:43:38 +00:00
-> label ( 'sdk.description' , '/docs/references/functions/list-templates.md' )
2024-07-19 12:09:18 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
2024-07-26 12:17:03 +00:00
-> label ( 'sdk.response.model' , Response :: MODEL_TEMPLATE_FUNCTION_LIST )
2024-07-29 13:29:16 +00:00
-> param ( 'runtimes' , [], new ArrayList ( new WhiteList ( array_keys ( Config :: getParam ( 'runtimes' )), true ), APP_LIMIT_ARRAY_PARAMS_SIZE ), 'List of runtimes allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' runtimes are allowed.' , true )
2024-07-30 10:41:04 +00:00
-> param ( 'useCases' , [], new ArrayList ( new WhiteList ([ 'dev-tools' , 'starter' , 'databases' , 'ai' , 'messaging' , 'utilities' ]), APP_LIMIT_ARRAY_PARAMS_SIZE ), 'List of use cases allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' use cases are allowed.' , true )
2024-07-30 09:43:38 +00:00
-> param ( 'limit' , 25 , new Range ( 1 , 5000 ), 'Limit the number of templates returned in the response. Default limit is 25, and maximum limit is 5000.' , true )
-> param ( 'offset' , 0 , new Range ( 0 , 5000 ), 'Offset the list of returned templates. Maximum offset is 5000.' , true )
2024-07-19 12:09:18 +00:00
-> inject ( 'response' )
2024-07-29 13:29:16 +00:00
-> action ( function ( array $runtimes , array $usecases , int $limit , int $offset , Response $response ) {
2024-07-29 12:25:58 +00:00
$templates = Config :: getParam ( 'function-templates' , []);
2024-07-29 13:29:16 +00:00
if ( ! empty ( $runtimes )) {
$templates = \array_filter ( $templates , function ( $template ) use ( $runtimes ) {
return \count ( \array_intersect ( $runtimes , \array_column ( $template [ 'runtimes' ], 'name' ))) > 0 ;
});
}
2024-07-29 12:25:58 +00:00
if ( ! empty ( $usecases )) {
$templates = \array_filter ( $templates , function ( $template ) use ( $usecases ) {
2024-07-30 10:41:04 +00:00
return \count ( \array_intersect ( $usecases , $template [ 'useCases' ])) > 0 ;
2024-07-29 12:25:58 +00:00
});
}
$responseTemplates = \array_slice ( $templates , $offset , $limit );
2024-07-19 12:09:18 +00:00
$response -> dynamic ( new Document ([
2024-07-29 12:25:58 +00:00
'templates' => $responseTemplates ,
'total' => \count ( $responseTemplates ),
2024-07-26 12:17:03 +00:00
]), Response :: MODEL_TEMPLATE_FUNCTION_LIST );
2024-07-19 12:09:18 +00:00
});
2024-07-30 11:46:45 +00:00
App :: get ( '/v1/functions/templates/:templateId' )
-> desc ( 'Get function template' )
-> label ( 'scope' , 'public' )
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'getTemplate' )
2024-08-27 11:56:15 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_ADMIN ])
2024-07-30 11:46:45 +00:00
-> label ( 'sdk.description' , '/docs/references/functions/get-template.md' )
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_TEMPLATE_FUNCTION )
-> param ( 'templateId' , '' , new Text ( 128 ), 'Template ID.' )
-> inject ( 'response' )
-> action ( function ( string $templateId , Response $response ) {
$templates = Config :: getParam ( 'function-templates' , []);
2024-10-25 16:04:32 +00:00
$template = array_shift ( array_filter ( $templates , function ( $item ) use ( $templateId ) {
return $item [ 'id' ] === $templateId ;
2024-07-30 11:46:45 +00:00
}));
if ( empty ( $template )) {
throw new Exception ( Exception :: FUNCTION_TEMPLATE_NOT_FOUND );
}
$response -> dynamic ( new Document ( $template ), Response :: MODEL_TEMPLATE_FUNCTION );
});