2020-05-05 02:35:01 +12:00
< ? php
2021-03-11 06:48:05 +13:00
use Ahc\Jwt\JWT ;
use Appwrite\Auth\Auth ;
2022-04-20 01:13:55 +12:00
use Appwrite\Event\Build ;
2022-05-25 02:28:27 +12:00
use Appwrite\Event\Delete ;
use Appwrite\Event\Event ;
2022-11-16 07:13:17 +13:00
use Appwrite\Event\Func ;
2023-09-12 15:24:11 +12:00
use Appwrite\Event\Validator\FunctionEvent ;
2023-03-11 01:20:24 +13:00
use Appwrite\Utopia\Response\Model\Rule ;
2022-02-19 01:36:24 +13:00
use Appwrite\Extend\Exception ;
2022-01-19 00:05:04 +13:00
use Appwrite\Utopia\Database\Validator\CustomId ;
2023-03-11 01:20:24 +13:00
use Appwrite\Messaging\Adapter\Realtime ;
2023-02-15 00:01:38 +13:00
use Utopia\Validator\Assoc ;
2023-10-16 06:41:09 +13:00
use Appwrite\Usage\Stats ;
2023-06-11 22:29:04 +12:00
use Utopia\Database\Helpers\ID ;
use Utopia\Database\Helpers\Permission ;
use Utopia\Database\Helpers\Role ;
2021-07-26 02:47:18 +12:00
use Utopia\Database\Validator\UID ;
2022-05-25 02:28:27 +12:00
use Utopia\Storage\Device ;
2021-01-22 21:28:33 +13:00
use Utopia\Storage\Validator\File ;
2021-01-28 02:15:44 +13:00
use Utopia\Storage\Validator\FileExt ;
2021-01-22 21:28:33 +13:00
use Utopia\Storage\Validator\FileSize ;
use Utopia\Storage\Validator\Upload ;
2020-10-31 08:53:27 +13:00
use Appwrite\Utopia\Response ;
2022-05-25 02:28:27 +12:00
use Utopia\Swoole\Request ;
2020-05-06 05:30:12 +12:00
use Appwrite\Task\Validator\Cron ;
2022-08-23 21:12:48 +12:00
use Appwrite\Utopia\Database\Validator\Queries\Deployments ;
2022-08-23 21:26:34 +12:00
use Appwrite\Utopia\Database\Validator\Queries\Executions ;
2022-08-23 21:02:06 +12:00
use Appwrite\Utopia\Database\Validator\Queries\Functions ;
2020-07-13 09:18:52 +12:00
use Utopia\App ;
2021-06-13 02:33:23 +12:00
use Utopia\Database\Database ;
2021-05-05 09:25:17 +12:00
use Utopia\Database\Document ;
2022-07-13 01:32:39 +12:00
use Utopia\Database\DateTime ;
2021-05-05 09:25:17 +12:00
use Utopia\Database\Query ;
use Utopia\Database\Validator\Authorization ;
2020-05-06 05:30:12 +12:00
use Utopia\Validator\ArrayList ;
2020-05-05 02:35:01 +12:00
use Utopia\Validator\Text ;
use Utopia\Validator\Range ;
2020-05-06 05:30:12 +12:00
use Utopia\Validator\WhiteList ;
2020-07-15 04:13:18 +12:00
use Utopia\Config\Config ;
2022-02-05 23:08:05 +13:00
use Executor\Executor ;
2021-10-14 22:37:00 +13:00
use Utopia\CLI\Console ;
2022-08-13 00:23:58 +12:00
use Utopia\Database\Validator\Roles ;
2021-12-07 04:04:00 +13:00
use Utopia\Validator\Boolean ;
2022-08-03 04:11:15 +12:00
use Utopia\Database\Exception\Duplicate as DuplicateException ;
2023-06-22 22:59:41 +12:00
use MaxMind\Db\Reader ;
2023-08-10 03:53:58 +12:00
use Utopia\VCS\Adapter\Git\GitHub ;
2023-10-28 03:08:33 +13:00
use Utopia\VCS\Exception\RepositoryNotFound ;
2020-05-05 02:35:01 +12:00
include_once __DIR__ . '/../shared/api.php' ;
2023-10-09 23:16:05 +13:00
$redeployVcs = function ( Request $request , Document $function , Document $project , Document $installation , Database $dbForProject , Build $queueForBuilds , Document $template , GitHub $github ) {
2023-06-17 02:43:37 +12:00
$deploymentId = ID :: unique ();
$entrypoint = $function -> getAttribute ( 'entrypoint' , '' );
2023-08-10 03:53:58 +12:00
$providerInstallationId = $installation -> getAttribute ( 'providerInstallationId' , '' );
$privateKey = App :: getEnv ( '_APP_VCS_GITHUB_PRIVATE_KEY' );
$githubAppId = App :: getEnv ( '_APP_VCS_GITHUB_APP_ID' );
2023-08-10 05:35:23 +12:00
$github -> initializeVariables ( $providerInstallationId , $privateKey , $githubAppId );
2023-08-10 03:53:58 +12:00
$owner = $github -> getOwnerName ( $providerInstallationId );
$providerRepositoryId = $function -> getAttribute ( 'providerRepositoryId' , '' );
2023-10-28 03:08:33 +13:00
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 );
}
2023-08-10 03:53:58 +12:00
$providerBranch = $function -> getAttribute ( 'providerBranch' , 'main' );
2023-08-10 05:35:23 +12:00
$authorUrl = " https://github.com/ $owner " ;
$repositoryUrl = " https://github.com/ $owner / $repositoryName " ;
$branchUrl = " https://github.com/ $owner / $repositoryName /tree/ $providerBranch " ;
2023-08-10 12:31:49 +12:00
$commitDetails = [];
2023-08-11 00:22:10 +12:00
if ( $template -> isEmpty ()) {
2023-08-10 12:31:49 +12:00
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 ());
}
}
2023-08-10 03:53:58 +12:00
2023-06-17 02:43:37 +12:00
$deployment = $dbForProject -> createDocument ( 'deployments' , new Document ([
'$id' => $deploymentId ,
'$permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
'resourceId' => $function -> getId (),
2023-10-03 03:02:48 +13:00
'resourceInternalId' => $function -> getInternalId (),
2023-06-17 02:43:37 +12:00
'resourceType' => 'functions' ,
'entrypoint' => $entrypoint ,
2023-07-21 23:52:14 +12:00
'commands' => $function -> getAttribute ( 'commands' , '' ),
2023-06-17 02:43:37 +12:00
'type' => 'vcs' ,
2023-07-30 21:51:13 +12:00
'installationId' => $installation -> getId (),
'installationInternalId' => $installation -> getInternalId (),
2023-08-10 03:53:58 +12:00
'providerRepositoryId' => $providerRepositoryId ,
2023-07-30 21:51:13 +12:00
'repositoryId' => $function -> getAttribute ( 'repositoryId' , '' ),
'repositoryInternalId' => $function -> getAttribute ( 'repositoryInternalId' , '' ),
2023-08-10 05:35:23 +12:00
'providerBranchUrl' => $branchUrl ,
2023-08-10 03:53:58 +12:00
'providerRepositoryName' => $repositoryName ,
'providerRepositoryOwner' => $owner ,
'providerRepositoryUrl' => $repositoryUrl ,
'providerCommitHash' => $commitDetails [ 'commitHash' ] ? ? '' ,
2023-08-10 05:35:23 +12:00
'providerCommitAuthorUrl' => $authorUrl ,
2023-08-10 03:53:58 +12:00
'providerCommitAuthor' => $commitDetails [ 'commitAuthor' ] ? ? '' ,
'providerCommitMessage' => $commitDetails [ 'commitMessage' ] ? ? '' ,
'providerCommitUrl' => $commitDetails [ 'commitUrl' ] ? ? '' ,
'providerBranch' => $providerBranch ,
2023-07-30 21:51:13 +12:00
'providerRootDirectory' => $function -> getAttribute ( 'providerRootDirectory' , '' ),
2023-06-17 02:43:37 +12:00
'search' => implode ( ' ' , [ $deploymentId , $entrypoint ]),
'activate' => true ,
]));
2023-08-11 19:52:08 +12:00
2023-10-09 23:16:05 +13:00
$queueForBuilds
2023-06-17 02:43:37 +12:00
-> setType ( BUILD_TYPE_DEPLOYMENT )
-> setResource ( $function )
-> setDeployment ( $deployment )
2023-07-30 21:51:13 +12:00
-> setTemplate ( $template )
2023-06-17 02:43:37 +12:00
-> setProject ( $project )
-> trigger ();
};
2020-07-13 09:18:52 +12:00
App :: post ( '/v1/functions' )
-> groups ([ 'api' , 'functions' ])
2023-10-03 03:02:48 +13:00
-> desc ( 'Create function' )
2020-05-05 02:35:01 +12:00
-> label ( 'scope' , 'functions.write' )
2022-04-14 00:39:31 +12:00
-> label ( 'event' , 'functions.[functionId].create' )
2022-09-05 20:00:08 +12:00
-> label ( 'audits.event' , 'function.create' )
2022-08-10 02:38:54 +12:00
-> label ( 'audits.resource' , 'function/{response.$id}' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-05 02:35:01 +12:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'create' )
2020-05-06 05:30:12 +12:00
-> label ( 'sdk.description' , '/docs/references/functions/create-function.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_CREATED )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_FUNCTION )
2023-06-11 22:29:04 +12:00
-> 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.' )
2020-09-11 02:40:14 +12:00
-> param ( 'name' , '' , new Text ( 128 ), 'Function name. Max length: 128 chars.' )
2021-06-22 22:18:26 +12:00
-> param ( 'runtime' , '' , new WhiteList ( array_keys ( Config :: getParam ( 'runtimes' )), true ), 'Execution runtime.' )
2023-08-23 08:11:33 +12:00
-> 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 )
2023-09-12 15:24:11 +12:00
-> param ( 'events' , [], new ArrayList ( new FunctionEvent (), APP_LIMIT_ARRAY_PARAMS_SIZE ), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.' , true )
2020-09-11 02:40:14 +12:00
-> param ( 'schedule' , '' , new Cron (), 'Schedule CRON syntax.' , true )
2022-01-21 21:05:41 +13:00
-> param ( 'timeout' , 15 , new Range ( 1 , ( int ) App :: getEnv ( '_APP_FUNCTIONS_TIMEOUT' , 900 )), 'Function maximum execution time in seconds.' , true )
2023-08-23 08:11:33 +12:00
-> 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 )
2023-08-23 20:16:23 +12:00
-> param ( 'entrypoint' , '' , new Text ( 1028 , 0 ), 'Entrypoint File. This path is relative to the "providerRootDirectory".' , true )
2023-08-18 19:14:13 +12:00
-> param ( 'commands' , '' , new Text ( 8192 , 0 ), 'Build Commands.' , true )
2023-08-23 20:29:01 +12:00
-> param ( 'installationId' , '' , new Text ( 128 , 0 ), 'Appwrite Installation ID for VCS (Version Control System) deployment.' , true )
2023-08-23 08:11:33 +12:00
-> 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 )
2023-08-23 20:29:01 +12:00
-> 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 )
2023-08-23 08:11:33 +12:00
-> 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 ( 'templateBranch' , '' , new Text ( 128 , 0 ), 'Production branch for the repo linked to the function template.' , true )
2023-06-22 22:59:41 +12:00
-> inject ( 'request' )
2020-12-27 04:20:08 +13:00
-> inject ( 'response' )
2021-12-28 01:45:23 +13:00
-> inject ( 'dbForProject' )
2022-09-23 18:12:17 +12:00
-> inject ( 'project' )
-> inject ( 'user' )
2022-12-21 05:11:30 +13:00
-> inject ( 'queueForEvents' )
2023-10-09 23:16:05 +13:00
-> inject ( 'queueForBuilds' )
2022-11-07 10:41:33 +13:00
-> inject ( 'dbForConsole' )
2023-08-10 03:53:58 +12:00
-> inject ( 'gitHub' )
2023-10-09 23:16:05 +13:00
-> action ( function ( string $functionId , string $name , string $runtime , array $execute , array $events , string $schedule , int $timeout , bool $enabled , bool $logging , string $entrypoint , string $commands , string $installationId , string $providerRepositoryId , string $providerBranch , bool $providerSilentMode , string $providerRootDirectory , string $templateRepository , string $templateOwner , string $templateRootDirectory , string $templateBranch , Request $request , Response $response , Database $dbForProject , Document $project , Document $user , Event $queueForEvents , Build $queueForBuilds , Database $dbForConsole , GitHub $github ) use ( $redeployVcs ) {
2022-08-15 02:22:38 +12:00
$functionId = ( $functionId == 'unique()' ) ? ID :: unique () : $functionId ;
2023-06-17 01:44:36 +12:00
2023-06-18 00:12:21 +12:00
// build from template
2023-07-30 21:51:13 +12:00
$template = new Document ([]);
2023-08-18 18:55:44 +12:00
if (
! empty ( $templateRepository )
&& ! empty ( $templateOwner )
&& ! empty ( $templateRootDirectory )
&& ! empty ( $templateBranch )
) {
2023-07-30 21:51:13 +12:00
$template -> setAttribute ( 'repositoryName' , $templateRepository )
-> setAttribute ( 'ownerName' , $templateOwner )
2023-06-22 22:59:41 +12:00
-> setAttribute ( 'rootDirectory' , $templateRootDirectory )
-> setAttribute ( 'branch' , $templateBranch );
2023-06-18 00:12:21 +12:00
}
2023-08-12 01:34:57 +12:00
$installation = $dbForConsole -> getDocument ( 'installations' , $installationId );
2023-06-17 01:44:36 +12:00
2023-07-30 21:51:13 +12:00
if ( ! empty ( $installationId ) && $installation -> isEmpty ()) {
2023-06-17 01:44:36 +12:00
throw new Exception ( Exception :: INSTALLATION_NOT_FOUND );
}
2023-07-30 21:51:13 +12:00
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".' );
2023-06-17 01:44:36 +12:00
}
2021-12-28 01:45:23 +13:00
$function = $dbForProject -> createDocument ( 'functions' , new Document ([
2021-08-15 06:56:28 +12:00
'$id' => $functionId ,
2021-05-05 09:25:17 +12:00
'execute' => $execute ,
2022-09-12 21:02:14 +12:00
'enabled' => $enabled ,
2023-07-28 19:56:07 +12:00
'live' => true ,
'logging' => $logging ,
2020-07-13 09:18:52 +12:00
'name' => $name ,
2021-06-22 02:42:39 +12:00
'runtime' => $runtime ,
2023-06-11 22:29:04 +12:00
'deploymentInternalId' => '' ,
2022-01-25 11:59:02 +13:00
'deployment' => '' ,
2020-07-13 09:18:52 +12:00
'events' => $events ,
'schedule' => $schedule ,
2023-06-11 22:29:04 +12:00
'scheduleInternalId' => '' ,
2023-08-22 05:43:03 +12:00
'scheduleId' => '' ,
2020-07-13 09:18:52 +12:00
'timeout' => $timeout ,
2023-07-28 19:56:07 +12:00
'entrypoint' => $entrypoint ,
'commands' => $commands ,
'search' => implode ( ' ' , [ $functionId , $name , $runtime ]),
2023-07-30 21:51:13 +12:00
'version' => 'v3' ,
'installationId' => $installation -> getId (),
'installationInternalId' => $installation -> getInternalId (),
'providerRepositoryId' => $providerRepositoryId ,
'repositoryId' => '' ,
'repositoryInternalId' => '' ,
'providerBranch' => $providerBranch ,
'providerRootDirectory' => $providerRootDirectory ,
'providerSilentMode' => $providerSilentMode ,
2021-05-05 09:25:17 +12:00
]));
2020-05-06 05:30:12 +12:00
2022-11-16 05:14:52 +13:00
$schedule = Authorization :: skip (
2023-08-22 05:43:03 +12:00
fn () => $dbForConsole -> createDocument ( 'schedules' , new Document ([
2022-11-17 01:51:43 +13:00
'region' => App :: getEnv ( '_APP_REGION' , 'default' ), // Todo replace with projects region
2022-11-07 10:41:33 +13:00
'resourceType' => 'function' ,
'resourceId' => $function -> getId (),
2023-06-11 22:29:04 +12:00
'resourceInternalId' => $function -> getInternalId (),
2022-11-07 10:41:33 +13:00
'resourceUpdatedAt' => DateTime :: now (),
'projectId' => $project -> getId (),
2022-11-08 04:42:08 +13:00
'schedule' => $function -> getAttribute ( 'schedule' ),
2022-11-07 10:41:33 +13:00
'active' => false ,
]))
);
2022-11-16 05:14:52 +13:00
$function -> setAttribute ( 'scheduleId' , $schedule -> getId ());
2023-06-11 22:29:04 +12:00
$function -> setAttribute ( 'scheduleInternalId' , $schedule -> getInternalId ());
2023-08-22 05:43:03 +12:00
2023-06-17 01:44:36 +12:00
// Git connect logic
2023-07-30 21:51:13 +12:00
if ( ! empty ( $providerRepositoryId )) {
2023-11-15 04:50:59 +13:00
$teamId = $project -> getAttribute ( 'teamId' , '' );
2023-07-31 07:10:25 +12:00
$repository = $dbForConsole -> createDocument ( 'repositories' , new Document ([
2023-06-17 01:44:36 +12:00
'$id' => ID :: unique (),
'$permissions' => [
2023-11-15 04:50:59 +13:00
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' )),
2023-06-17 01:44:36 +12:00
],
2023-07-30 21:51:13 +12:00
'installationId' => $installation -> getId (),
'installationInternalId' => $installation -> getInternalId (),
2023-06-17 01:44:36 +12:00
'projectId' => $project -> getId (),
'projectInternalId' => $project -> getInternalId (),
2023-07-30 21:51:13 +12:00
'providerRepositoryId' => $providerRepositoryId ,
2023-07-28 19:56:07 +12:00
'resourceId' => $function -> getId (),
'resourceInternalId' => $function -> getInternalId (),
2023-06-28 20:48:10 +12:00
'resourceType' => 'function' ,
2023-07-30 21:51:13 +12:00
'providerPullRequestIds' => []
2023-06-17 01:44:36 +12:00
]));
2023-08-22 05:43:03 +12:00
$function -> setAttribute ( 'repositoryId' , $repository -> getId ());
$function -> setAttribute ( 'repositoryInternalId' , $repository -> getInternalId ());
2023-06-17 01:44:36 +12:00
}
2023-08-22 05:43:03 +12:00
$function = $dbForProject -> updateDocument ( 'functions' , $function -> getId (), $function );
2022-11-07 10:41:33 +13:00
2023-06-17 01:44:36 +12:00
// Redeploy vcs logic
2023-07-30 21:51:13 +12:00
if ( ! empty ( $providerRepositoryId )) {
2023-10-09 23:16:05 +13:00
$redeployVcs ( $request , $function , $project , $installation , $dbForProject , $queueForBuilds , $template , $github );
2023-06-17 01:44:36 +12:00
}
2023-07-25 01:12:36 +12:00
$functionsDomain = App :: getEnv ( '_APP_DOMAIN_FUNCTIONS' , '' );
if ( ! empty ( $functionsDomain )) {
2023-03-09 07:30:01 +13:00
$ruleId = ID :: unique ();
$routeSubdomain = ID :: unique ();
$domain = " { $routeSubdomain } . { $functionsDomain } " ;
2023-03-11 01:20:24 +13:00
$rule = Authorization :: skip (
2023-03-29 02:21:42 +13:00
fn () => $dbForConsole -> createDocument ( 'rules' , new Document ([
2023-03-09 07:30:01 +13:00
'$id' => $ruleId ,
'projectId' => $project -> getId (),
'projectInternalId' => $project -> getInternalId (),
'domain' => $domain ,
'resourceType' => 'function' ,
'resourceId' => $function -> getId (),
'resourceInternalId' => $function -> getInternalId (),
2023-03-09 08:50:51 +13:00
'status' => 'verified' ,
2023-03-09 07:30:01 +13:00
'certificateId' => '' ,
]))
);
2023-03-11 01:20:24 +13:00
/** Trigger Webhook */
$ruleModel = new Rule ();
2023-10-02 06:39:26 +13:00
$ruleCreate =
$queueForEvents
2023-10-28 03:08:33 +13:00
-> setClass ( Event :: WEBHOOK_CLASS_NAME )
-> setQueue ( Event :: WEBHOOK_QUEUE_NAME );
2023-10-02 06:39:26 +13:00
2023-03-11 01:20:24 +13:00
$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' ]
);
2023-03-09 07:30:01 +13:00
}
2022-09-23 18:12:17 +12:00
2022-12-21 05:11:30 +13:00
$queueForEvents -> setParam ( 'functionId' , $function -> getId ());
2022-04-14 00:39:31 +12:00
2022-09-07 23:02:36 +12:00
$response
-> setStatusCode ( Response :: STATUS_CODE_CREATED )
-> dynamic ( $function , Response :: MODEL_FUNCTION );
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
App :: get ( '/v1/functions' )
-> groups ([ 'api' , 'functions' ])
2023-10-03 03:02:48 +13:00
-> desc ( 'List functions' )
2020-05-06 05:30:12 +12:00
-> label ( 'scope' , 'functions.read' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-06 05:30:12 +12:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'list' )
-> label ( 'sdk.description' , '/docs/references/functions/list-functions.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_FUNCTION_LIST )
2023-03-30 08:38:39 +13: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-11 02:40:14 +12:00
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
2020-12-27 04:20:08 +13:00
-> inject ( 'response' )
2021-12-28 01:45:23 +13:00
-> inject ( 'dbForProject' )
2022-08-23 21:02:06 +12:00
-> action ( function ( array $queries , string $search , Response $response , Database $dbForProject ) {
2020-07-13 09:18:52 +12:00
2022-08-23 21:02:06 +12:00
$queries = Query :: parseQueries ( $queries );
2021-08-07 00:36:05 +12:00
2022-08-12 11:53:52 +12:00
if ( ! empty ( $search )) {
2022-08-23 21:02:06 +12:00
$queries [] = Query :: search ( 'search' , $search );
2021-08-07 00:36:05 +12:00
}
2022-08-23 21:02:06 +12:00
// Get cursor document if there was a cursor query
2023-08-22 15:25:55 +12:00
$cursor = \array_filter ( $queries , function ( $query ) {
return \in_array ( $query -> getMethod (), [ Query :: TYPE_CURSORAFTER , Query :: TYPE_CURSORBEFORE ]);
});
2022-08-31 11:31:43 +12:00
$cursor = reset ( $cursor );
2022-08-30 23:55:23 +12:00
if ( $cursor ) {
2022-08-23 21:02:06 +12:00
/** @var Query $cursor */
$functionId = $cursor -> getValue ();
$cursorDocument = $dbForProject -> getDocument ( 'functions' , $functionId );
2021-08-19 01:42:03 +12:00
2022-08-12 11:53:52 +12:00
if ( $cursorDocument -> isEmpty ()) {
2022-08-23 21:02:06 +12:00
throw new Exception ( Exception :: GENERAL_CURSOR_NOT_FOUND , " Function ' { $functionId } ' for the 'cursor' value not found. " );
2022-08-12 11:53:52 +12:00
}
2022-08-23 21:02:06 +12:00
$cursor -> setValue ( $cursorDocument );
2021-08-19 01:42:03 +12:00
}
2020-07-13 09:18:52 +12:00
2022-08-23 21:02:06 +12:00
$filterQueries = Query :: groupByType ( $queries )[ 'filters' ];
2020-10-31 08:53:27 +13:00
$response -> dynamic ( new Document ([
2022-08-23 21:02:06 +12:00
'functions' => $dbForProject -> find ( 'functions' , $queries ),
2022-08-12 11:53:52 +12:00
'total' => $dbForProject -> count ( 'functions' , $filterQueries , APP_LIMIT_COUNT ),
2020-10-31 08:53:27 +13:00
]), Response :: MODEL_FUNCTION_LIST );
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
2021-12-10 02:02:12 +13:00
App :: get ( '/v1/functions/runtimes' )
-> groups ([ 'api' , 'functions' ])
2022-03-03 01:21:03 +13:00
-> desc ( 'List runtimes' )
2021-12-10 02:02:12 +13: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-25 02:28:27 +12:00
-> action ( function ( Response $response ) {
2021-12-10 02:02:12 +13:00
$runtimes = Config :: getParam ( 'runtimes' );
$runtimes = array_map ( function ( $key ) use ( $runtimes ) {
$runtimes [ $key ][ '$id' ] = $key ;
return $runtimes [ $key ];
}, array_keys ( $runtimes ));
2022-04-14 00:39:31 +12:00
$response -> dynamic ( new Document ([
2022-02-27 22:57:09 +13:00
'total' => count ( $runtimes ),
2021-12-10 02:02:12 +13:00
'runtimes' => $runtimes
]), Response :: MODEL_RUNTIME_LIST );
});
2020-07-13 09:18:52 +12:00
App :: get ( '/v1/functions/:functionId' )
-> groups ([ 'api' , 'functions' ])
2023-10-03 03:02:48 +13:00
-> desc ( 'Get function' )
2020-05-06 05:30:12 +12:00
-> label ( 'scope' , 'functions.read' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-06 05:30:12 +12:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'get' )
-> label ( 'sdk.description' , '/docs/references/functions/get-function.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_FUNCTION )
2021-12-11 01:27:11 +13:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2020-12-27 04:20:08 +13:00
-> inject ( 'response' )
2021-12-28 01:45:23 +13:00
-> inject ( 'dbForProject' )
2022-05-25 02:28:27 +12:00
-> action ( function ( string $functionId , Response $response , Database $dbForProject ) {
2021-12-28 01:45:23 +13:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-05-06 05:30:12 +12:00
2021-06-21 01:59:36 +12:00
if ( $function -> isEmpty ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-05-06 05:30:12 +12:00
}
2020-10-31 08:53:27 +13:00
$response -> dynamic ( $function , Response :: MODEL_FUNCTION );
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
2020-10-31 08:53:27 +13:00
App :: get ( '/v1/functions/:functionId/usage' )
2023-10-03 03:02:48 +13:00
-> desc ( 'Get function usage' )
2023-09-28 06:10:21 +13:00
-> groups ([ 'api' , 'functions' , 'usage' ])
2020-10-31 08:53:27 +13:00
-> label ( 'scope' , 'functions.read' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_ADMIN ])
2020-10-31 08:53:27 +13:00
-> label ( 'sdk.namespace' , 'functions' )
2022-07-17 22:30:58 +12:00
-> label ( 'sdk.method' , 'getFunctionUsage' )
2021-08-27 07:12:36 +12:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
2021-08-28 05:34:43 +12:00
-> label ( 'sdk.response.model' , Response :: MODEL_USAGE_FUNCTIONS )
2021-12-11 01:27:11 +13:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2020-10-31 08:53:27 +13:00
-> param ( 'range' , '30d' , new WhiteList ([ '24h' , '7d' , '30d' , '90d' ]), 'Date range.' , true )
2020-12-27 04:20:08 +13:00
-> inject ( 'response' )
2021-12-28 01:45:23 +13:00
-> inject ( 'dbForProject' )
2022-05-25 02:28:27 +12:00
-> action ( function ( string $functionId , string $range , Response $response , Database $dbForProject ) {
2020-10-31 08:53:27 +13:00
2021-12-28 01:45:23 +13:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-10-31 08:53:27 +13:00
2021-06-21 01:59:36 +12:00
if ( $function -> isEmpty ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-10-31 08:53:27 +13:00
}
2022-04-14 00:39:31 +12:00
2023-10-16 06:41:09 +13:00
$usage = [];
if ( App :: getEnv ( '_APP_USAGE_STATS' , 'enabled' ) == 'enabled' ) {
$periods = [
'24h' => [
'period' => '1h' ,
'limit' => 24 ,
],
'7d' => [
'period' => '1d' ,
'limit' => 7 ,
],
'30d' => [
'period' => '1d' ,
'limit' => 30 ,
],
'90d' => [
'period' => '1d' ,
'limit' => 90 ,
],
];
2023-06-11 22:29:04 +12:00
2023-10-16 06:41:09 +13:00
$metrics = [
" executions. $functionId .compute.total " ,
" executions. $functionId .compute.success " ,
" executions. $functionId .compute.failure " ,
" executions. $functionId .compute.time " ,
" builds. $functionId .compute.total " ,
" builds. $functionId .compute.success " ,
" builds. $functionId .compute.failure " ,
" builds. $functionId .compute.time " ,
2021-08-27 07:12:36 +12:00
];
2023-10-16 06:41:09 +13:00
$stats = [];
Authorization :: skip ( function () use ( $dbForProject , $periods , $range , $metrics , & $stats ) {
foreach ( $metrics as $metric ) {
$limit = $periods [ $range ][ 'limit' ];
$period = $periods [ $range ][ 'period' ];
$requestDocs = $dbForProject -> find ( 'stats' , [
Query :: equal ( 'period' , [ $period ]),
Query :: equal ( 'metric' , [ $metric ]),
Query :: limit ( $limit ),
Query :: orderDesc ( 'time' ),
]);
$stats [ $metric ] = [];
foreach ( $requestDocs as $requestDoc ) {
$stats [ $metric ][] = [
'value' => $requestDoc -> getAttribute ( 'value' ),
'date' => $requestDoc -> getAttribute ( 'time' ),
];
}
// backfill metrics with empty values for graphs
$backfill = $limit - \count ( $requestDocs );
while ( $backfill > 0 ) {
$last = $limit - $backfill - 1 ; // array index of last added metric
$diff = match ( $period ) { // convert period to seconds for unix timestamp math
'1h' => 3600 ,
'1d' => 86400 ,
};
$stats [ $metric ][] = [
'value' => 0 ,
'date' => DateTime :: formatTz ( DateTime :: addSeconds ( new \DateTime ( $stats [ $metric ][ $last ][ 'date' ] ? ? null ), - 1 * $diff )),
];
$backfill -- ;
}
$stats [ $metric ] = array_reverse ( $stats [ $metric ]);
}
});
$usage = new Document ([
'range' => $range ,
'executionsTotal' => $stats [ " executions. $functionId .compute.total " ] ? ? [],
'executionsFailure' => $stats [ " executions. $functionId .compute.failure " ] ? ? [],
'executionsSuccess' => $stats [ " executions. $functionId .compute.success " ] ? ? [],
'executionsTime' => $stats [ " executions. $functionId .compute.time " ] ? ? [],
'buildsTotal' => $stats [ " builds. $functionId .compute.total " ] ? ? [],
'buildsFailure' => $stats [ " builds. $functionId .compute.failure " ] ? ? [],
'buildsSuccess' => $stats [ " builds. $functionId .compute.success " ] ? ? [],
'buildsTime' => $stats [ " builds. $functionId .compute.time " ? ? []]
]);
2023-06-11 22:29:04 +12:00
}
2021-08-27 07:12:36 +12:00
2023-10-16 06:41:09 +13:00
$response -> dynamic ( $usage , Response :: MODEL_USAGE_FUNCTION );
2022-07-17 22:30:58 +12:00
});
App :: get ( '/v1/functions/usage' )
2023-10-04 05:50:48 +13:00
-> desc ( 'Get functions usage' )
2023-10-16 06:41:09 +13:00
-> groups ([ 'api' , 'functions' , 'usage' ])
2022-07-17 22:30:58 +12: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 )
-> param ( 'range' , '30d' , new WhiteList ([ '24h' , '7d' , '30d' , '90d' ]), 'Date range.' , true )
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2022-07-17 22:39:44 +12:00
-> action ( function ( string $range , Response $response , Database $dbForProject ) {
2022-07-17 22:30:58 +12:00
2023-10-16 06:41:09 +13:00
$usage = [];
if ( App :: getEnv ( '_APP_USAGE_STATS' , 'enabled' ) == 'enabled' ) {
$periods = [
'24h' => [
'period' => '1h' ,
'limit' => 24 ,
],
'7d' => [
'period' => '1d' ,
'limit' => 7 ,
],
'30d' => [
'period' => '1d' ,
'limit' => 30 ,
],
'90d' => [
'period' => '1d' ,
'limit' => 90 ,
],
];
2023-06-11 22:29:04 +12:00
2023-10-16 06:41:09 +13:00
$metrics = [
'executions.$all.compute.total' ,
'executions.$all.compute.failure' ,
'executions.$all.compute.success' ,
'executions.$all.compute.time' ,
'builds.$all.compute.total' ,
'builds.$all.compute.failure' ,
'builds.$all.compute.success' ,
'builds.$all.compute.time' ,
2023-06-11 22:29:04 +12:00
];
2023-10-16 06:41:09 +13:00
$stats = [];
Authorization :: skip ( function () use ( $dbForProject , $periods , $range , $metrics , & $stats ) {
foreach ( $metrics as $metric ) {
$limit = $periods [ $range ][ 'limit' ];
$period = $periods [ $range ][ 'period' ];
$requestDocs = $dbForProject -> find ( 'stats' , [
Query :: equal ( 'period' , [ $period ]),
Query :: equal ( 'metric' , [ $metric ]),
Query :: limit ( $limit ),
Query :: orderDesc ( 'time' ),
]);
$stats [ $metric ] = [];
foreach ( $requestDocs as $requestDoc ) {
$stats [ $metric ][] = [
'value' => $requestDoc -> getAttribute ( 'value' ),
'date' => $requestDoc -> getAttribute ( 'time' ),
];
}
// backfill metrics with empty values for graphs
$backfill = $limit - \count ( $requestDocs );
while ( $backfill > 0 ) {
$last = $limit - $backfill - 1 ; // array index of last added metric
$diff = match ( $period ) { // convert period to seconds for unix timestamp math
'1h' => 3600 ,
'1d' => 86400 ,
};
$stats [ $metric ][] = [
'value' => 0 ,
'date' => DateTime :: formatTz ( DateTime :: addSeconds ( new \DateTime ( $stats [ $metric ][ $last ][ 'date' ] ? ? null ), - 1 * $diff )),
];
$backfill -- ;
}
$stats [ $metric ] = array_reverse ( $stats [ $metric ]);
}
});
$usage = new Document ([
'range' => $range ,
'executionsTotal' => $stats [ $metrics [ 0 ]] ? ? [],
'executionsFailure' => $stats [ $metrics [ 1 ]] ? ? [],
'executionsSuccess' => $stats [ $metrics [ 2 ]] ? ? [],
'executionsTime' => $stats [ $metrics [ 3 ]] ? ? [],
'buildsTotal' => $stats [ $metrics [ 4 ]] ? ? [],
'buildsFailure' => $stats [ $metrics [ 5 ]] ? ? [],
'buildsSuccess' => $stats [ $metrics [ 6 ]] ? ? [],
'buildsTime' => $stats [ $metrics [ 7 ]] ? ? [],
]);
2023-06-11 22:29:04 +12:00
}
2023-10-16 06:41:09 +13:00
$response -> dynamic ( $usage , Response :: MODEL_USAGE_FUNCTIONS );
2020-12-27 04:20:08 +13:00
});
2020-10-31 08:53:27 +13:00
2020-07-13 09:18:52 +12:00
App :: put ( '/v1/functions/:functionId' )
-> groups ([ 'api' , 'functions' ])
2023-10-03 03:02:48 +13:00
-> desc ( 'Update function' )
2020-05-06 05:30:12 +12:00
-> label ( 'scope' , 'functions.write' )
2022-04-14 00:39:31 +12:00
-> label ( 'event' , 'functions.[functionId].update' )
2022-09-05 20:00:08 +12:00
-> label ( 'audits.event' , 'function.update' )
2022-08-10 02:38:54 +12:00
-> label ( 'audits.resource' , 'function/{response.$id}' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-06 05:30:12 +12:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'update' )
-> label ( 'sdk.description' , '/docs/references/functions/update-function.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_FUNCTION )
2021-12-11 01:27:11 +13:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2020-09-11 02:40:14 +12:00
-> param ( 'name' , '' , new Text ( 128 ), 'Function name. Max length: 128 chars.' )
2023-09-03 11:37:40 +12:00
-> param ( 'runtime' , '' , new WhiteList ( array_keys ( Config :: getParam ( 'runtimes' )), true ), 'Execution runtime.' , true )
2023-08-23 08:11:33 +12:00
-> 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 )
2023-09-12 15:24:11 +12:00
-> param ( 'events' , [], new ArrayList ( new FunctionEvent (), APP_LIMIT_ARRAY_PARAMS_SIZE ), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.' , true )
2020-09-11 02:40:14 +12:00
-> param ( 'schedule' , '' , new Cron (), 'Schedule CRON syntax.' , true )
2022-01-21 21:05:41 +13:00
-> param ( 'timeout' , 15 , new Range ( 1 , ( int ) App :: getEnv ( '_APP_FUNCTIONS_TIMEOUT' , 900 )), 'Maximum execution time in seconds.' , true )
2023-08-23 08:11:33 +12:00
-> 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 )
2023-08-23 20:16:23 +12:00
-> param ( 'entrypoint' , '' , new Text ( 1028 , 0 ), 'Entrypoint File. This path is relative to the "providerRootDirectory".' , true )
2023-08-18 19:14:13 +12:00
-> param ( 'commands' , '' , new Text ( 8192 , 0 ), 'Build Commands.' , true )
2023-08-23 20:29:01 +12:00
-> param ( 'installationId' , '' , new Text ( 128 , 0 ), 'Appwrite Installation ID for VCS (Version Controle System) deployment.' , true )
2023-08-08 03:37:36 +12:00
-> 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 )
2023-08-23 20:29:01 +12:00
-> 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 )
2023-08-23 08:11:33 +12:00
-> param ( 'providerRootDirectory' , '' , new Text ( 128 , 0 ), 'Path to function code in the linked repo.' , true )
2023-06-22 22:59:41 +12:00
-> inject ( 'request' )
2020-12-27 04:20:08 +13:00
-> inject ( 'response' )
2021-12-28 01:45:23 +13:00
-> inject ( 'dbForProject' )
2021-01-17 12:38:13 +13:00
-> inject ( 'project' )
2022-12-21 05:11:30 +13:00
-> inject ( 'queueForEvents' )
2023-10-09 23:16:05 +13:00
-> inject ( 'queueForBuilds' )
2022-11-04 18:12:08 +13:00
-> inject ( 'dbForConsole' )
2023-08-10 03:53:58 +12:00
-> inject ( 'gitHub' )
2023-10-09 23:16:05 +13:00
-> action ( function ( string $functionId , string $name , string $runtime , array $execute , array $events , string $schedule , int $timeout , bool $enabled , bool $logging , string $entrypoint , string $commands , string $installationId , string $providerRepositoryId , string $providerBranch , bool $providerSilentMode , string $providerRootDirectory , Request $request , Response $response , Database $dbForProject , Document $project , Event $queueForEvents , Build $queueForBuilds , Database $dbForConsole , GitHub $github ) use ( $redeployVcs ) {
2023-05-26 10:29:08 +12:00
// TODO: If only branch changes, re-deploy
2021-01-17 12:38:13 +13:00
2021-12-28 01:45:23 +13:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-07-13 09:18:52 +12:00
2021-06-21 01:59:36 +12:00
if ( $function -> isEmpty ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-13 09:18:52 +12:00
}
2023-08-12 01:34:57 +12:00
$installation = $dbForConsole -> getDocument ( 'installations' , $installationId );
2023-05-22 22:58:13 +12:00
2023-07-30 21:51:13 +12:00
if ( ! empty ( $installationId ) && $installation -> isEmpty ()) {
2023-05-22 22:58:13 +12:00
throw new Exception ( Exception :: INSTALLATION_NOT_FOUND );
}
2023-07-30 21:51:13 +12:00
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".' );
2023-05-22 22:58:13 +12:00
}
if ( $function -> isEmpty ()) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
}
2023-09-03 11:37:40 +12:00
if ( empty ( $runtime )) {
$runtime = $function -> getAttribute ( 'runtime' );
}
2022-09-09 20:49:18 +12:00
$enabled ? ? = $function -> getAttribute ( 'enabled' , true );
2022-09-12 00:20:26 +12:00
2023-07-30 21:51:13 +12:00
$repositoryId = $function -> getAttribute ( 'repositoryId' , '' );
$repositoryInternalId = $function -> getAttribute ( 'repositoryInternalId' , '' );
2023-05-22 22:58:13 +12:00
2023-08-23 01:16:07 +12:00
if ( empty ( $entrypoint )) {
$entrypoint = $function -> getAttribute ( 'entrypoint' , '' );
}
2023-07-30 21:51:13 +12:00
$isConnected = ! empty ( $function -> getAttribute ( 'providerRepositoryId' , '' ));
2023-05-22 22:58:13 +12:00
2023-06-08 03:50:32 +12:00
// Git disconnect logic
2023-07-30 21:51:13 +12:00
if ( $isConnected && empty ( $providerRepositoryId )) {
2023-07-31 07:10:25 +12:00
$repositories = $dbForConsole -> find ( 'repositories' , [
2023-07-01 18:46:21 +12:00
Query :: equal ( 'projectInternalId' , [ $project -> getInternalId ()]),
2023-07-28 19:56:07 +12:00
Query :: equal ( 'resourceInternalId' , [ $function -> getInternalId ()]),
2023-06-10 00:36:33 +12:00
Query :: equal ( 'resourceType' , [ 'function' ]),
Query :: limit ( 100 ),
]);
2023-07-30 21:51:13 +12:00
foreach ( $repositories as $repository ) {
2023-07-31 07:10:25 +12:00
$dbForConsole -> deleteDocument ( 'repositories' , $repository -> getId ());
2023-06-10 00:36:33 +12:00
}
2023-06-08 03:50:32 +12:00
2023-07-30 21:51:13 +12:00
$providerRepositoryId = '' ;
$installationId = '' ;
$providerBranch = '' ;
$providerRootDirectory = '' ;
$providerSilentMode = true ;
$repositoryId = '' ;
$repositoryInternalId = '' ;
2023-06-08 03:50:32 +12:00
}
// Git connect logic
2023-07-30 21:51:13 +12:00
if ( ! $isConnected && ! empty ( $providerRepositoryId )) {
$teamId = $project -> getAttribute ( 'teamId' , '' );
2023-07-31 07:10:25 +12:00
$repository = $dbForConsole -> createDocument ( 'repositories' , new Document ([
2023-05-22 22:58:13 +12:00
'$id' => ID :: unique (),
'$permissions' => [
2023-11-15 04:50:59 +13:00
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' )),
2023-05-22 22:58:13 +12:00
],
2023-07-30 21:51:13 +12:00
'installationId' => $installation -> getId (),
'installationInternalId' => $installation -> getInternalId (),
2023-05-22 22:58:13 +12:00
'projectId' => $project -> getId (),
'projectInternalId' => $project -> getInternalId (),
2023-07-30 21:51:13 +12:00
'providerRepositoryId' => $providerRepositoryId ,
2023-07-28 19:56:07 +12:00
'resourceId' => $function -> getId (),
'resourceInternalId' => $function -> getInternalId (),
2023-06-28 20:48:10 +12:00
'resourceType' => 'function' ,
2023-07-30 21:51:13 +12:00
'providerPullRequestIds' => []
2023-06-08 03:50:32 +12:00
]));
2023-05-22 22:58:13 +12:00
2023-07-30 21:51:13 +12:00
$repositoryId = $repository -> getId ();
$repositoryInternalId = $repository -> getInternalId ();
2023-06-08 03:50:32 +12:00
}
2023-05-22 22:58:13 +12:00
2023-06-22 22:59:41 +12:00
$live = true ;
if (
2023-08-20 21:42:17 +12:00
$function -> getAttribute ( 'name' ) !== $name ||
2023-06-22 22:59:41 +12:00
$function -> getAttribute ( 'entrypoint' ) !== $entrypoint ||
2023-07-21 23:52:14 +12:00
$function -> getAttribute ( 'commands' ) !== $commands ||
2023-08-08 21:46:43 +12:00
$function -> getAttribute ( 'providerRootDirectory' ) !== $providerRootDirectory ||
2023-08-08 21:28:25 +12:00
$function -> getAttribute ( 'runtime' ) !== $runtime
2023-06-22 22:59:41 +12:00
) {
$live = false ;
}
2021-12-28 01:45:23 +13:00
$function = $dbForProject -> updateDocument ( 'functions' , $function -> getId (), new Document ( array_merge ( $function -> getArrayCopy (), [
2021-05-05 09:25:17 +12:00
'execute' => $execute ,
2020-07-13 09:18:52 +12:00
'name' => $name ,
2023-08-08 21:28:25 +12:00
'runtime' => $runtime ,
2020-07-13 09:18:52 +12:00
'events' => $events ,
'schedule' => $schedule ,
2021-01-17 12:38:13 +13:00
'timeout' => $timeout ,
2022-09-09 20:49:18 +12:00
'enabled' => $enabled ,
2023-06-22 22:59:41 +12:00
'live' => $live ,
2023-02-24 22:24:20 +13:00
'logging' => $logging ,
2023-03-16 23:07:15 +13:00
'entrypoint' => $entrypoint ,
2023-07-21 23:52:14 +12:00
'commands' => $commands ,
2023-07-30 21:51:13 +12:00
'installationId' => $installation -> getId (),
'installationInternalId' => $installation -> getInternalId (),
'providerRepositoryId' => $providerRepositoryId ,
'repositoryId' => $repositoryId ,
'repositoryInternalId' => $repositoryInternalId ,
'providerBranch' => $providerBranch ,
'providerRootDirectory' => $providerRootDirectory ,
'providerSilentMode' => $providerSilentMode ,
2023-08-08 21:28:25 +12:00
'search' => implode ( ' ' , [ $functionId , $name , $runtime ]),
2021-05-05 09:25:17 +12:00
])));
2020-05-06 05:30:12 +12:00
2023-06-17 02:43:37 +12:00
// Redeploy logic
2023-07-30 21:51:13 +12:00
if ( ! $isConnected && ! empty ( $providerRepositoryId )) {
2023-10-09 23:16:05 +13:00
$redeployVcs ( $request , $function , $project , $installation , $dbForProject , $queueForBuilds , new Document (), $github );
2023-06-17 02:43:37 +12:00
}
2023-07-28 19:56:07 +12:00
// Inform scheduler if function is still active
2022-11-16 23:33:11 +13:00
$schedule = $dbForConsole -> getDocument ( 'schedules' , $function -> getAttribute ( 'scheduleId' ));
2022-11-16 05:14:52 +13:00
$schedule
2023-06-11 22:29:04 +12:00
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
2022-11-08 04:42:08 +13:00
-> setAttribute ( 'schedule' , $function -> getAttribute ( 'schedule' ))
2022-11-09 03:00:39 +13:00
-> setAttribute ( 'active' , ! empty ( $function -> getAttribute ( 'schedule' )) && ! empty ( $function -> getAttribute ( 'deployment' )));
2022-11-16 21:41:57 +13:00
Authorization :: skip ( fn () => $dbForConsole -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
2022-11-07 23:37:07 +13:00
2022-12-21 05:11:30 +13:00
$queueForEvents -> setParam ( 'functionId' , $function -> getId ());
2022-04-14 00:39:31 +12:00
2020-10-31 08:53:27 +13:00
$response -> dynamic ( $function , Response :: MODEL_FUNCTION );
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
2023-08-21 22:21:18 +12:00
App :: get ( '/v1/functions/:functionId/deployments/:deploymentId/download' )
-> groups ([ 'api' , 'functions' ])
-> desc ( 'Download Deployment' )
-> label ( 'scope' , 'functions.read' )
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'downloadDeployment' )
-> label ( 'sdk.description' , '/docs/references/functions/download-deployment.md' )
-> 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' )
-> inject ( 'deviceFunctions' )
-> action ( function ( string $functionId , string $deploymentId , Response $response , Request $request , Database $dbForProject , Device $deviceFunctions ) {
$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' , '' );
if ( ! $deviceFunctions -> exists ( $path )) {
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-28 03:08:33 +13:00
-> addHeader ( 'Content-Disposition' , 'attachment; filename="' . $deploymentId . '.tar.gz"' );
2023-08-21 22:21:18 +12:00
$size = $deviceFunctions -> getFileSize ( $path );
$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 );
$response -> send ( $deviceFunctions -> read ( $path , $start , ( $end - $start + 1 )));
}
if ( $size > APP_STORAGE_READ_BUFFER ) {
for ( $i = 0 ; $i < ceil ( $size / MAX_OUTPUT_CHUNK_SIZE ); $i ++ ) {
$response -> chunk (
$deviceFunctions -> read (
$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 {
$response -> send ( $deviceFunctions -> read ( $path ));
}
});
2022-02-15 22:16:32 +13:00
App :: patch ( '/v1/functions/:functionId/deployments/:deploymentId' )
2020-07-13 09:18:52 +12:00
-> groups ([ 'api' , 'functions' ])
2023-10-03 03:02:48 +13:00
-> desc ( 'Update function deployment' )
2020-05-07 05:35:56 +12:00
-> label ( 'scope' , 'functions.write' )
2022-04-14 00:39:31 +12:00
-> label ( 'event' , 'functions.[functionId].deployments.[deploymentId].update' )
2022-09-05 20:00:08 +12:00
-> label ( 'audits.event' , 'deployment.update' )
2022-08-13 19:34:03 +12:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-07 05:35:56 +12:00
-> label ( 'sdk.namespace' , 'functions' )
2022-01-25 11:59:02 +13:00
-> label ( 'sdk.method' , 'updateDeployment' )
-> label ( 'sdk.description' , '/docs/references/functions/update-function-deployment.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_FUNCTION )
2021-12-11 01:27:11 +13:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2022-02-15 22:16:32 +13:00
-> param ( 'deploymentId' , '' , new UID (), 'Deployment ID.' )
2020-12-27 04:20:08 +13:00
-> inject ( 'response' )
2021-12-28 01:45:23 +13:00
-> inject ( 'dbForProject' )
2022-12-21 05:11:30 +13:00
-> inject ( 'queueForEvents' )
2022-11-07 10:41:33 +13:00
-> inject ( 'dbForConsole' )
2023-10-03 03:02:48 +13:00
-> action ( function ( string $functionId , string $deploymentId , Response $response , Database $dbForProject , Event $queueForEvents , Database $dbForConsole ) {
2021-01-17 12:38:13 +13:00
2022-01-06 22:45:56 +13:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2022-02-15 22:16:32 +13:00
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
2022-01-27 12:19:02 +13:00
$build = $dbForProject -> getDocument ( 'builds' , $deployment -> getAttribute ( 'buildId' , '' ));
2021-09-06 12:37:20 +12:00
2021-12-09 04:08:53 +13:00
if ( $function -> isEmpty ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2021-12-09 04:08:53 +13:00
}
2021-01-17 12:38:13 +13:00
2022-01-25 11:59:02 +13:00
if ( $deployment -> isEmpty ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
2021-12-09 04:08:53 +13:00
}
2021-09-06 12:37:20 +12:00
2021-12-09 04:08:53 +13:00
if ( $build -> isEmpty ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: BUILD_NOT_FOUND );
2020-05-07 05:35:56 +12:00
}
2021-12-09 04:08:53 +13:00
if ( $build -> getAttribute ( 'status' ) !== 'ready' ) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: BUILD_NOT_READY );
2021-09-06 12:37:20 +12:00
}
2021-12-28 01:45:23 +13:00
$function = $dbForProject -> updateDocument ( 'functions' , $function -> getId (), new Document ( array_merge ( $function -> getArrayCopy (), [
2023-06-11 22:29:04 +12:00
'deploymentInternalId' => $deployment -> getInternalId (),
'deployment' => $deployment -> getId (),
2021-12-09 04:08:53 +13:00
])));
2023-07-28 19:56:07 +12:00
// Inform scheduler if function is still active
2022-11-16 23:33:11 +13:00
$schedule = $dbForConsole -> getDocument ( 'schedules' , $function -> getAttribute ( 'scheduleId' ));
2023-06-11 22:29:04 +12:00
$schedule
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
-> setAttribute ( 'schedule' , $function -> getAttribute ( 'schedule' ))
-> setAttribute ( 'active' , ! empty ( $function -> getAttribute ( 'schedule' )) && ! empty ( $function -> getAttribute ( 'deployment' )));
2022-11-16 03:33:43 +13:00
Authorization :: skip ( fn () => $dbForConsole -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
2022-11-07 10:41:33 +13:00
2022-12-21 05:11:30 +13:00
$queueForEvents
2022-04-14 00:39:31 +12:00
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'deploymentId' , $deployment -> getId ());
2021-12-09 04:08:53 +13:00
$response -> dynamic ( $function , Response :: MODEL_FUNCTION );
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
App :: delete ( '/v1/functions/:functionId' )
-> groups ([ 'api' , 'functions' ])
2023-10-03 03:02:48 +13:00
-> desc ( 'Delete function' )
2020-05-06 05:30:12 +12:00
-> label ( 'scope' , 'functions.write' )
2022-04-14 00:39:31 +12:00
-> label ( 'event' , 'functions.[functionId].delete' )
2022-09-05 20:00:08 +12:00
-> label ( 'audits.event' , 'function.delete' )
2022-08-10 02:38:54 +12:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-06 05:30:12 +12:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'delete' )
-> label ( 'sdk.description' , '/docs/references/functions/delete-function.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
2021-12-11 01:27:11 +13:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2020-12-27 04:20:08 +13:00
-> inject ( 'response' )
2021-12-28 01:45:23 +13:00
-> inject ( 'dbForProject' )
2022-12-21 05:11:30 +13:00
-> inject ( 'queueForDeletes' )
-> inject ( 'queueForEvents' )
2022-11-04 18:12:08 +13:00
-> inject ( 'dbForConsole' )
2023-10-03 03:02:48 +13:00
-> action ( function ( string $functionId , Response $response , Database $dbForProject , Delete $queueForDeletes , Event $queueForEvents , Database $dbForConsole ) {
2020-10-31 08:53:27 +13:00
2021-12-28 01:45:23 +13:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-05-06 05:30:12 +12:00
2021-06-21 01:59:36 +12:00
if ( $function -> isEmpty ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-13 09:18:52 +12:00
}
2020-05-07 00:12:52 +12:00
2021-12-28 01:45:23 +13:00
if ( ! $dbForProject -> deleteDocument ( 'functions' , $function -> getId ())) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'Failed to remove function from DB' );
2020-05-05 02:35:01 +12:00
}
2020-05-06 05:30:12 +12:00
2023-07-28 19:56:07 +12:00
// Inform scheduler to no longer run function
2022-11-16 23:33:11 +13:00
$schedule = $dbForConsole -> getDocument ( 'schedules' , $function -> getAttribute ( 'scheduleId' ));
2022-11-16 05:14:52 +13:00
$schedule
2022-11-07 10:41:33 +13:00
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
2023-06-11 22:29:04 +12:00
-> setAttribute ( 'active' , false );
2022-11-16 03:33:43 +13:00
Authorization :: skip ( fn () => $dbForConsole -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
2022-11-04 18:12:08 +13:00
2022-12-21 05:11:30 +13:00
$queueForDeletes
2022-04-18 08:34:32 +12:00
-> setType ( DELETE_TYPE_DOCUMENT )
-> setDocument ( $function );
2022-04-14 00:39:31 +12:00
2022-12-21 05:11:30 +13:00
$queueForEvents -> setParam ( 'functionId' , $function -> getId ());
2020-10-31 08:53:27 +13:00
2020-07-13 09:18:52 +12:00
$response -> noContent ();
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
2022-01-25 12:05:17 +13:00
App :: post ( '/v1/functions/:functionId/deployments' )
2020-07-13 09:18:52 +12:00
-> groups ([ 'api' , 'functions' ])
2023-10-03 03:02:48 +13:00
-> desc ( 'Create deployment' )
2020-05-06 05:30:12 +12:00
-> label ( 'scope' , 'functions.write' )
2022-04-14 00:39:31 +12:00
-> label ( 'event' , 'functions.[functionId].deployments.[deploymentId].create' )
2022-09-05 20:00:08 +12:00
-> label ( 'audits.event' , 'deployment.create' )
2022-08-13 19:34:03 +12:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-06 05:30:12 +12:00
-> label ( 'sdk.namespace' , 'functions' )
2022-01-25 12:05:17 +13:00
-> label ( 'sdk.method' , 'createDeployment' )
-> label ( 'sdk.description' , '/docs/references/functions/create-deployment.md' )
2021-01-30 03:29:53 +13:00
-> label ( 'sdk.packaging' , true )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.request.type' , 'multipart/form-data' )
2022-07-18 21:43:51 +12:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_ACCEPTED )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
2022-01-25 12:05:17 +13:00
-> label ( 'sdk.response.model' , Response :: MODEL_DEPLOYMENT )
2021-12-11 01:27:11 +13:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2023-08-22 21:47:10 +12:00
-> param ( 'entrypoint' , null , new Text ( 1028 ), 'Entrypoint File.' , true )
-> param ( 'commands' , null , new Text ( 8192 , 0 ), 'Build Commands.' , true )
2023-03-02 01:00:36 +13:00
-> param ( 'code' , [], new File (), 'Gzip file with your code package. When used with the Appwrite CLI, pass the path to your code directory, and the CLI will automatically package your code. Use a path that is within the current directory.' , skipValidation : true )
-> param ( 'activate' , false , new Boolean ( true ), 'Automatically activate the deployment when it is finished building.' )
2020-12-27 04:20:08 +13:00
-> inject ( 'request' )
-> inject ( 'response' )
2021-12-28 01:45:23 +13:00
-> inject ( 'dbForProject' )
2022-12-21 05:11:30 +13:00
-> inject ( 'queueForEvents' )
2021-12-09 04:08:53 +13:00
-> inject ( 'project' )
2021-12-13 21:08:36 +13:00
-> inject ( 'deviceFunctions' )
-> inject ( 'deviceLocal' )
2022-12-21 05:11:30 +13:00
-> inject ( 'queueForBuilds' )
2023-10-16 06:41:09 +13:00
-> action ( function ( string $functionId , ? string $entrypoint , ? string $commands , mixed $code , bool $activate , Request $request , Response $response , Database $dbForProject , Event $queueForEvents , Document $project , Device $deviceFunctions , Device $deviceLocal , Build $queueForBuilds ) {
2023-10-02 06:39:26 +13:00
$activate = filter_var ( $activate , FILTER_VALIDATE_BOOLEAN );
2021-01-17 12:38:13 +13:00
2021-12-28 01:45:23 +13:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-07-13 09:18:52 +12:00
2021-06-21 01:59:36 +12:00
if ( $function -> isEmpty ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-13 09:18:52 +12:00
}
2020-07-15 08:33:52 +12:00
2023-08-22 21:47:10 +12:00
if ( $entrypoint === null ) {
$entrypoint = $function -> getAttribute ( 'entrypoint' , '' );
}
if ( $commands === null ) {
2023-09-03 17:44:10 +12:00
$commands = $function -> getAttribute ( 'commands' , '' );
2023-08-22 21:47:10 +12:00
}
2023-03-29 02:21:42 +13:00
if ( empty ( $entrypoint )) {
2023-03-16 23:07:15 +13:00
throw new Exception ( Exception :: FUNCTION_ENTRYPOINT_MISSING );
}
2021-02-01 08:11:03 +13:00
$file = $request -> getFiles ( 'code' );
2023-06-11 22:29:04 +12:00
// GraphQL multipart spec adds files with index keys
if ( empty ( $file )) {
$file = $request -> getFiles ( 0 );
}
2020-07-15 08:33:52 +12:00
if ( empty ( $file )) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: STORAGE_FILE_EMPTY , 'No file sent' );
2020-07-15 08:33:52 +12:00
}
2023-06-11 22:29:04 +12:00
$fileExt = new FileExt ([ FileExt :: TYPE_GZIP ]);
2023-10-03 03:02:48 +13:00
$fileSizeValidator = new FileSize ( App :: getEnv ( '_APP_FUNCTIONS_SIZE_LIMIT' , '30000000' ));
2023-06-11 22:29:04 +12:00
$upload = new Upload ();
2020-07-15 08:33:52 +12:00
// Make sure we handle a single file and multiple files the same way
2021-09-27 20:01:04 +13:00
$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' ];
2021-11-30 21:06:54 +13:00
$fileSize = ( \is_array ( $file [ 'size' ]) && isset ( $file [ 'size' ][ 0 ])) ? $file [ 'size' ][ 0 ] : $file [ 'size' ];
2020-07-15 08:33:52 +12:00
2021-01-28 07:08:46 +13:00
if ( ! $fileExt -> isValid ( $file [ 'name' ])) { // Check if file type is allowed
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: STORAGE_FILE_TYPE_UNSUPPORTED );
2021-01-27 11:15:20 +13:00
}
2020-07-15 08:33:52 +12:00
2021-09-27 20:01:04 +13:00
$contentRange = $request -> getHeader ( 'content-range' );
2022-08-15 02:22:38 +12:00
$deploymentId = ID :: unique ();
2021-09-27 20:01:04 +13:00
$chunk = 1 ;
$chunks = 1 ;
if ( ! empty ( $contentRange )) {
$start = $request -> getContentRangeStart ();
$end = $request -> getContentRangeEnd ();
2021-11-30 21:06:54 +13:00
$fileSize = $request -> getContentRangeSize ();
2022-02-17 21:26:24 +13:00
$deploymentId = $request -> getHeader ( 'x-appwrite-id' , $deploymentId );
2023-04-27 21:29:39 +12:00
// TODO make `end >= $fileSize` in next breaking version
if ( is_null ( $start ) || is_null ( $end ) || is_null ( $fileSize ) || $end > $fileSize ) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: STORAGE_INVALID_CONTENT_RANGE );
2021-09-27 20:01:04 +13:00
}
2023-04-27 21:29:39 +12:00
// TODO remove the condition that checks `$end === $fileSize` in next breaking version
if ( $end === $fileSize - 1 || $end === $fileSize ) {
2021-09-27 20:01:04 +13:00
//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)
2021-11-30 21:06:54 +13:00
$chunks = ( int ) ceil ( $fileSize / ( $end + 1 - $start ));
2021-11-30 21:01:47 +13:00
$chunk = ( int ) ( $start / ( $end + 1 - $start )) + 1 ;
2021-09-27 20:01:04 +13:00
}
}
2021-11-30 21:06:54 +13:00
if ( ! $fileSizeValidator -> isValid ( $fileSize )) { // Check if file size is exceeding allowed limit
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: STORAGE_INVALID_FILE_SIZE );
2020-07-15 08:33:52 +12:00
}
2021-09-27 20:01:04 +13:00
if ( ! $upload -> isValid ( $fileTmpName )) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: STORAGE_INVALID_FILE );
2020-07-15 08:33:52 +12:00
}
// Save to storage
2021-12-13 21:08:36 +13:00
$fileSize ? ? = $deviceLocal -> getFileSize ( $fileTmpName );
2022-04-14 00:39:31 +12:00
$path = $deviceFunctions -> getPath ( $deploymentId . '.' . \pathinfo ( $fileName , PATHINFO_EXTENSION ));
2022-02-17 21:26:24 +13:00
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
2021-09-27 20:01:04 +13:00
2022-02-18 01:47:26 +13:00
$metadata = [ 'content_type' => $deviceLocal -> getFileMimeType ( $fileTmpName )];
if ( ! $deployment -> isEmpty ()) {
2022-02-17 21:26:24 +13:00
$chunks = $deployment -> getAttribute ( 'chunksTotal' , 1 );
2022-02-18 01:47:26 +13:00
$metadata = $deployment -> getAttribute ( 'metadata' , []);
if ( $chunk === - 1 ) {
2021-11-30 21:40:21 +13:00
$chunk = $chunks ;
2021-09-27 20:01:04 +13:00
}
}
2022-02-18 01:47:26 +13:00
$chunksUploaded = $deviceFunctions -> upload ( $fileTmpName , $path , $chunk , $chunks , $metadata );
2021-09-27 20:01:04 +13:00
if ( empty ( $chunksUploaded )) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'Failed moving file' );
2020-07-15 08:33:52 +12:00
}
2021-12-09 04:08:53 +13:00
2022-03-02 04:09:21 +13:00
$activate = ( bool ) filter_var ( $activate , FILTER_VALIDATE_BOOLEAN );
2022-02-26 03:27:50 +13:00
2022-04-14 00:39:31 +12:00
if ( $chunksUploaded === $chunks ) {
2022-02-26 03:27:50 +13:00
if ( $activate ) {
// Remove deploy for all other deployments.
$activeDeployments = $dbForProject -> find ( 'deployments' , [
2022-08-12 11:53:52 +12:00
Query :: equal ( 'activate' , [ true ]),
Query :: equal ( 'resourceId' , [ $functionId ]),
Query :: equal ( 'resourceType' , [ 'functions' ])
2022-02-26 03:27:50 +13:00
]);
foreach ( $activeDeployments as $activeDeployment ) {
$activeDeployment -> setAttribute ( 'activate' , false );
$dbForProject -> updateDocument ( 'deployments' , $activeDeployment -> getId (), $activeDeployment );
}
}
2022-04-14 00:39:31 +12:00
2021-12-13 21:08:36 +13:00
$fileSize = $deviceFunctions -> getFileSize ( $path );
2021-09-27 20:01:04 +13:00
2022-02-17 21:26:24 +13:00
if ( $deployment -> isEmpty ()) {
$deployment = $dbForProject -> createDocument ( 'deployments' , new Document ([
'$id' => $deploymentId ,
2022-08-02 21:21:53 +12:00
'$permissions' => [
2022-08-14 17:21:11 +12:00
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
2022-08-02 21:21:53 +12:00
],
2023-06-11 22:29:04 +12:00
'resourceInternalId' => $function -> getInternalId (),
2022-02-17 21:26:24 +13:00
'resourceId' => $function -> getId (),
2022-02-19 04:38:48 +13:00
'resourceType' => 'functions' ,
2023-06-11 22:29:04 +12:00
'buildInternalId' => '' ,
2022-02-17 21:26:24 +13:00
'entrypoint' => $entrypoint ,
2023-07-21 23:52:14 +12:00
'commands' => $commands ,
2021-09-27 20:01:04 +13:00
'path' => $path ,
2021-11-30 21:06:54 +13:00
'size' => $fileSize ,
2022-02-17 21:26:24 +13:00
'search' => implode ( ' ' , [ $deploymentId , $entrypoint ]),
2022-02-23 01:27:18 +13:00
'activate' => $activate ,
2022-02-18 01:47:26 +13:00
'metadata' => $metadata ,
2023-05-22 23:29:35 +12:00
'type' => 'manual'
2021-09-27 20:01:04 +13:00
]));
} else {
2022-02-18 01:47:26 +13:00
$deployment = $dbForProject -> updateDocument ( 'deployments' , $deploymentId , $deployment -> setAttribute ( 'size' , $fileSize ) -> setAttribute ( 'metadata' , $metadata ));
2021-09-27 20:01:04 +13:00
}
2022-02-26 00:06:58 +13:00
2022-04-20 01:13:55 +12:00
// Start the build
2022-12-21 05:11:30 +13:00
$queueForBuilds
2022-04-20 01:13:55 +12:00
-> setType ( BUILD_TYPE_DEPLOYMENT )
-> setResource ( $function )
-> setDeployment ( $deployment )
-> setProject ( $project )
-> trigger ();
2021-09-27 20:01:04 +13:00
} else {
2022-04-14 00:39:31 +12:00
if ( $deployment -> isEmpty ()) {
2022-02-17 21:26:24 +13:00
$deployment = $dbForProject -> createDocument ( 'deployments' , new Document ([
'$id' => $deploymentId ,
2022-08-02 21:21:53 +12:00
'$permissions' => [
2022-08-14 17:21:11 +12:00
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
2022-08-02 21:21:53 +12:00
],
2023-06-11 22:29:04 +12:00
'resourceInternalId' => $function -> getInternalId (),
2022-02-17 21:26:24 +13:00
'resourceId' => $function -> getId (),
2022-02-19 04:38:48 +13:00
'resourceType' => 'functions' ,
2023-06-11 22:29:04 +12:00
'buildInternalId' => '' ,
2022-02-17 21:26:24 +13:00
'entrypoint' => $entrypoint ,
2023-07-21 23:52:14 +12:00
'commands' => $commands ,
2021-09-27 20:01:04 +13:00
'path' => $path ,
2022-02-23 01:31:04 +13:00
'size' => $fileSize ,
2021-09-27 20:01:04 +13:00
'chunksTotal' => $chunks ,
'chunksUploaded' => $chunksUploaded ,
2022-02-17 21:26:24 +13:00
'search' => implode ( ' ' , [ $deploymentId , $entrypoint ]),
2022-02-23 01:27:13 +13:00
'activate' => $activate ,
2022-02-18 01:47:26 +13:00
'metadata' => $metadata ,
2023-05-22 23:29:35 +12:00
'type' => 'manual'
2021-09-27 20:01:04 +13:00
]));
} else {
2022-02-18 01:47:26 +13:00
$deployment = $dbForProject -> updateDocument ( 'deployments' , $deploymentId , $deployment -> setAttribute ( 'chunksUploaded' , $chunksUploaded ) -> setAttribute ( 'metadata' , $metadata ));
2021-09-27 20:01:04 +13:00
}
}
2020-05-06 05:30:12 +12:00
2022-02-18 01:47:26 +13:00
$metadata = null ;
2021-12-09 04:08:53 +13:00
2022-12-21 05:11:30 +13:00
$queueForEvents
2022-04-14 00:39:31 +12:00
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'deploymentId' , $deployment -> getId ());
2022-09-07 23:02:36 +12:00
$response
-> setStatusCode ( Response :: STATUS_CODE_ACCEPTED )
-> dynamic ( $deployment , Response :: MODEL_DEPLOYMENT );
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
2022-01-25 12:09:24 +13:00
App :: get ( '/v1/functions/:functionId/deployments' )
2020-07-13 09:18:52 +12:00
-> groups ([ 'api' , 'functions' ])
2023-10-03 03:02:48 +13:00
-> desc ( 'List deployments' )
2020-05-06 05:30:12 +12:00
-> label ( 'scope' , 'functions.read' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-06 05:30:12 +12:00
-> label ( 'sdk.namespace' , 'functions' )
2022-01-25 12:09:24 +13:00
-> label ( 'sdk.method' , 'listDeployments' )
-> label ( 'sdk.description' , '/docs/references/functions/list-deployments.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
2022-01-25 12:09:24 +13:00
-> label ( 'sdk.response.model' , Response :: MODEL_DEPLOYMENT_LIST )
2021-12-11 01:27:11 +13:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2023-03-30 08:38:39 +13: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-11 02:40:14 +12:00
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
2020-12-27 04:20:08 +13:00
-> inject ( 'response' )
2021-12-28 01:45:23 +13:00
-> inject ( 'dbForProject' )
2022-08-23 21:12:48 +12:00
-> action ( function ( string $functionId , array $queries , string $search , Response $response , Database $dbForProject ) {
2021-01-17 12:38:13 +13:00
2021-12-28 01:45:23 +13:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-05-06 05:30:12 +12:00
2021-06-21 01:59:36 +12:00
if ( $function -> isEmpty ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-13 09:18:52 +12:00
}
2021-05-27 22:09:14 +12:00
2022-08-23 21:12:48 +12:00
$queries = Query :: parseQueries ( $queries );
2021-08-07 00:36:05 +12:00
2022-08-12 11:53:52 +12:00
if ( ! empty ( $search )) {
2022-08-23 21:12:48 +12:00
$queries [] = Query :: search ( 'search' , $search );
2021-08-07 00:36:05 +12:00
}
2022-08-23 21:12:48 +12:00
// Set resource queries
$queries [] = Query :: equal ( 'resourceId' , [ $function -> getId ()]);
$queries [] = Query :: equal ( 'resourceType' , [ 'functions' ]);
2021-08-19 01:42:03 +12:00
2022-08-23 21:12:48 +12:00
// Get cursor document if there was a cursor query
2023-08-22 15:25:55 +12:00
$cursor = \array_filter ( $queries , function ( $query ) {
return \in_array ( $query -> getMethod (), [ Query :: TYPE_CURSORAFTER , Query :: TYPE_CURSORBEFORE ]);
});
2022-08-31 11:31:43 +12:00
$cursor = reset ( $cursor );
2022-08-30 23:55:23 +12:00
if ( $cursor ) {
2022-08-23 21:12:48 +12:00
/** @var Query $cursor */
$deploymentId = $cursor -> getValue ();
$cursorDocument = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
2021-08-19 01:42:03 +12:00
2022-08-12 11:53:52 +12:00
if ( $cursorDocument -> isEmpty ()) {
2022-08-23 21:12:48 +12:00
throw new Exception ( Exception :: GENERAL_CURSOR_NOT_FOUND , " Deployment ' { $deploymentId } ' for the 'cursor' value not found. " );
2022-08-12 11:53:52 +12:00
}
2021-08-19 01:42:03 +12:00
2022-08-23 21:12:48 +12:00
$cursor -> setValue ( $cursorDocument );
2021-08-19 01:42:03 +12:00
}
2022-08-23 21:12:48 +12:00
$filterQueries = Query :: groupByType ( $queries )[ 'filters' ];
2021-08-19 01:42:03 +12:00
2022-08-23 21:12:48 +12:00
$results = $dbForProject -> find ( 'deployments' , $queries );
2022-08-12 11:53:52 +12:00
$total = $dbForProject -> count ( 'deployments' , $filterQueries , APP_LIMIT_COUNT );
2020-07-13 09:18:52 +12:00
2022-02-01 00:29:31 +13:00
foreach ( $results as $result ) {
2022-02-17 00:43:21 +13:00
$build = $dbForProject -> getDocument ( 'builds' , $result -> getAttribute ( 'buildId' , '' ));
2022-02-17 05:00:45 +13:00
$result -> setAttribute ( 'status' , $build -> getAttribute ( 'status' , 'processing' ));
2023-08-06 02:50:28 +12:00
$result -> setAttribute ( 'buildLogs' , $build -> getAttribute ( 'logs' , '' ));
2022-10-11 04:24:40 +13:00
$result -> setAttribute ( 'buildTime' , $build -> getAttribute ( 'duration' , 0 ));
2023-06-22 22:59:41 +12:00
$result -> setAttribute ( 'size' , $result -> getAttribute ( 'size' , 0 ) + $build -> getAttribute ( 'size' , 0 ));
2021-12-07 03:12:41 +13:00
}
2020-10-31 08:53:27 +13:00
$response -> dynamic ( new Document ([
2022-01-25 12:09:24 +13:00
'deployments' => $results ,
2022-02-27 22:57:09 +13:00
'total' => $total ,
2022-01-25 12:09:24 +13:00
]), Response :: MODEL_DEPLOYMENT_LIST );
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
2022-01-25 12:11:33 +13:00
App :: get ( '/v1/functions/:functionId/deployments/:deploymentId' )
2020-07-13 09:18:52 +12:00
-> groups ([ 'api' , 'functions' ])
2023-10-03 03:02:48 +13:00
-> desc ( 'Get deployment' )
2020-05-06 05:30:12 +12:00
-> label ( 'scope' , 'functions.read' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-06 05:30:12 +12:00
-> label ( 'sdk.namespace' , 'functions' )
2022-01-25 12:11:33 +13:00
-> label ( 'sdk.method' , 'getDeployment' )
-> label ( 'sdk.description' , '/docs/references/functions/get-deployment.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
2022-07-07 16:49:44 +12:00
-> label ( 'sdk.response.model' , Response :: MODEL_DEPLOYMENT )
2021-12-11 01:27:11 +13:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2022-01-25 12:11:33 +13:00
-> param ( 'deploymentId' , '' , new UID (), 'Deployment ID.' )
2020-12-27 04:20:08 +13:00
-> inject ( 'response' )
2021-12-28 01:45:23 +13:00
-> inject ( 'dbForProject' )
2022-05-25 02:28:27 +12:00
-> action ( function ( string $functionId , string $deploymentId , Response $response , Database $dbForProject ) {
2021-01-17 12:38:13 +13:00
2021-12-28 01:45:23 +13:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-05-06 05:30:12 +12:00
2021-06-21 01:59:36 +12:00
if ( $function -> isEmpty ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-13 09:18:52 +12:00
}
2020-05-06 05:30:12 +12:00
2022-01-25 12:11:33 +13:00
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
2020-05-06 05:30:12 +12:00
2022-01-26 05:51:05 +13:00
if ( $deployment -> getAttribute ( 'resourceId' ) !== $function -> getId ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
2020-07-13 09:18:52 +12:00
}
2020-05-06 05:30:12 +12:00
2022-01-25 12:11:33 +13:00
if ( $deployment -> isEmpty ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
2020-05-06 05:30:12 +12:00
}
2022-10-31 18:11:55 +13:00
$build = $dbForProject -> getDocument ( 'builds' , $deployment -> getAttribute ( 'buildId' , '' ));
2023-05-28 23:39:48 +12:00
$deployment -> setAttribute ( 'status' , $build -> getAttribute ( 'status' , 'waiting' ));
2023-08-06 02:50:28 +12:00
$deployment -> setAttribute ( 'buildLogs' , $build -> getAttribute ( 'logs' , '' ));
2023-06-22 22:59:41 +12:00
$deployment -> setAttribute ( 'buildTime' , $build -> getAttribute ( 'duration' , 0 ));
$deployment -> setAttribute ( 'size' , $deployment -> getAttribute ( 'size' , 0 ) + $build -> getAttribute ( 'size' , 0 ));
2022-10-31 18:11:55 +13:00
2022-01-25 12:11:33 +13:00
$response -> dynamic ( $deployment , Response :: MODEL_DEPLOYMENT );
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
2022-01-25 12:14:21 +13:00
App :: delete ( '/v1/functions/:functionId/deployments/:deploymentId' )
2020-07-13 09:18:52 +12:00
-> groups ([ 'api' , 'functions' ])
2023-10-03 03:02:48 +13:00
-> desc ( 'Delete deployment' )
2020-05-06 05:30:12 +12:00
-> label ( 'scope' , 'functions.write' )
2022-04-14 00:39:31 +12:00
-> label ( 'event' , 'functions.[functionId].deployments.[deploymentId].delete' )
2022-09-05 20:00:08 +12:00
-> label ( 'audits.event' , 'deployment.delete' )
2022-08-13 19:34:03 +12:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-06 05:30:12 +12:00
-> label ( 'sdk.namespace' , 'functions' )
2022-01-25 12:14:21 +13:00
-> label ( 'sdk.method' , 'deleteDeployment' )
-> label ( 'sdk.description' , '/docs/references/functions/delete-deployment.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
2021-12-11 01:27:11 +13:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2022-01-25 12:14:21 +13:00
-> param ( 'deploymentId' , '' , new UID (), 'Deployment ID.' )
2020-12-27 04:20:08 +13:00
-> inject ( 'response' )
2021-12-28 01:45:23 +13:00
-> inject ( 'dbForProject' )
2022-12-21 05:11:30 +13:00
-> inject ( 'queueForDeletes' )
-> inject ( 'queueForEvents' )
2021-12-13 21:08:36 +13:00
-> inject ( 'deviceFunctions' )
2022-12-21 05:11:30 +13:00
-> action ( function ( string $functionId , string $deploymentId , Response $response , Database $dbForProject , Delete $queueForDeletes , Event $queueForEvents , Device $deviceFunctions ) {
2021-01-17 12:38:13 +13:00
2021-12-28 01:45:23 +13:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2021-06-21 01:59:36 +12:00
if ( $function -> isEmpty ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-13 09:18:52 +12:00
}
2022-04-14 00:39:31 +12:00
2022-01-25 12:14:21 +13:00
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
if ( $deployment -> isEmpty ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
2021-09-13 22:50:45 +12:00
}
2022-02-01 12:44:55 +13:00
if ( $deployment -> getAttribute ( 'resourceId' ) !== $function -> getId ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
2021-09-13 22:50:45 +12:00
}
2023-06-14 20:57:30 +12: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' , '' ))) {
if ( ! ( $deviceFunctions -> delete ( $deployment -> getAttribute ( 'path' , '' )))) {
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'Failed to remove deployment from storage' );
2020-07-19 09:48:28 +12:00
}
2020-05-06 05:30:12 +12:00
}
2022-04-14 00:39:31 +12:00
if ( $function -> getAttribute ( 'deployment' ) === $deployment -> getId ()) { // Reset function deployment
2021-12-28 01:45:23 +13:00
$function = $dbForProject -> updateDocument ( 'functions' , $function -> getId (), new Document ( array_merge ( $function -> getArrayCopy (), [
2022-01-25 12:14:21 +13:00
'deployment' => '' ,
2023-06-12 02:08:48 +12:00
'deploymentInternalId' => '' ,
2021-05-05 09:25:17 +12:00
])));
2020-10-31 08:53:27 +13:00
}
2022-12-21 05:11:30 +13:00
$queueForEvents
2022-04-14 00:39:31 +12:00
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'deploymentId' , $deployment -> getId ());
2020-07-19 09:48:28 +12:00
2022-12-21 05:11:30 +13:00
$queueForDeletes
2022-04-18 08:34:32 +12:00
-> setType ( DELETE_TYPE_DOCUMENT )
-> setDocument ( $deployment );
2022-01-28 14:29:20 +13:00
2020-07-13 09:18:52 +12:00
$response -> noContent ();
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
2022-09-29 21:42:46 +13:00
App :: post ( '/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId' )
-> groups ([ 'api' , 'functions' ])
2023-10-03 03:02:48 +13:00
-> desc ( 'Create build' )
2022-09-29 21:42:46 +13: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.description' , '/docs/references/functions/create-build.md' )
-> 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.' )
-> param ( 'buildId' , '' , new UID (), 'Build unique ID.' )
2023-06-22 22:59:41 +12:00
-> inject ( 'request' )
2022-09-29 21:42:46 +13:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
-> inject ( 'project' )
2022-12-21 05:11:30 +13:00
-> inject ( 'queueForEvents' )
-> inject ( 'queueForBuilds' )
2023-09-28 04:51:17 +13:00
-> action ( function ( string $functionId , string $deploymentId , string $buildId , Request $request , Response $response , Database $dbForProject , Document $project , Event $queueForEvents , Build $queueForBuilds ) {
2022-09-29 21:42:46 +13:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
}
2023-08-19 18:15:47 +12:00
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
2022-09-29 21:42:46 +13:00
if ( $deployment -> isEmpty ()) {
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
}
$build = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'builds' , $buildId ));
if ( $build -> isEmpty ()) {
throw new Exception ( Exception :: BUILD_NOT_FOUND );
}
2023-08-20 21:43:05 +12:00
$deploymentId = ID :: unique ();
2022-09-29 21:42:46 +13:00
2023-09-14 07:18:50 +12:00
$deployment -> removeAttribute ( '$internalId' );
2023-08-19 18:15:47 +12:00
$deployment = $dbForProject -> createDocument ( 'deployments' , $deployment -> setAttributes ([
2023-08-20 21:43:05 +12:00
'$id' => $deploymentId ,
2023-08-19 18:15:47 +12:00
'buildId' => '' ,
'buildInternalId' => '' ,
'entrypoint' => $function -> getAttribute ( 'entrypoint' ),
'commands' => $function -> getAttribute ( 'commands' , '' ),
2023-08-20 21:43:05 +12:00
'search' => implode ( ' ' , [ $deploymentId , $function -> getAttribute ( 'entrypoint' )]),
2023-08-19 18:15:47 +12:00
]));
2022-09-29 21:42:46 +13:00
2022-12-21 05:11:30 +13:00
$queueForBuilds
2023-08-19 18:15:47 +12:00
-> setType ( BUILD_TYPE_DEPLOYMENT )
2022-09-29 21:42:46 +13:00
-> setResource ( $function )
-> setDeployment ( $deployment )
-> setProject ( $project )
-> trigger ();
2023-09-28 04:51:17 +13:00
$queueForEvents
2023-08-18 09:37:52 +12:00
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'deploymentId' , $deployment -> getId ());
2022-09-29 21:42:46 +13:00
$response -> noContent ();
});
2020-07-13 09:18:52 +12:00
App :: post ( '/v1/functions/:functionId/executions' )
-> groups ([ 'api' , 'functions' ])
2023-10-03 03:02:48 +13:00
-> desc ( 'Create execution' )
2020-12-30 20:26:01 +13:00
-> label ( 'scope' , 'execution.write' )
2022-04-14 00:39:31 +12:00
-> label ( 'event' , 'functions.[functionId].executions.[executionId].create' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
2020-05-06 05:30:12 +12:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'createExecution' )
-> label ( 'sdk.description' , '/docs/references/functions/create-execution.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_CREATED )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_EXECUTION )
2021-12-11 01:27:11 +13:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2023-10-03 03:02:48 +13:00
-> param ( 'body' , '' , new Text ( 0 , 0 ), 'HTTP body of execution. Default value is empty string.' , true )
2022-09-07 20:24:49 +12:00
-> param ( 'async' , false , new Boolean (), 'Execute code in the background. Default value is false.' , true )
2023-09-06 20:16:01 +12:00
-> param ( 'path' , '/' , new Text ( 2048 ), 'HTTP path of execution. Path can include query params. Default value is /' , true )
2023-07-01 18:46:21 +12:00
-> param ( 'method' , 'POST' , new Whitelist ([ 'GET' , 'POST' , 'PUT' , 'PATCH' , 'DELETE' , 'OPTIONS' ], true ), 'HTTP method of execution. Default value is GET.' , true )
2023-08-18 18:55:44 +12:00
-> param ( 'headers' , [], new Assoc (), 'HTTP headers of execution. Defaults to empty.' , true )
2020-12-27 04:20:08 +13:00
-> inject ( 'response' )
-> inject ( 'project' )
2021-12-28 01:45:23 +13:00
-> inject ( 'dbForProject' )
2021-03-11 05:58:46 +13:00
-> inject ( 'user' )
2022-12-21 05:11:30 +13:00
-> inject ( 'queueForEvents' )
2023-10-16 06:41:09 +13:00
-> inject ( 'usage' )
2022-09-09 20:49:18 +12:00
-> inject ( 'mode' )
2022-11-16 23:33:11 +13:00
-> inject ( 'queueForFunctions' )
2023-06-22 22:59:41 +12:00
-> inject ( 'geodb' )
2023-10-16 06:41:09 +13:00
-> action ( function ( string $functionId , string $body , bool $async , string $path , string $method , array $headers , Response $response , Document $project , Database $dbForProject , Document $user , Event $queueForEvents , Stats $usage , string $mode , Func $queueForFunctions , Reader $geodb ) {
2020-07-17 09:50:37 +12:00
2022-04-14 00:39:31 +12:00
$function = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'functions' , $functionId ));
2020-07-13 09:18:52 +12:00
2023-08-17 09:58:25 +12:00
$isAPIKey = Auth :: isAppUser ( Authorization :: getRoles ());
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: getRoles ());
2023-08-01 06:24:21 +12:00
2023-08-17 09:58:25 +12:00
if ( $function -> isEmpty () || ( ! $function -> getAttribute ( 'enabled' ) && ! $isAPIKey && ! $isPrivilegedUser )) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-13 09:18:52 +12:00
}
2020-07-17 09:50:37 +12:00
2023-09-05 05:53:25 +12:00
$version = $function -> getAttribute ( 'version' , 'v2' );
$runtimes = Config :: getParam ( $version === 'v2' ? 'runtimes-v2' : 'runtimes' , []);
2022-02-06 08:49:57 +13:00
$runtime = ( isset ( $runtimes [ $function -> getAttribute ( 'runtime' , '' )])) ? $runtimes [ $function -> getAttribute ( 'runtime' , '' )] : null ;
2022-02-04 14:29:40 +13:00
if ( \is_null ( $runtime )) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: FUNCTION_RUNTIME_UNSUPPORTED , 'Runtime "' . $function -> getAttribute ( 'runtime' , '' ) . '" is not supported' );
2022-02-04 14:29:40 +13:00
}
2022-04-14 00:39:31 +12:00
$deployment = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'deployments' , $function -> getAttribute ( 'deployment' , '' )));
2020-07-17 09:50:37 +12:00
2022-01-26 05:51:05 +13:00
if ( $deployment -> getAttribute ( 'resourceId' ) !== $function -> getId ()) {
2022-08-14 18:56:12 +12:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND , 'Deployment not found. Create a deployment before trying to execute a function' );
2020-07-17 09:50:37 +12:00
}
2022-01-25 12:16:53 +13:00
if ( $deployment -> isEmpty ()) {
2022-08-14 18:56:12 +12:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND , 'Deployment not found. Create a deployment before trying to execute a function' );
2020-07-17 09:50:37 +12:00
}
2020-12-30 12:00:44 +13:00
2022-02-03 13:05:03 +13:00
/** Check if build has completed */
2022-04-14 00:39:31 +12:00
$build = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'builds' , $deployment -> getAttribute ( 'buildId' , '' )));
2022-02-03 13:05:03 +13:00
if ( $build -> isEmpty ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: BUILD_NOT_FOUND );
2022-02-03 13:05:03 +13:00
}
if ( $build -> getAttribute ( 'status' ) !== 'ready' ) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: BUILD_NOT_READY );
2022-02-03 13:05:03 +13:00
}
2022-01-18 19:38:10 +13:00
$validator = new Authorization ( 'execute' );
2020-12-30 12:00:44 +13:00
2021-05-05 09:25:17 +12:00
if ( ! $validator -> isValid ( $function -> getAttribute ( 'execute' ))) { // Check if user has write access to execute function
2022-07-27 02:56:59 +12:00
throw new Exception ( Exception :: USER_UNAUTHORIZED , $validator -> getDescription ());
2020-12-30 12:00:44 +13:00
}
2021-03-11 09:25:54 +13:00
$jwt = '' ; // initialize
2021-06-21 01:59:36 +12:00
if ( ! $user -> isEmpty ()) { // If userId exists, generate a JWT for function
2021-07-23 02:49:52 +12:00
$sessions = $user -> getAttribute ( 'sessions' , []);
$current = new Document ();
2021-03-11 05:58:46 +13:00
2022-04-14 00:39:31 +12:00
foreach ( $sessions as $session ) {
/** @var Utopia\Database\Document $session */
2021-07-23 02:49:52 +12:00
if ( $session -> getAttribute ( 'secret' ) == Auth :: hash ( Auth :: $secret )) { // If current session delete the cookies too
$current = $session ;
2021-03-11 05:58:46 +13:00
}
}
2022-04-14 00:39:31 +12:00
if ( ! $current -> isEmpty ()) {
2021-03-11 09:25:54 +13:00
$jwtObj = new JWT ( App :: getEnv ( '_APP_OPENSSL_KEY_V1' ), 'HS256' , 900 , 10 ); // Instantiate with key, algo, maxAge and leeway.
$jwt = $jwtObj -> encode ([
2021-03-11 06:48:05 +13:00
'userId' => $user -> getId (),
2021-07-23 02:49:52 +12:00
'sessionId' => $current -> getId (),
2021-03-11 06:48:05 +13:00
]);
2021-03-11 05:58:46 +13:00
}
}
2023-06-22 22:59:41 +12:00
$headers [ 'x-appwrite-trigger' ] = 'http' ;
$headers [ 'x-appwrite-user-id' ] = $user -> getId () ? ? '' ;
$headers [ 'x-appwrite-user-jwt' ] = $jwt ? ? '' ;
$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 19:01:51 +12:00
$headers [ 'x-appwrite-country-code' ] = $record [ 'country' ][ 'iso_code' ] ? ? '' ;
2023-06-22 22:59:41 +12: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 19:17:23 +12:00
$headersFiltered = [];
foreach ( $headers as $key => $value ) {
2023-08-12 01:34:57 +12:00
if ( \in_array ( \strtolower ( $key ), FUNCTION_ALLOWLIST_HEADERS_REQUEST )) {
2023-08-10 03:53:58 +12:00
$headersFiltered [] = [ 'name' => $key , 'value' => $value ];
2023-07-30 19:17:23 +12:00
}
}
2023-07-30 04:20:20 +12:00
$executionId = ID :: unique ();
$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 (),
'trigger' => 'http' , // http / schedule / event
'status' => $async ? 'waiting' : 'processing' , // waiting / processing / completed / failed
'responseStatusCode' => 0 ,
'responseHeaders' => [],
'requestPath' => $path ,
'requestMethod' => $method ,
2023-07-30 19:17:23 +12:00
'requestHeaders' => $headersFiltered ,
2023-07-30 04:20:20 +12:00
'errors' => '' ,
'logs' => '' ,
'duration' => 0.0 ,
'search' => implode ( ' ' , [ $functionId , $executionId ]),
]);
2022-12-21 05:11:30 +13:00
$queueForEvents
2022-04-14 00:39:31 +12:00
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'executionId' , $execution -> getId ())
2022-06-22 22:51:49 +12:00
-> setContext ( 'function' , $function );
2022-04-14 00:39:31 +12:00
2021-08-24 21:32:27 +12:00
if ( $async ) {
2023-07-28 19:56:07 +12:00
if ( $function -> getAttribute ( 'logging' )) {
/** @var Document $execution */
$execution = Authorization :: skip ( fn () => $dbForProject -> createDocument ( 'executions' , $execution ));
}
2022-11-16 23:33:11 +13:00
$queueForFunctions
2022-11-16 07:13:17 +13:00
-> setType ( 'http' )
-> setExecution ( $execution )
-> setFunction ( $function )
2023-02-15 00:01:38 +13:00
-> setBody ( $body )
-> setHeaders ( $headers )
-> setPath ( $path )
-> setMethod ( $method )
2022-11-16 07:13:17 +13:00
-> setJWT ( $jwt )
-> setProject ( $project )
2022-11-17 01:44:14 +13:00
-> setUser ( $user )
2023-11-07 23:25:33 +13:00
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'executionId' , $execution -> getId ())
2022-11-17 01:44:14 +13:00
-> trigger ();
2020-07-13 09:18:52 +12:00
2022-09-07 23:02:36 +12:00
return $response
-> setStatusCode ( Response :: STATUS_CODE_ACCEPTED )
-> dynamic ( $execution , Response :: MODEL_EXECUTION );
2021-08-24 21:32:27 +12:00
}
2022-01-11 03:18:33 +13:00
2023-03-29 02:21:42 +13:00
$durationStart = \microtime ( true );
2022-08-11 01:43:05 +12:00
2023-03-12 05:06:02 +13:00
$vars = [];
2023-09-11 22:22:16 +12: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-12 05:06:02 +13:00
// Shared vars
2023-09-05 20:21:36 +12:00
foreach ( $function -> getAttribute ( 'varsProject' , []) as $var ) {
2023-08-18 18:55:44 +12:00
$vars [ $var -> getAttribute ( 'key' )] = $var -> getAttribute ( 'value' , '' );
}
2022-08-11 01:43:05 +12:00
2023-03-12 05:06:02 +13:00
// Function vars
2023-09-05 20:21:36 +12:00
foreach ( $function -> getAttribute ( 'vars' , []) as $var ) {
$vars [ $var -> getAttribute ( 'key' )] = $var -> getAttribute ( 'value' , '' );
}
2023-03-12 05:06:02 +13:00
// Appwrite vars
2022-08-11 01:43:05 +12:00
$vars = \array_merge ( $vars , [
2023-06-22 22:59:41 +12:00
'APPWRITE_FUNCTION_ID' => $functionId ,
2022-09-30 22:52:35 +13:00
'APPWRITE_FUNCTION_NAME' => $function -> getAttribute ( 'name' ),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment -> getId (),
'APPWRITE_FUNCTION_PROJECT_ID' => $project -> getId (),
2022-09-19 23:58:41 +12:00
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime [ 'name' ] ? ? '' ,
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime [ 'version' ] ? ? '' ,
2022-02-04 14:29:40 +13:00
]);
2022-02-03 13:05:03 +13:00
2022-02-06 08:49:57 +13:00
/** Execute function */
2022-04-14 04:15:25 +12:00
$executor = new Executor ( App :: getEnv ( '_APP_EXECUTOR_HOST' ));
2022-02-16 06:39:03 +13:00
try {
2023-09-05 05:53:25 +12:00
$version = $function -> getAttribute ( 'version' , 'v2' );
2023-07-24 18:23:23 +12:00
$command = $runtime [ 'startCommand' ];
2023-09-05 05:53:25 +12:00
$command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"' ;
2022-02-16 06:39:03 +13:00
$executionResponse = $executor -> createExecution (
projectId : $project -> getId (),
deploymentId : $deployment -> getId (),
2023-06-22 22:59:41 +12:00
body : \strlen ( $body ) > 0 ? $body : null ,
2022-11-08 21:49:45 +13:00
variables : $vars ,
2022-02-16 06:39:03 +13:00
timeout : $function -> getAttribute ( 'timeout' , 0 ),
2022-11-08 21:49:45 +13:00
image : $runtime [ 'image' ],
2023-03-15 19:08:43 +13:00
source : $build -> getAttribute ( 'path' , '' ),
2022-02-16 06:39:03 +13:00
entrypoint : $deployment -> getAttribute ( 'entrypoint' , '' ),
2023-09-05 05:53:25 +12:00
version : $version ,
2023-02-15 00:01:38 +13:00
path : $path ,
method : $method ,
headers : $headers ,
2023-10-16 06:41:09 +13:00
runtimeEntrypoint : $command ,
requestTimeout : 30
2022-02-16 06:39:03 +13:00
);
2023-07-30 19:17:23 +12:00
$headersFiltered = [];
foreach ( $executionResponse [ 'headers' ] as $key => $value ) {
2023-08-12 01:34:57 +12:00
if ( \in_array ( \strtolower ( $key ), FUNCTION_ALLOWLIST_HEADERS_RESPONSE )) {
2023-08-10 03:53:58 +12:00
$headersFiltered [] = [ 'name' => $key , 'value' => $value ];
2023-07-30 19:17:23 +12:00
}
}
2023-07-30 04:20:20 +12:00
2022-02-16 06:39:03 +13:00
/** Update execution status */
2023-03-07 00:16:34 +13:00
$status = $executionResponse [ 'statusCode' ] >= 400 ? 'failed' : 'completed' ;
2023-02-03 08:21:00 +13:00
$execution -> setAttribute ( 'status' , $status );
2023-07-30 04:20:20 +12:00
$execution -> setAttribute ( 'responseStatusCode' , $executionResponse [ 'statusCode' ]);
2023-07-30 19:17:23 +12:00
$execution -> setAttribute ( 'responseHeaders' , $headersFiltered );
2023-02-15 00:01:38 +13:00
$execution -> setAttribute ( 'logs' , $executionResponse [ 'logs' ]);
$execution -> setAttribute ( 'errors' , $executionResponse [ 'errors' ]);
2022-09-10 00:02:04 +12:00
$execution -> setAttribute ( 'duration' , $executionResponse [ 'duration' ]);
2022-02-16 06:39:03 +13:00
} catch ( \Throwable $th ) {
2023-03-29 02:21:42 +13:00
$durationEnd = \microtime ( true );
2022-07-03 02:25:44 +12:00
$execution
2023-03-29 02:21:42 +13:00
-> setAttribute ( 'duration' , $durationEnd - $durationStart )
2022-07-03 02:25:44 +12:00
-> setAttribute ( 'status' , 'failed' )
2023-07-30 04:20:20 +12:00
-> setAttribute ( 'responseStatusCode' , 500 )
2023-02-15 21:36:20 +13:00
-> setAttribute ( 'errors' , $th -> getMessage () . '\nError Code: ' . $th -> getCode ());
2022-02-16 06:39:03 +13:00
Console :: error ( $th -> getMessage ());
}
2022-02-04 14:29:40 +13:00
2023-10-06 02:15:39 +13:00
if ( $function -> getAttribute ( 'logging' )) {
/** @var Document $execution */
$execution = Authorization :: skip ( fn () => $dbForProject -> createDocument ( 'executions' , $execution ));
}
2023-10-16 06:41:09 +13:00
// TODO revise this later using route label
$usage
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'executions.{scope}.compute' , 1 )
-> setParam ( 'executionStatus' , $execution -> getAttribute ( 'status' , '' ))
-> setParam ( 'executionTime' , $execution -> getAttribute ( 'duration' )); // ms
2022-08-14 20:44:17 +12:00
$roles = Authorization :: getRoles ();
$isPrivilegedUser = Auth :: isPrivilegedUser ( $roles );
$isAppUser = Auth :: isAppUser ( $roles );
2022-09-06 12:28:57 +12:00
2022-08-16 17:23:51 +12:00
if ( ! $isPrivilegedUser && ! $isAppUser ) {
2023-02-15 00:01:38 +13:00
$execution -> setAttribute ( 'logs' , '' );
$execution -> setAttribute ( 'errors' , '' );
2022-08-16 16:34:49 +12:00
}
2022-07-13 20:57:57 +12:00
2023-08-07 01:11:30 +12:00
$headers = [];
2023-08-16 18:19:42 +12:00
foreach (( $executionResponse [ 'headers' ] ? ? []) as $key => $value ) {
2023-08-10 03:53:58 +12:00
$headers [] = [ 'name' => $key , 'value' => $value ];
2023-08-07 01:11:30 +12:00
}
2023-08-16 18:19:42 +12:00
$execution -> setAttribute ( 'responseBody' , $executionResponse [ 'body' ] ? ? '' );
2023-08-07 01:11:30 +12:00
$execution -> setAttribute ( 'responseHeaders' , $headers );
2023-02-27 23:20:37 +13:00
2022-01-11 03:18:33 +13:00
$response
2022-01-27 12:19:02 +13:00
-> setStatusCode ( Response :: STATUS_CODE_CREATED )
2022-02-25 02:22:20 +13:00
-> dynamic ( $execution , Response :: MODEL_EXECUTION );
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
App :: get ( '/v1/functions/:functionId/executions' )
-> groups ([ 'api' , 'functions' ])
2023-10-03 03:02:48 +13:00
-> desc ( 'List executions' )
2020-12-30 20:26:01 +13:00
-> label ( 'scope' , 'execution.read' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
2020-05-06 05:30:12 +12:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'listExecutions' )
-> label ( 'sdk.description' , '/docs/references/functions/list-executions.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_EXECUTION_LIST )
2021-12-11 01:27:11 +13:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2023-03-30 08:38:39 +13: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 23:12:42 +13:00
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
2020-12-27 04:20:08 +13:00
-> inject ( 'response' )
2021-12-28 01:45:23 +13:00
-> inject ( 'dbForProject' )
2022-09-09 20:49:18 +12:00
-> inject ( 'mode' )
-> action ( function ( string $functionId , array $queries , string $search , Response $response , Database $dbForProject , string $mode ) {
2022-04-14 00:39:31 +12:00
$function = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'functions' , $functionId ));
2020-05-06 05:30:12 +12:00
2023-08-17 09:58:25 +12:00
$isAPIKey = Auth :: isAppUser ( Authorization :: getRoles ());
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: getRoles ());
2023-08-01 06:24:21 +12:00
2023-08-17 09:58:25 +12:00
if ( $function -> isEmpty () || ( ! $function -> getAttribute ( 'enabled' ) && ! $isAPIKey && ! $isPrivilegedUser )) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-13 09:18:52 +12:00
}
2021-05-27 22:09:14 +12:00
2022-08-23 21:26:34 +12:00
$queries = Query :: parseQueries ( $queries );
2021-08-07 00:36:05 +12:00
2022-08-12 11:53:52 +12:00
if ( ! empty ( $search )) {
2022-08-23 21:26:34 +12:00
$queries [] = Query :: search ( 'search' , $search );
2021-08-07 00:36:05 +12:00
}
2022-08-23 21:26:34 +12:00
// Set internal queries
$queries [] = Query :: equal ( 'functionId' , [ $function -> getId ()]);
2021-09-27 23:12:42 +13:00
2022-08-23 21:26:34 +12:00
// Get cursor document if there was a cursor query
2023-08-22 15:25:55 +12:00
$cursor = \array_filter ( $queries , function ( $query ) {
return \in_array ( $query -> getMethod (), [ Query :: TYPE_CURSORAFTER , Query :: TYPE_CURSORBEFORE ]);
});
2022-08-31 11:31:43 +12:00
$cursor = reset ( $cursor );
2022-08-30 23:55:23 +12:00
if ( $cursor ) {
2022-08-23 21:26:34 +12:00
/** @var Query $cursor */
$executionId = $cursor -> getValue ();
$cursorDocument = $dbForProject -> getDocument ( 'executions' , $executionId );
2021-08-07 00:36:05 +12:00
2022-08-12 11:53:52 +12:00
if ( $cursorDocument -> isEmpty ()) {
2022-08-23 21:26:34 +12:00
throw new Exception ( Exception :: GENERAL_CURSOR_NOT_FOUND , " Execution ' { $executionId } ' for the 'cursor' value not found. " );
2021-08-07 00:36:05 +12:00
}
2022-08-23 21:26:34 +12:00
$cursor -> setValue ( $cursorDocument );
2021-09-27 23:12:42 +13:00
}
2022-08-23 21:26:34 +12:00
$filterQueries = Query :: groupByType ( $queries )[ 'filters' ];
$results = $dbForProject -> find ( 'executions' , $queries );
2022-08-12 11:53:52 +12:00
$total = $dbForProject -> count ( 'executions' , $filterQueries , APP_LIMIT_COUNT );
2020-07-13 09:18:52 +12:00
2022-08-14 20:54:11 +12:00
$roles = Authorization :: getRoles ();
$isPrivilegedUser = Auth :: isPrivilegedUser ( $roles );
$isAppUser = Auth :: isAppUser ( $roles );
2022-08-16 16:34:49 +12:00
if ( ! $isPrivilegedUser && ! $isAppUser ) {
2022-08-14 20:54:11 +12:00
$results = array_map ( function ( $execution ) {
2023-02-15 00:01:38 +13:00
$execution -> setAttribute ( 'logs' , '' );
$execution -> setAttribute ( 'errors' , '' );
2022-08-14 21:23:41 +12:00
return $execution ;
2022-08-14 20:54:11 +12:00
}, $results );
}
2020-07-13 09:18:52 +12:00
2020-10-31 08:53:27 +13:00
$response -> dynamic ( new Document ([
2021-05-27 22:09:14 +12:00
'executions' => $results ,
2022-02-27 22:57:09 +13:00
'total' => $total ,
2020-10-31 08:53:27 +13:00
]), Response :: MODEL_EXECUTION_LIST );
2020-12-27 04:20:08 +13:00
});
2020-07-13 09:18:52 +12:00
App :: get ( '/v1/functions/:functionId/executions/:executionId' )
-> groups ([ 'api' , 'functions' ])
2023-10-03 03:02:48 +13:00
-> desc ( 'Get execution' )
2020-12-30 20:26:01 +13:00
-> label ( 'scope' , 'execution.read' )
2021-04-16 19:22:17 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_SESSION , APP_AUTH_TYPE_KEY , APP_AUTH_TYPE_JWT ])
2020-05-06 05:30:12 +12:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'getExecution' )
-> label ( 'sdk.description' , '/docs/references/functions/get-execution.md' )
2020-11-12 10:02:24 +13:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_EXECUTION )
2021-12-11 01:27:11 +13:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
-> param ( 'executionId' , '' , new UID (), 'Execution ID.' )
2020-12-27 04:20:08 +13:00
-> inject ( 'response' )
2021-12-28 01:45:23 +13:00
-> inject ( 'dbForProject' )
2022-09-09 20:49:18 +12:00
-> inject ( 'mode' )
-> action ( function ( string $functionId , string $executionId , Response $response , Database $dbForProject , string $mode ) {
2022-04-14 00:39:31 +12:00
$function = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'functions' , $functionId ));
2020-05-06 05:30:12 +12:00
2023-08-17 09:58:25 +12:00
$isAPIKey = Auth :: isAppUser ( Authorization :: getRoles ());
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: getRoles ());
2023-08-01 06:24:21 +12:00
2023-08-17 09:58:25 +12:00
if ( $function -> isEmpty () || ( ! $function -> getAttribute ( 'enabled' ) && ! $isAPIKey && ! $isPrivilegedUser )) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-13 09:18:52 +12:00
}
2020-05-06 05:30:12 +12:00
2021-12-28 01:45:23 +13:00
$execution = $dbForProject -> getDocument ( 'executions' , $executionId );
2020-05-06 05:30:12 +12:00
2020-10-28 08:46:15 +13:00
if ( $execution -> getAttribute ( 'functionId' ) !== $function -> getId ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: EXECUTION_NOT_FOUND );
2020-07-13 09:18:52 +12:00
}
2020-05-06 05:30:12 +12:00
2021-06-21 01:59:36 +12:00
if ( $execution -> isEmpty ()) {
2022-07-27 02:24:32 +12:00
throw new Exception ( Exception :: EXECUTION_NOT_FOUND );
2020-05-06 05:30:12 +12:00
}
2020-07-13 09:18:52 +12:00
2022-08-14 20:44:17 +12:00
$roles = Authorization :: getRoles ();
$isPrivilegedUser = Auth :: isPrivilegedUser ( $roles );
$isAppUser = Auth :: isAppUser ( $roles );
2022-08-16 17:23:51 +12:00
if ( ! $isPrivilegedUser && ! $isAppUser ) {
2023-02-15 00:01:38 +13:00
$execution -> setAttribute ( 'logs' , '' );
$execution -> setAttribute ( 'errors' , '' );
2020-05-06 05:30:12 +12:00
}
2020-07-13 09:18:52 +12:00
2020-10-31 08:53:27 +13:00
$response -> dynamic ( $execution , Response :: MODEL_EXECUTION );
2020-12-27 04:20:08 +13:00
});
2021-12-07 03:12:41 +13:00
2022-08-02 03:13:47 +12:00
// Variables
2022-08-10 03:29:24 +12:00
App :: post ( '/v1/functions/:functionId/variables' )
2023-10-03 03:02:48 +13:00
-> desc ( 'Create variable' )
2022-08-02 03:13:47 +12:00
-> groups ([ 'api' , 'functions' ])
-> label ( 'scope' , 'functions.write' )
2022-09-05 20:00:08 +12:00
-> label ( 'audits.event' , 'variable.create' )
2022-09-04 20:13:44 +12:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2022-08-04 01:32:50 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2022-08-02 03:13:47 +12:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'createVariable' )
2022-08-10 00:11:50 +12:00
-> label ( 'sdk.description' , '/docs/references/functions/create-variable.md' )
2022-08-02 03:13:47 +12: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 22:05:42 +12:00
-> param ( 'functionId' , '' , new UID (), 'Function unique ID.' , false )
2022-08-30 19:27:44 +12:00
-> param ( 'key' , null , new Text ( Database :: LENGTH_KEY ), 'Variable key. Max length: ' . Database :: LENGTH_KEY . ' chars.' , false )
2023-08-13 07:08:44 +12:00
-> param ( 'value' , null , new Text ( 8192 , 0 ), 'Variable value. Max length: 8192 chars.' , false )
2022-08-02 03:13:47 +12:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-06-11 22:29:04 +12:00
-> inject ( 'dbForConsole' )
-> action ( function ( string $functionId , string $key , string $value , Response $response , Database $dbForProject , Database $dbForConsole ) {
2022-08-02 03:13:47 +12:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
2022-08-30 19:27:54 +12:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2022-08-02 03:13:47 +12:00
}
2022-08-27 01:38:39 +12:00
$variableId = ID :: unique ();
2022-08-02 03:13:47 +12:00
$variable = new Document ([
2022-08-27 01:38:39 +12:00
'$id' => $variableId ,
2022-08-25 03:07:18 +12:00
'$permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
2023-03-12 05:06:02 +13:00
'resourceInternalId' => $function -> getInternalId (),
'resourceId' => $function -> getId (),
'resourceType' => 'function' ,
2022-08-02 03:13:47 +12:00
'key' => $key ,
2022-08-27 01:38:39 +12:00
'value' => $value ,
2023-03-12 05:06:02 +13:00
'search' => implode ( ' ' , [ $variableId , $function -> getId (), $key , 'function' ]),
2022-08-02 03:13:47 +12:00
]);
try {
$variable = $dbForProject -> createDocument ( 'variables' , $variable );
} catch ( DuplicateException $th ) {
2022-08-30 19:28:01 +12:00
throw new Exception ( Exception :: VARIABLE_ALREADY_EXISTS );
2022-08-02 03:13:47 +12:00
}
2023-06-22 22:59:41 +12:00
$dbForProject -> updateDocument ( 'functions' , $function -> getId (), $function -> setAttribute ( 'live' , false ));
2022-08-02 03:13:47 +12:00
2023-07-28 19:56:07 +12:00
// Inform scheduler to pull the latest changes
2023-06-11 22:29:04 +12: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 23:11:10 +12:00
$response
-> setStatusCode ( Response :: STATUS_CODE_CREATED )
-> dynamic ( $variable , Response :: MODEL_VARIABLE );
2022-08-02 03:13:47 +12:00
});
2022-08-10 03:29:24 +12:00
App :: get ( '/v1/functions/:functionId/variables' )
2023-10-03 03:02:48 +13:00
-> desc ( 'List variables' )
2022-08-02 03:13:47 +12:00
-> groups ([ 'api' , 'functions' ])
-> label ( 'scope' , 'functions.read' )
2022-08-04 01:32:50 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2022-08-02 03:13:47 +12:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'listVariables' )
2022-08-10 00:11:50 +12:00
-> label ( 'sdk.description' , '/docs/references/functions/list-variables.md' )
2022-08-02 03:13:47 +12: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 22:05:42 +12:00
-> param ( 'functionId' , '' , new UID (), 'Function unique ID.' , false )
2022-08-02 03:13:47 +12:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2022-09-12 21:02:14 +12:00
-> action ( function ( string $functionId , Response $response , Database $dbForProject ) {
2022-08-02 03:13:47 +12:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
2022-08-30 19:28:48 +12:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2022-08-02 03:13:47 +12:00
}
$response -> dynamic ( new Document ([
2023-09-05 20:21:36 +12:00
'variables' => $function -> getAttribute ( 'vars' , []),
'total' => \count ( $function -> getAttribute ( 'vars' , [])),
2022-08-02 03:13:47 +12:00
]), Response :: MODEL_VARIABLE_LIST );
});
2022-08-10 03:29:24 +12:00
App :: get ( '/v1/functions/:functionId/variables/:variableId' )
2023-10-03 03:02:48 +13:00
-> desc ( 'Get variable' )
2022-08-02 03:13:47 +12:00
-> groups ([ 'api' , 'functions' ])
-> label ( 'scope' , 'functions.read' )
2022-08-04 01:32:50 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2022-08-02 03:13:47 +12:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'getVariable' )
2022-08-10 00:11:50 +12:00
-> label ( 'sdk.description' , '/docs/references/functions/get-variable.md' )
2022-08-02 03:13:47 +12: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 22:05:42 +12:00
-> param ( 'functionId' , '' , new UID (), 'Function unique ID.' , false )
-> param ( 'variableId' , '' , new UID (), 'Variable unique ID.' , false )
2022-08-02 03:13:47 +12:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2022-08-02 22:05:58 +12:00
-> action ( function ( string $functionId , string $variableId , Response $response , Database $dbForProject ) {
2022-08-02 03:13:47 +12:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
2022-08-30 19:28:37 +12:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2022-08-02 03:13:47 +12:00
}
2023-07-25 01:12:36 +12:00
$variable = $dbForProject -> getDocument ( 'variables' , $variableId );
2023-08-18 18:55:44 +12:00
if (
$variable === false ||
$variable -> isEmpty () ||
$variable -> getAttribute ( 'resourceInternalId' ) !== $function -> getInternalId () ||
$variable -> getAttribute ( 'resourceType' ) !== 'function'
) {
2023-07-25 01:12:36 +12:00
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
}
2022-08-02 03:13:47 +12:00
if ( $variable === false || $variable -> isEmpty ()) {
2022-08-30 19:29:09 +12:00
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
2022-08-02 03:13:47 +12:00
}
$response -> dynamic ( $variable , Response :: MODEL_VARIABLE );
});
2022-08-10 03:29:24 +12:00
App :: put ( '/v1/functions/:functionId/variables/:variableId' )
2023-10-03 03:02:48 +13:00
-> desc ( 'Update variable' )
2022-08-02 03:13:47 +12:00
-> groups ([ 'api' , 'functions' ])
-> label ( 'scope' , 'functions.write' )
2022-09-05 20:00:08 +12:00
-> label ( 'audits.event' , 'variable.update' )
2022-09-04 20:13:44 +12:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2022-08-04 01:32:50 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2022-08-02 03:13:47 +12:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'updateVariable' )
2022-08-10 00:11:50 +12:00
-> label ( 'sdk.description' , '/docs/references/functions/update-variable.md' )
2022-08-02 03:13:47 +12: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 22:05:42 +12:00
-> param ( 'functionId' , '' , new UID (), 'Function unique ID.' , false )
-> param ( 'variableId' , '' , new UID (), 'Variable unique ID.' , false )
2022-08-31 19:33:43 +12:00
-> param ( 'key' , null , new Text ( 255 ), 'Variable key. Max length: 255 chars.' , false )
2023-08-13 07:08:44 +12:00
-> param ( 'value' , null , new Text ( 8192 , 0 ), 'Variable value. Max length: 8192 chars.' , true )
2022-08-02 03:13:47 +12:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-06-11 22:29:04 +12:00
-> inject ( 'dbForConsole' )
-> action ( function ( string $functionId , string $variableId , string $key , ? string $value , Response $response , Database $dbForProject , Database $dbForConsole ) {
2022-08-02 03:13:47 +12:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
2022-08-30 19:29:20 +12:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2022-08-02 03:13:47 +12:00
}
2023-07-25 01:12:36 +12: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-02 03:13:47 +12:00
if ( $variable === false || $variable -> isEmpty ()) {
2022-08-30 19:29:30 +12:00
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
2022-08-02 03:13:47 +12:00
}
$variable
2022-08-31 19:33:43 +12:00
-> setAttribute ( 'key' , $key )
2022-08-02 03:13:47 +12:00
-> setAttribute ( 'value' , $value ? ? $variable -> getAttribute ( 'value' ))
2023-03-29 02:21:42 +13:00
-> setAttribute ( 'search' , implode ( ' ' , [ $variableId , $function -> getId (), $key , 'function' ]));
2022-08-02 03:13:47 +12:00
try {
$dbForProject -> updateDocument ( 'variables' , $variable -> getId (), $variable );
} catch ( DuplicateException $th ) {
2022-08-30 19:28:09 +12:00
throw new Exception ( Exception :: VARIABLE_ALREADY_EXISTS );
2022-08-02 03:13:47 +12:00
}
2023-06-22 22:59:41 +12:00
$dbForProject -> updateDocument ( 'functions' , $function -> getId (), $function -> setAttribute ( 'live' , false ));
2022-08-02 03:13:47 +12:00
2023-07-28 19:56:07 +12:00
// Inform scheduler to pull the latest changes
2023-06-11 22:29:04 +12: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-02 03:13:47 +12:00
$response -> dynamic ( $variable , Response :: MODEL_VARIABLE );
});
2022-08-10 03:29:24 +12:00
App :: delete ( '/v1/functions/:functionId/variables/:variableId' )
2023-10-03 03:02:48 +13:00
-> desc ( 'Delete variable' )
2022-08-02 03:13:47 +12:00
-> groups ([ 'api' , 'functions' ])
-> label ( 'scope' , 'functions.write' )
2022-09-05 20:00:08 +12:00
-> label ( 'audits.event' , 'variable.delete' )
2022-09-04 20:13:44 +12:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2022-08-04 01:32:50 +12:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2022-08-02 03:13:47 +12:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'deleteVariable' )
2022-08-10 00:11:50 +12:00
-> label ( 'sdk.description' , '/docs/references/functions/delete-variable.md' )
2022-08-02 03:13:47 +12:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
2022-09-19 22:05:42 +12:00
-> param ( 'functionId' , '' , new UID (), 'Function unique ID.' , false )
-> param ( 'variableId' , '' , new UID (), 'Variable unique ID.' , false )
2022-08-02 03:13:47 +12:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2023-06-11 22:29:04 +12:00
-> inject ( 'dbForConsole' )
-> action ( function ( string $functionId , string $variableId , Response $response , Database $dbForProject , Database $dbForConsole ) {
2022-08-02 03:13:47 +12:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
2022-08-30 19:28:20 +12:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2022-08-02 03:13:47 +12:00
}
2023-07-25 01:12:36 +12: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-02 03:13:47 +12:00
if ( $variable === false || $variable -> isEmpty ()) {
2022-08-30 19:28:27 +12:00
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
2022-08-02 03:13:47 +12:00
}
$dbForProject -> deleteDocument ( 'variables' , $variable -> getId ());
2023-06-11 22:29:04 +12:00
2023-06-22 22:59:41 +12:00
$dbForProject -> updateDocument ( 'functions' , $function -> getId (), $function -> setAttribute ( 'live' , false ));
2022-08-02 03:13:47 +12:00
2023-07-28 19:56:07 +12:00
// Inform scheduler to pull the latest changes
2023-06-11 22:29:04 +12: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-02 03:13:47 +12:00
$response -> noContent ();
});