2020-05-04 14:35:01 +00:00
< ? php
2021-03-10 17:48:05 +00:00
use Ahc\Jwt\JWT ;
use Appwrite\Auth\Auth ;
2022-04-19 13:13:55 +00:00
use Appwrite\Event\Build ;
2022-05-24 14:28:27 +00:00
use Appwrite\Event\Delete ;
use Appwrite\Event\Event ;
2022-11-15 18:13:17 +00:00
use Appwrite\Event\Func ;
2023-10-25 09:46:45 +00:00
use Appwrite\Event\Usage ;
2022-02-18 12:36:24 +00:00
use Appwrite\Extend\Exception ;
2024-05-20 10:44:08 +00:00
use Appwrite\Extend\Exception as AppwriteException ;
2024-08-06 08:19:28 +00:00
use Appwrite\Functions\Validator\Headers ;
2024-06-27 22:12:23 +00:00
use Appwrite\Platform\Tasks\ScheduleExecutions ;
2025-02-03 09:32:01 +00:00
use Appwrite\SDK\AuthType ;
use Appwrite\SDK\ContentType ;
use Appwrite\SDK\Method ;
use Appwrite\SDK\MethodType ;
use Appwrite\SDK\Response as SDKResponse ;
2024-03-06 17:34:21 +00:00
use Appwrite\Utopia\Database\Validator\Queries\Deployments ;
use Appwrite\Utopia\Database\Validator\Queries\Executions ;
use Appwrite\Utopia\Response ;
use Executor\Executor ;
use MaxMind\Db\Reader ;
use Utopia\App ;
use Utopia\CLI\Console ;
use Utopia\Config\Config ;
use Utopia\Database\Database ;
use Utopia\Database\DateTime ;
use Utopia\Database\Document ;
use Utopia\Database\Exception\Duplicate as DuplicateException ;
2024-02-12 16:02:04 +00:00
use Utopia\Database\Exception\Query as QueryException ;
2023-06-11 10:29:04 +00:00
use Utopia\Database\Helpers\ID ;
use Utopia\Database\Helpers\Permission ;
use Utopia\Database\Helpers\Role ;
2024-03-06 17:34:21 +00:00
use Utopia\Database\Query ;
use Utopia\Database\Validator\Authorization ;
2024-06-07 19:05:29 +00:00
use Utopia\Database\Validator\Datetime as DatetimeValidator ;
2024-10-17 05:41:24 +00:00
use Utopia\Database\Validator\Query\Cursor ;
2021-07-25 14:47:18 +00:00
use Utopia\Database\Validator\UID ;
2022-05-24 14:28:27 +00:00
use Utopia\Storage\Device ;
use Utopia\Swoole\Request ;
2024-04-01 11:08:46 +00:00
use Utopia\System\System ;
2024-08-05 13:08:41 +00:00
use Utopia\Validator\AnyOf ;
2020-05-05 17:30:12 +00:00
use Utopia\Validator\ArrayList ;
2024-03-06 17:34:21 +00:00
use Utopia\Validator\Assoc ;
use Utopia\Validator\Boolean ;
2020-05-04 14:35:01 +00:00
use Utopia\Validator\Range ;
2024-03-06 17:34:21 +00:00
use Utopia\Validator\Text ;
2020-05-05 17:30:12 +00:00
use Utopia\Validator\WhiteList ;
2020-05-04 14:35:01 +00:00
include_once __DIR__ . '/../shared/api.php' ;
2024-07-31 11:27:32 +00:00
App :: get ( '/v1/functions/specifications' )
2024-07-12 09:25:57 +00:00
-> groups ([ 'api' , 'functions' ])
2024-08-20 04:23:11 +00:00
-> desc ( 'List available function runtime specifications' )
2024-07-12 09:25:57 +00:00
-> label ( 'scope' , 'functions.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2025-02-03 09:32:01 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'listSpecifications' ,
description : '/docs/references/functions/list-specifications.md' ,
auth : [ AuthType :: KEY , AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_SPECIFICATION_LIST ,
)
]
))
2024-07-12 09:25:57 +00:00
-> inject ( 'response' )
2024-07-16 05:26:53 +00:00
-> inject ( 'plan' )
2024-08-06 09:54:46 +00:00
-> action ( function ( Response $response , array $plan ) {
2024-07-31 11:27:32 +00:00
$allRuntimeSpecs = Config :: getParam ( 'runtime-specifications' , []);
2024-07-31 06:21:07 +00:00
2024-07-31 11:27:32 +00:00
$runtimeSpecs = [];
2024-08-01 06:30:24 +00:00
foreach ( $allRuntimeSpecs as $spec ) {
$spec [ 'enabled' ] = true ;
2024-07-31 06:21:07 +00:00
2024-08-06 09:54:46 +00:00
if ( array_key_exists ( 'runtimeSpecifications' , $plan )) {
2024-08-02 08:00:42 +00:00
$spec [ 'enabled' ] = in_array ( $spec [ 'slug' ], $plan [ 'runtimeSpecifications' ]);
}
2024-07-31 06:21:07 +00:00
2024-07-31 11:27:32 +00:00
// Only add specs that are within the limits set by environment variables
2024-10-27 16:22:45 +00:00
if ( $spec [ 'cpus' ] <= System :: getEnv ( '_APP_COMPUTE_CPUS' , 1 ) && $spec [ 'memory' ] <= System :: getEnv ( '_APP_COMPUTE_MEMORY' , 512 )) {
2024-07-31 11:27:32 +00:00
$runtimeSpecs [] = $spec ;
2024-07-31 06:21:07 +00:00
}
}
2024-07-12 09:25:57 +00:00
$response -> dynamic ( new Document ([
2024-07-31 11:27:32 +00:00
'specifications' => $runtimeSpecs ,
2024-08-19 04:18:45 +00:00
'total' => count ( $runtimeSpecs )
2024-07-31 11:27:32 +00:00
]), Response :: MODEL_SPECIFICATION_LIST );
2024-07-12 09:25:57 +00:00
});
2020-07-12 21:18:52 +00:00
App :: get ( '/v1/functions/:functionId' )
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'Get function' )
2020-05-05 17:30:12 +00:00
-> label ( 'scope' , 'functions.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2025-02-03 09:32:01 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'get' ,
description : '/docs/references/functions/get-function.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_FUNCTION ,
)
]
))
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-05-24 14:28:27 +00:00
-> action ( function ( string $functionId , Response $response , Database $dbForProject ) {
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-05-05 17:30:12 +00:00
2021-06-20 13:59:36 +00:00
if ( $function -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-05-05 17:30:12 +00:00
}
2020-10-30 19:53:27 +00:00
$response -> dynamic ( $function , Response :: MODEL_FUNCTION );
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
2020-10-30 19:53:27 +00:00
App :: get ( '/v1/functions/:functionId/usage' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Get function usage' )
2023-09-27 17:10:21 +00:00
-> groups ([ 'api' , 'functions' , 'usage' ])
2020-10-30 19:53:27 +00:00
-> label ( 'scope' , 'functions.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2025-02-03 09:32:01 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'getFunctionUsage' ,
description : '/docs/references/functions/get-function-usage.md' ,
auth : [ AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_USAGE_FUNCTION ,
)
]
))
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2023-11-08 09:09:32 +00:00
-> param ( 'range' , '30d' , new WhiteList ([ '24h' , '30d' , '90d' ]), 'Date range.' , true )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-05-24 14:28:27 +00:00
-> action ( function ( string $functionId , string $range , Response $response , Database $dbForProject ) {
2020-10-30 19:53:27 +00:00
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-10-30 19:53:27 +00:00
2021-06-20 13:59:36 +00:00
if ( $function -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-10-30 19:53:27 +00:00
}
2022-04-13 12:39:31 +00:00
2023-10-25 07:39:59 +00:00
$periods = Config :: getParam ( 'usage' , []);
$stats = $usage = [];
$days = $periods [ $range ];
$metrics = [
str_replace ([ '{resourceType}' , '{resourceInternalId}' ], [ 'functions' , $function -> getInternalId ()], METRIC_FUNCTION_ID_DEPLOYMENTS ),
str_replace ([ '{resourceType}' , '{resourceInternalId}' ], [ 'functions' , $function -> getInternalId ()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE ),
str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_BUILDS ),
str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_BUILDS_STORAGE ),
str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_BUILDS_COMPUTE ),
str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_EXECUTIONS ),
str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE ),
2024-07-30 08:53:28 +00:00
str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_BUILDS_MB_SECONDS ),
str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS )
2023-10-25 07:39:59 +00:00
];
2023-11-08 09:09:32 +00:00
Authorization :: skip ( function () use ( $dbForProject , $days , $metrics , & $stats ) {
foreach ( $metrics as $metric ) {
2024-02-01 10:21:50 +00:00
$result = $dbForProject -> findOne ( 'stats' , [
2023-11-01 18:44:06 +00:00
Query :: equal ( 'metric' , [ $metric ]),
Query :: equal ( 'period' , [ 'inf' ])
]);
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'total' ] = $result [ 'value' ] ? ? 0 ;
2023-10-25 07:39:59 +00:00
$limit = $days [ 'limit' ];
$period = $days [ 'period' ];
2024-02-01 10:21:50 +00:00
$results = $dbForProject -> find ( 'stats' , [
2023-10-25 07:39:59 +00:00
Query :: equal ( 'metric' , [ $metric ]),
2023-10-25 12:06:54 +00:00
Query :: equal ( 'period' , [ $period ]),
2023-10-25 07:39:59 +00:00
Query :: limit ( $limit ),
Query :: orderDesc ( 'time' ),
]);
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'data' ] = [];
2023-10-25 07:39:59 +00:00
foreach ( $results as $result ) {
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'data' ][ $result -> getAttribute ( 'time' )] = [
'value' => $result -> getAttribute ( 'value' ),
2023-10-25 07:39:59 +00:00
];
}
}
});
2023-06-11 10:29:04 +00:00
2023-10-25 07:39:59 +00:00
$format = match ( $days [ 'period' ]) {
'1h' => 'Y-m-d\TH:00:00.000P' ,
'1d' => 'Y-m-d\T00:00:00.000P' ,
};
2024-03-06 17:34:21 +00:00
foreach ( $metrics as $metric ) {
$usage [ $metric ][ 'total' ] = $stats [ $metric ][ 'total' ];
$usage [ $metric ][ 'data' ] = [];
$leap = time () - ( $days [ 'limit' ] * $days [ 'factor' ]);
while ( $leap < time ()) {
$leap += $days [ 'factor' ];
$formatDate = date ( $format , $leap );
$usage [ $metric ][ 'data' ][] = [
'value' => $stats [ $metric ][ 'data' ][ $formatDate ][ 'value' ] ? ? 0 ,
'date' => $formatDate ,
];
}
2023-06-11 10:29:04 +00:00
}
2021-08-26 19:12:36 +00:00
2023-10-25 07:39:59 +00:00
$response -> dynamic ( new Document ([
'range' => $range ,
2023-11-08 09:09:32 +00:00
'deploymentsTotal' => $usage [ $metrics [ 0 ]][ 'total' ],
'deploymentsStorageTotal' => $usage [ $metrics [ 1 ]][ 'total' ],
'buildsTotal' => $usage [ $metrics [ 2 ]][ 'total' ],
'buildsStorageTotal' => $usage [ $metrics [ 3 ]][ 'total' ],
'buildsTimeTotal' => $usage [ $metrics [ 4 ]][ 'total' ],
'executionsTotal' => $usage [ $metrics [ 5 ]][ 'total' ],
'executionsTimeTotal' => $usage [ $metrics [ 6 ]][ 'total' ],
'deployments' => $usage [ $metrics [ 0 ]][ 'data' ],
'deploymentsStorage' => $usage [ $metrics [ 1 ]][ 'data' ],
'builds' => $usage [ $metrics [ 2 ]][ 'data' ],
'buildsStorage' => $usage [ $metrics [ 3 ]][ 'data' ],
'buildsTime' => $usage [ $metrics [ 4 ]][ 'data' ],
'executions' => $usage [ $metrics [ 5 ]][ 'data' ],
'executionsTime' => $usage [ $metrics [ 6 ]][ 'data' ],
2024-07-30 08:53:28 +00:00
'buildsMbSecondsTotal' => $usage [ $metrics [ 7 ]][ 'total' ],
'buildsMbSeconds' => $usage [ $metrics [ 7 ]][ 'data' ],
'executionsMbSeconds' => $usage [ $metrics [ 8 ]][ 'data' ],
'executionsMbSecondsTotal' => $usage [ $metrics [ 8 ]][ 'total' ]
2023-10-25 07:39:59 +00:00
]), Response :: MODEL_USAGE_FUNCTION );
2022-07-17 10:30:58 +00:00
});
App :: get ( '/v1/functions/usage' )
2023-10-03 16:50:48 +00:00
-> desc ( 'Get functions usage' )
2024-10-24 13:21:40 +00:00
-> groups ([ 'api' , 'functions' , 'usage' ])
2022-07-17 10:30:58 +00:00
-> label ( 'scope' , 'functions.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2025-02-03 09:32:01 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'getUsage' ,
description : '/docs/references/functions/get-functions-usage.md' ,
auth : [ AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_USAGE_FUNCTIONS ,
)
]
))
2023-11-08 09:09:32 +00:00
-> param ( 'range' , '30d' , new WhiteList ([ '24h' , '30d' , '90d' ]), 'Date range.' , true )
2022-07-17 10:30:58 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2022-07-17 10:39:44 +00:00
-> action ( function ( string $range , Response $response , Database $dbForProject ) {
2022-07-17 10:30:58 +00:00
2023-10-25 07:39:59 +00:00
$periods = Config :: getParam ( 'usage' , []);
$stats = $usage = [];
$days = $periods [ $range ];
$metrics = [
METRIC_FUNCTIONS ,
METRIC_DEPLOYMENTS ,
METRIC_DEPLOYMENTS_STORAGE ,
METRIC_BUILDS ,
METRIC_BUILDS_STORAGE ,
METRIC_BUILDS_COMPUTE ,
METRIC_EXECUTIONS ,
METRIC_EXECUTIONS_COMPUTE ,
2024-07-30 08:53:28 +00:00
METRIC_BUILDS_MB_SECONDS ,
METRIC_EXECUTIONS_MB_SECONDS ,
2023-10-25 07:39:59 +00:00
];
2023-11-08 09:09:32 +00:00
Authorization :: skip ( function () use ( $dbForProject , $days , $metrics , & $stats ) {
foreach ( $metrics as $metric ) {
2024-02-01 10:21:50 +00:00
$result = $dbForProject -> findOne ( 'stats' , [
2023-11-01 18:44:06 +00:00
Query :: equal ( 'metric' , [ $metric ]),
Query :: equal ( 'period' , [ 'inf' ])
]);
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'total' ] = $result [ 'value' ] ? ? 0 ;
2023-10-25 07:39:59 +00:00
$limit = $days [ 'limit' ];
$period = $days [ 'period' ];
2024-02-01 10:21:50 +00:00
$results = $dbForProject -> find ( 'stats' , [
2023-10-25 07:39:59 +00:00
Query :: equal ( 'metric' , [ $metric ]),
2023-10-25 12:06:54 +00:00
Query :: equal ( 'period' , [ $period ]),
2023-10-25 07:39:59 +00:00
Query :: limit ( $limit ),
Query :: orderDesc ( 'time' ),
]);
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'data' ] = [];
2023-10-25 07:39:59 +00:00
foreach ( $results as $result ) {
2023-11-08 09:09:32 +00:00
$stats [ $metric ][ 'data' ][ $result -> getAttribute ( 'time' )] = [
'value' => $result -> getAttribute ( 'value' ),
2023-10-25 07:39:59 +00:00
];
}
}
});
2023-06-11 10:29:04 +00:00
2023-10-25 07:39:59 +00:00
$format = match ( $days [ 'period' ]) {
'1h' => 'Y-m-d\TH:00:00.000P' ,
'1d' => 'Y-m-d\T00:00:00.000P' ,
};
2024-03-06 17:34:21 +00:00
foreach ( $metrics as $metric ) {
$usage [ $metric ][ 'total' ] = $stats [ $metric ][ 'total' ];
$usage [ $metric ][ 'data' ] = [];
$leap = time () - ( $days [ 'limit' ] * $days [ 'factor' ]);
while ( $leap < time ()) {
$leap += $days [ 'factor' ];
$formatDate = date ( $format , $leap );
$usage [ $metric ][ 'data' ][] = [
'value' => $stats [ $metric ][ 'data' ][ $formatDate ][ 'value' ] ? ? 0 ,
'date' => $formatDate ,
];
}
2023-06-11 10:29:04 +00:00
}
2023-10-25 07:39:59 +00:00
$response -> dynamic ( new Document ([
'range' => $range ,
2023-11-08 09:09:32 +00:00
'functionsTotal' => $usage [ $metrics [ 0 ]][ 'total' ],
'deploymentsTotal' => $usage [ $metrics [ 1 ]][ 'total' ],
'deploymentsStorageTotal' => $usage [ $metrics [ 2 ]][ 'total' ],
'buildsTotal' => $usage [ $metrics [ 3 ]][ 'total' ],
'buildsStorageTotal' => $usage [ $metrics [ 4 ]][ 'total' ],
'buildsTimeTotal' => $usage [ $metrics [ 5 ]][ 'total' ],
'executionsTotal' => $usage [ $metrics [ 6 ]][ 'total' ],
'executionsTimeTotal' => $usage [ $metrics [ 7 ]][ 'total' ],
'functions' => $usage [ $metrics [ 0 ]][ 'data' ],
'deployments' => $usage [ $metrics [ 1 ]][ 'data' ],
'deploymentsStorage' => $usage [ $metrics [ 2 ]][ 'data' ],
'builds' => $usage [ $metrics [ 3 ]][ 'data' ],
'buildsStorage' => $usage [ $metrics [ 4 ]][ 'data' ],
'buildsTime' => $usage [ $metrics [ 5 ]][ 'data' ],
'executions' => $usage [ $metrics [ 6 ]][ 'data' ],
'executionsTime' => $usage [ $metrics [ 7 ]][ 'data' ],
2024-07-30 08:53:28 +00:00
'buildsMbSecondsTotal' => $usage [ $metrics [ 8 ]][ 'total' ],
'buildsMbSeconds' => $usage [ $metrics [ 8 ]][ 'data' ],
'executionsMbSeconds' => $usage [ $metrics [ 9 ]][ 'data' ],
'executionsMbSecondsTotal' => $usage [ $metrics [ 9 ]][ 'total' ],
2023-10-25 07:39:59 +00:00
]), Response :: MODEL_USAGE_FUNCTIONS );
2020-12-26 15:20:08 +00:00
});
2020-10-30 19:53:27 +00:00
2023-08-21 10:21:18 +00:00
App :: get ( '/v1/functions/:functionId/deployments/:deploymentId/download' )
-> groups ([ 'api' , 'functions' ])
2024-07-01 19:35:55 +00:00
-> desc ( 'Download deployment' )
2023-08-21 10:21:18 +00:00
-> label ( 'scope' , 'functions.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2025-02-03 09:32:01 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'getDeploymentDownload' ,
description : '/docs/references/functions/get-deployment-download.md' ,
auth : [ AuthType :: KEY , AuthType :: JWT ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_NONE ,
)
],
contentType : ContentType :: ANY ,
type : MethodType :: LOCATION
))
2023-08-21 10:21:18 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
-> param ( 'deploymentId' , '' , new UID (), 'Deployment ID.' )
-> inject ( 'response' )
-> inject ( 'request' )
-> inject ( 'dbForProject' )
2024-02-20 14:10:51 +00:00
-> inject ( 'deviceForFunctions' )
-> action ( function ( string $functionId , string $deploymentId , Response $response , Request $request , Database $dbForProject , Device $deviceForFunctions ) {
2023-08-21 10:21:18 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
}
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
if ( $deployment -> isEmpty ()) {
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
}
if ( $deployment -> getAttribute ( 'resourceId' ) !== $function -> getId ()) {
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
}
$path = $deployment -> getAttribute ( 'path' , '' );
2024-02-20 14:10:51 +00:00
if ( ! $deviceForFunctions -> exists ( $path )) {
2023-08-21 10:21:18 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
}
$response
-> setContentType ( 'application/gzip' )
2024-10-22 09:38:05 +00:00
-> addHeader ( 'Cache-Control' , 'private, max-age=3888000' ) // 45 days
2023-08-21 10:21:18 +00:00
-> addHeader ( 'X-Peak' , \memory_get_peak_usage ())
2023-10-27 14:08:33 +00:00
-> addHeader ( 'Content-Disposition' , 'attachment; filename="' . $deploymentId . '.tar.gz"' );
2023-08-21 10:21:18 +00:00
2024-02-20 14:10:51 +00:00
$size = $deviceForFunctions -> getFileSize ( $path );
2023-08-21 10:21:18 +00:00
$rangeHeader = $request -> getHeader ( 'range' );
if ( ! empty ( $rangeHeader )) {
$start = $request -> getRangeStart ();
$end = $request -> getRangeEnd ();
$unit = $request -> getRangeUnit ();
if ( $end === null ) {
$end = min (( $start + MAX_OUTPUT_CHUNK_SIZE - 1 ), ( $size - 1 ));
}
if ( $unit !== 'bytes' || $start >= $end || $end >= $size ) {
throw new Exception ( Exception :: STORAGE_INVALID_RANGE );
}
$response
-> addHeader ( 'Accept-Ranges' , 'bytes' )
-> addHeader ( 'Content-Range' , 'bytes ' . $start . '-' . $end . '/' . $size )
-> addHeader ( 'Content-Length' , $end - $start + 1 )
-> setStatusCode ( Response :: STATUS_CODE_PARTIALCONTENT );
2024-02-20 14:10:51 +00:00
$response -> send ( $deviceForFunctions -> read ( $path , $start , ( $end - $start + 1 )));
2023-08-21 10:21:18 +00:00
}
if ( $size > APP_STORAGE_READ_BUFFER ) {
for ( $i = 0 ; $i < ceil ( $size / MAX_OUTPUT_CHUNK_SIZE ); $i ++ ) {
$response -> chunk (
2024-02-20 14:10:51 +00:00
$deviceForFunctions -> read (
2023-08-21 10:21:18 +00:00
$path ,
( $i * MAX_OUTPUT_CHUNK_SIZE ),
min ( MAX_OUTPUT_CHUNK_SIZE , $size - ( $i * MAX_OUTPUT_CHUNK_SIZE ))
),
(( $i + 1 ) * MAX_OUTPUT_CHUNK_SIZE ) >= $size
);
}
} else {
2024-02-20 14:10:51 +00:00
$response -> send ( $deviceForFunctions -> read ( $path ));
2023-08-21 10:21:18 +00:00
}
});
2022-02-15 09:16:32 +00:00
App :: patch ( '/v1/functions/:functionId/deployments/:deploymentId' )
2020-07-12 21:18:52 +00:00
-> groups ([ 'api' , 'functions' ])
2024-07-01 19:35:55 +00:00
-> desc ( 'Update deployment' )
2020-05-06 17:35:56 +00:00
-> label ( 'scope' , 'functions.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2022-04-13 12:39:31 +00:00
-> label ( 'event' , 'functions.[functionId].deployments.[deploymentId].update' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'deployment.update' )
2022-08-13 07:34:03 +00:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2021-04-16 07:22:17 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2020-05-06 17:35:56 +00:00
-> label ( 'sdk.namespace' , 'functions' )
2022-01-24 22:59:02 +00:00
-> label ( 'sdk.method' , 'updateDeployment' )
-> label ( 'sdk.description' , '/docs/references/functions/update-function-deployment.md' )
2020-11-11 21:02:24 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_FUNCTION )
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2022-02-15 09:16:32 +00:00
-> param ( 'deploymentId' , '' , new UID (), 'Deployment ID.' )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
2025-02-03 09:32:01 +00:00
-> inject ( 'dbForPlatform' )
-> action ( function ( string $functionId , string $deploymentId , Response $response , Database $dbForProject , Event $queueForEvents , Database $dbForPlatform ) {
2021-01-16 23:38:13 +00:00
2022-01-06 09:45:56 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2022-02-15 09:16:32 +00:00
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
2022-01-26 23:19:02 +00:00
$build = $dbForProject -> getDocument ( 'builds' , $deployment -> getAttribute ( 'buildId' , '' ));
2021-09-06 00:37:20 +00:00
2021-12-08 15:08:53 +00:00
if ( $function -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2021-12-08 15:08:53 +00:00
}
2021-01-16 23:38:13 +00:00
2022-01-24 22:59:02 +00:00
if ( $deployment -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
2021-12-08 15:08:53 +00:00
}
2021-09-06 00:37:20 +00:00
2021-12-08 15:08:53 +00:00
if ( $build -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: BUILD_NOT_FOUND );
2020-05-06 17:35:56 +00:00
}
2021-12-08 15:08:53 +00:00
if ( $build -> getAttribute ( 'status' ) !== 'ready' ) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: BUILD_NOT_READY );
2021-09-06 00:37:20 +00:00
}
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> updateDocument ( 'functions' , $function -> getId (), new Document ( array_merge ( $function -> getArrayCopy (), [
2023-06-11 10:29:04 +00:00
'deploymentInternalId' => $deployment -> getInternalId (),
'deployment' => $deployment -> getId (),
2021-12-08 15:08:53 +00:00
])));
2023-07-28 07:56:07 +00:00
// Inform scheduler if function is still active
2025-02-03 09:32:01 +00:00
$schedule = $dbForPlatform -> getDocument ( 'schedules' , $function -> getAttribute ( 'scheduleId' ));
2023-06-11 10:29:04 +00:00
$schedule
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
-> setAttribute ( 'schedule' , $function -> getAttribute ( 'schedule' ))
-> setAttribute ( 'active' , ! empty ( $function -> getAttribute ( 'schedule' )) && ! empty ( $function -> getAttribute ( 'deployment' )));
2025-02-03 09:32:01 +00:00
Authorization :: skip ( fn () => $dbForPlatform -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
2022-11-06 21:41:33 +00:00
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-04-13 12:39:31 +00:00
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'deploymentId' , $deployment -> getId ());
2021-12-08 15:08:53 +00:00
$response -> dynamic ( $function , Response :: MODEL_FUNCTION );
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
App :: delete ( '/v1/functions/:functionId' )
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'Delete function' )
2020-05-05 17:30:12 +00:00
-> label ( 'scope' , 'functions.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2022-04-13 12:39:31 +00:00
-> label ( 'event' , 'functions.[functionId].delete' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'function.delete' )
2022-08-09 14:38:54 +00:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2025-02-03 09:32:01 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'delete' ,
description : '/docs/references/functions/delete-function.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_NOCONTENT ,
model : Response :: MODEL_NONE ,
)
],
contentType : ContentType :: NONE
))
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForDeletes' )
-> inject ( 'queueForEvents' )
2025-02-03 09:32:01 +00:00
-> inject ( 'dbForPlatform' )
-> action ( function ( string $functionId , Response $response , Database $dbForProject , Delete $queueForDeletes , Event $queueForEvents , Database $dbForPlatform ) {
2020-10-30 19:53:27 +00:00
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-05-05 17:30:12 +00:00
2021-06-20 13:59:36 +00:00
if ( $function -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2020-05-06 12:12:52 +00:00
2021-12-27 12:45:23 +00:00
if ( ! $dbForProject -> deleteDocument ( 'functions' , $function -> getId ())) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'Failed to remove function from DB' );
2020-05-04 14:35:01 +00:00
}
2020-05-05 17:30:12 +00:00
2023-07-28 07:56:07 +00:00
// Inform scheduler to no longer run function
2025-02-03 09:32:01 +00:00
$schedule = $dbForPlatform -> getDocument ( 'schedules' , $function -> getAttribute ( 'scheduleId' ));
2022-11-15 16:14:52 +00:00
$schedule
2022-11-06 21:41:33 +00:00
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
2023-06-11 10:29:04 +00:00
-> setAttribute ( 'active' , false );
2025-02-03 09:32:01 +00:00
Authorization :: skip ( fn () => $dbForPlatform -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
2022-11-04 05:12:08 +00:00
2022-12-20 16:11:30 +00:00
$queueForDeletes
2022-04-17 20:34:32 +00:00
-> setType ( DELETE_TYPE_DOCUMENT )
-> setDocument ( $function );
2022-04-13 12:39:31 +00:00
2022-12-20 16:11:30 +00:00
$queueForEvents -> setParam ( 'functionId' , $function -> getId ());
2020-10-30 19:53:27 +00:00
2020-07-12 21:18:52 +00:00
$response -> noContent ();
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
2022-01-24 23:09:24 +00:00
App :: get ( '/v1/functions/:functionId/deployments' )
2020-07-12 21:18:52 +00:00
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'List deployments' )
2020-05-05 17:30:12 +00:00
-> label ( 'scope' , 'functions.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2025-02-03 11:38:27 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'listDeployments' ,
description : '/docs/references/functions/list-deployments.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_DEPLOYMENT_LIST ,
)
]
))
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2023-03-29 19:38:39 +00:00
-> param ( 'queries' , [], new Deployments (), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode ( ', ' , Deployments :: ALLOWED_ATTRIBUTES ), true )
2020-09-10 14:40:14 +00:00
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-08-23 09:12:48 +00:00
-> action ( function ( string $functionId , array $queries , string $search , Response $response , Database $dbForProject ) {
2021-01-16 23:38:13 +00:00
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-05-05 17:30:12 +00:00
2021-06-20 13:59:36 +00:00
if ( $function -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2021-05-27 10:09:14 +00:00
2024-02-12 16:02:04 +00:00
try {
$queries = Query :: parseQueries ( $queries );
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
}
2021-08-06 12:36:05 +00:00
2022-08-11 23:53:52 +00:00
if ( ! empty ( $search )) {
2022-08-23 09:12:48 +00:00
$queries [] = Query :: search ( 'search' , $search );
2021-08-06 12:36:05 +00:00
}
2022-08-23 09:12:48 +00:00
// Set resource queries
2024-04-29 10:24:22 +00:00
$queries [] = Query :: equal ( 'resourceInternalId' , [ $function -> getInternalId ()]);
2022-08-23 09:12:48 +00:00
$queries [] = Query :: equal ( 'resourceType' , [ 'functions' ]);
2021-08-18 13:42:03 +00:00
2024-02-12 09:55:45 +00:00
/**
2024-02-12 10:03:31 +00:00
* Get cursor document if there was a cursor query , we use array_filter and reset for reference $cursor to $queries
2024-02-12 09:55:45 +00:00
*/
2023-08-22 03:25:55 +00:00
$cursor = \array_filter ( $queries , function ( $query ) {
2023-12-06 14:10:40 +00:00
return \in_array ( $query -> getMethod (), [ Query :: TYPE_CURSOR_AFTER , Query :: TYPE_CURSOR_BEFORE ]);
2023-08-22 03:25:55 +00:00
});
2022-08-30 23:31:43 +00:00
$cursor = reset ( $cursor );
2022-08-30 11:55:23 +00:00
if ( $cursor ) {
2022-08-23 09:12:48 +00:00
/** @var Query $cursor */
2024-10-17 05:41:24 +00:00
$validator = new Cursor ();
if ( ! $validator -> isValid ( $cursor )) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $validator -> getDescription ());
}
2022-08-23 09:12:48 +00:00
$deploymentId = $cursor -> getValue ();
$cursorDocument = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
2021-08-18 13:42:03 +00:00
2022-08-11 23:53:52 +00:00
if ( $cursorDocument -> isEmpty ()) {
2022-08-23 09:12:48 +00:00
throw new Exception ( Exception :: GENERAL_CURSOR_NOT_FOUND , " Deployment ' { $deploymentId } ' for the 'cursor' value not found. " );
2022-08-11 23:53:52 +00:00
}
2021-08-18 13:42:03 +00:00
2022-08-23 09:12:48 +00:00
$cursor -> setValue ( $cursorDocument );
2021-08-18 13:42:03 +00:00
}
2022-08-23 09:12:48 +00:00
$filterQueries = Query :: groupByType ( $queries )[ 'filters' ];
2021-08-18 13:42:03 +00:00
2022-08-23 09:12:48 +00:00
$results = $dbForProject -> find ( 'deployments' , $queries );
2022-08-11 23:53:52 +00:00
$total = $dbForProject -> count ( 'deployments' , $filterQueries , APP_LIMIT_COUNT );
2020-07-12 21:18:52 +00:00
2022-01-31 11:29:31 +00:00
foreach ( $results as $result ) {
2022-02-16 11:43:21 +00:00
$build = $dbForProject -> getDocument ( 'builds' , $result -> getAttribute ( 'buildId' , '' ));
2022-02-16 16:00:45 +00:00
$result -> setAttribute ( 'status' , $build -> getAttribute ( 'status' , 'processing' ));
2023-08-05 14:50:28 +00:00
$result -> setAttribute ( 'buildLogs' , $build -> getAttribute ( 'logs' , '' ));
2022-10-10 15:24:40 +00:00
$result -> setAttribute ( 'buildTime' , $build -> getAttribute ( 'duration' , 0 ));
2024-08-19 11:06:58 +00:00
$result -> setAttribute ( 'buildSize' , $build -> getAttribute ( 'size' , 0 ));
$result -> setAttribute ( 'size' , $result -> getAttribute ( 'size' , 0 ));
2021-12-06 14:12:41 +00:00
}
2020-10-30 19:53:27 +00:00
$response -> dynamic ( new Document ([
2022-01-24 23:09:24 +00:00
'deployments' => $results ,
2022-02-27 09:57:09 +00:00
'total' => $total ,
2022-01-24 23:09:24 +00:00
]), Response :: MODEL_DEPLOYMENT_LIST );
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
2022-01-24 23:11:33 +00:00
App :: get ( '/v1/functions/:functionId/deployments/:deploymentId' )
2020-07-12 21:18:52 +00:00
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'Get deployment' )
2020-05-05 17:30:12 +00:00
-> label ( 'scope' , 'functions.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2025-02-03 11:38:27 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'getDeployment' ,
description : '/docs/references/functions/get-deployment.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_DEPLOYMENT ,
)
]
))
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2022-01-24 23:11:33 +00:00
-> param ( 'deploymentId' , '' , new UID (), 'Deployment ID.' )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-05-24 14:28:27 +00:00
-> action ( function ( string $functionId , string $deploymentId , Response $response , Database $dbForProject ) {
2021-01-16 23:38:13 +00:00
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2020-05-05 17:30:12 +00:00
2021-06-20 13:59:36 +00:00
if ( $function -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2020-05-05 17:30:12 +00:00
2022-01-24 23:11:33 +00:00
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
2020-05-05 17:30:12 +00:00
2022-01-25 16:51:05 +00:00
if ( $deployment -> getAttribute ( 'resourceId' ) !== $function -> getId ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2020-05-05 17:30:12 +00:00
2022-01-24 23:11:33 +00:00
if ( $deployment -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
2020-05-05 17:30:12 +00:00
}
2022-10-31 05:11:55 +00:00
$build = $dbForProject -> getDocument ( 'builds' , $deployment -> getAttribute ( 'buildId' , '' ));
2023-05-28 11:39:48 +00:00
$deployment -> setAttribute ( 'status' , $build -> getAttribute ( 'status' , 'waiting' ));
2023-08-05 14:50:28 +00:00
$deployment -> setAttribute ( 'buildLogs' , $build -> getAttribute ( 'logs' , '' ));
2023-06-22 10:59:41 +00:00
$deployment -> setAttribute ( 'buildTime' , $build -> getAttribute ( 'duration' , 0 ));
2024-08-19 11:06:58 +00:00
$deployment -> setAttribute ( 'buildSize' , $build -> getAttribute ( 'size' , 0 ));
$deployment -> setAttribute ( 'size' , $deployment -> getAttribute ( 'size' , 0 ));
2022-10-31 05:11:55 +00:00
2022-01-24 23:11:33 +00:00
$response -> dynamic ( $deployment , Response :: MODEL_DEPLOYMENT );
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
2022-01-24 23:14:21 +00:00
App :: delete ( '/v1/functions/:functionId/deployments/:deploymentId' )
2020-07-12 21:18:52 +00:00
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'Delete deployment' )
2020-05-05 17:30:12 +00:00
-> label ( 'scope' , 'functions.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2022-04-13 12:39:31 +00:00
-> label ( 'event' , 'functions.[functionId].deployments.[deploymentId].delete' )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'deployment.delete' )
2022-08-13 07:34:03 +00:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2025-02-03 11:38:27 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'deleteDeployment' ,
description : '/docs/references/functions/delete-deployment.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_NOCONTENT ,
model : Response :: MODEL_NONE ,
)
],
contentType : ContentType :: NONE
))
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2022-01-24 23:14:21 +00:00
-> param ( 'deploymentId' , '' , new UID (), 'Deployment ID.' )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForDeletes' )
-> inject ( 'queueForEvents' )
2024-02-20 14:10:51 +00:00
-> inject ( 'deviceForFunctions' )
-> action ( function ( string $functionId , string $deploymentId , Response $response , Database $dbForProject , Delete $queueForDeletes , Event $queueForEvents , Device $deviceForFunctions ) {
2021-01-16 23:38:13 +00:00
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
2021-06-20 13:59:36 +00:00
if ( $function -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2022-04-13 12:39:31 +00:00
2022-01-24 23:14:21 +00:00
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
if ( $deployment -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
2021-09-13 10:50:45 +00:00
}
2022-01-31 23:44:55 +00:00
if ( $deployment -> getAttribute ( 'resourceId' ) !== $function -> getId ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
2021-09-13 10:50:45 +00:00
}
2023-06-14 08:57:30 +00:00
if ( ! $dbForProject -> deleteDocument ( 'deployments' , $deployment -> getId ())) {
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'Failed to remove deployment from DB' );
}
if ( ! empty ( $deployment -> getAttribute ( 'path' , '' ))) {
2024-02-20 14:10:51 +00:00
if ( ! ( $deviceForFunctions -> delete ( $deployment -> getAttribute ( 'path' , '' )))) {
2023-06-14 08:57:30 +00:00
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'Failed to remove deployment from storage' );
2020-07-18 21:48:28 +00:00
}
2020-05-05 17:30:12 +00:00
}
2022-04-13 12:39:31 +00:00
if ( $function -> getAttribute ( 'deployment' ) === $deployment -> getId ()) { // Reset function deployment
2021-12-27 12:45:23 +00:00
$function = $dbForProject -> updateDocument ( 'functions' , $function -> getId (), new Document ( array_merge ( $function -> getArrayCopy (), [
2022-01-24 23:14:21 +00:00
'deployment' => '' ,
2023-06-11 14:08:48 +00:00
'deploymentInternalId' => '' ,
2021-05-04 21:25:17 +00:00
])));
2020-10-30 19:53:27 +00:00
}
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-04-13 12:39:31 +00:00
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'deploymentId' , $deployment -> getId ());
2020-07-18 21:48:28 +00:00
2022-12-20 16:11:30 +00:00
$queueForDeletes
2022-04-17 20:34:32 +00:00
-> setType ( DELETE_TYPE_DOCUMENT )
-> setDocument ( $deployment );
2022-01-28 01:29:20 +00:00
2020-07-12 21:18:52 +00:00
$response -> noContent ();
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
2024-05-29 12:18:52 +00:00
App :: post ( '/v1/functions/:functionId/deployments/:deploymentId/build' )
-> alias ( '/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId' )
2022-09-29 08:42:46 +00:00
-> groups ([ 'api' , 'functions' ])
2024-06-02 17:44:41 +00:00
-> desc ( 'Rebuild deployment' )
2022-09-29 08:42:46 +00:00
-> label ( 'scope' , 'functions.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2022-09-29 08:42:46 +00:00
-> label ( 'event' , 'functions.[functionId].deployments.[deploymentId].update' )
-> label ( 'audits.event' , 'deployment.update' )
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2025-02-03 11:38:27 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'createBuild' ,
description : '/docs/references/functions/create-build.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_NOCONTENT ,
model : Response :: MODEL_NONE ,
)
]
))
2022-09-29 08:42:46 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
-> param ( 'deploymentId' , '' , new UID (), 'Deployment ID.' )
2024-06-02 17:44:41 +00:00
-> param ( 'buildId' , '' , new UID (), 'Build unique ID.' , true ) // added as optional param for backward compatibility
2023-06-22 10:59:41 +00:00
-> inject ( 'request' )
2022-09-29 08:42:46 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
-> inject ( 'project' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
-> inject ( 'queueForBuilds' )
2024-07-25 08:36:11 +00:00
-> inject ( 'deviceForFunctions' )
-> action ( function ( string $functionId , string $deploymentId , string $buildId , Request $request , Response $response , Database $dbForProject , Document $project , Event $queueForEvents , Build $queueForBuilds , Device $deviceForFunctions ) {
2022-09-29 08:42:46 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
}
2023-08-19 06:15:47 +00:00
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
2022-09-29 08:42:46 +00:00
if ( $deployment -> isEmpty ()) {
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
}
2024-07-25 08:36:11 +00:00
$path = $deployment -> getAttribute ( 'path' );
2024-09-05 02:25:11 +00:00
if ( empty ( $path ) || ! $deviceForFunctions -> exists ( $path )) {
2024-07-25 08:36:11 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
}
2023-08-20 09:43:05 +00:00
$deploymentId = ID :: unique ();
2022-09-29 08:42:46 +00:00
2024-07-25 08:36:11 +00:00
$destination = $deviceForFunctions -> getPath ( $deploymentId . '.' . \pathinfo ( 'code.tar.gz' , PATHINFO_EXTENSION ));
$deviceForFunctions -> transfer ( $path , $destination , $deviceForFunctions );
2023-09-13 19:18:50 +00:00
$deployment -> removeAttribute ( '$internalId' );
2023-08-19 06:15:47 +00:00
$deployment = $dbForProject -> createDocument ( 'deployments' , $deployment -> setAttributes ([
2024-07-25 08:36:11 +00:00
'$internalId' => '' ,
2023-08-20 09:43:05 +00:00
'$id' => $deploymentId ,
2023-08-19 06:15:47 +00:00
'buildId' => '' ,
'buildInternalId' => '' ,
2024-07-25 08:36:11 +00:00
'path' => $destination ,
2023-08-19 06:15:47 +00:00
'entrypoint' => $function -> getAttribute ( 'entrypoint' ),
'commands' => $function -> getAttribute ( 'commands' , '' ),
2023-08-20 09:43:05 +00:00
'search' => implode ( ' ' , [ $deploymentId , $function -> getAttribute ( 'entrypoint' )]),
2023-08-19 06:15:47 +00:00
]));
2022-09-29 08:42:46 +00:00
2022-12-20 16:11:30 +00:00
$queueForBuilds
2023-08-19 06:15:47 +00:00
-> setType ( BUILD_TYPE_DEPLOYMENT )
2022-09-29 08:42:46 +00:00
-> setResource ( $function )
2024-02-20 11:40:55 +00:00
-> setDeployment ( $deployment );
2022-09-29 08:42:46 +00:00
2023-09-27 15:51:17 +00:00
$queueForEvents
2023-08-17 21:37:52 +00:00
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'deploymentId' , $deployment -> getId ());
2022-09-29 08:42:46 +00:00
$response -> noContent ();
});
2024-05-27 12:51:28 +00:00
App :: patch ( '/v1/functions/:functionId/deployments/:deploymentId/build' )
2024-02-18 12:31:25 +00:00
-> groups ([ 'api' , 'functions' ])
2024-05-30 20:23:29 +00:00
-> desc ( 'Cancel deployment' )
2024-02-18 12:31:25 +00:00
-> label ( 'scope' , 'functions.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2024-02-18 12:31:25 +00:00
-> label ( 'audits.event' , 'deployment.update' )
-> label ( 'audits.resource' , 'function/{request.functionId}' )
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
-> label ( 'sdk.namespace' , 'functions' )
2024-05-30 20:23:29 +00:00
-> label ( 'sdk.method' , 'updateDeploymentBuild' )
2024-02-18 17:55:57 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_BUILD )
2024-02-18 12:31:25 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
-> param ( 'deploymentId' , '' , new UID (), 'Deployment ID.' )
-> inject ( 'response' )
-> inject ( 'dbForProject' )
-> inject ( 'project' )
-> inject ( 'queueForEvents' )
2024-03-04 20:30:06 +00:00
-> action ( function ( string $functionId , string $deploymentId , Response $response , Database $dbForProject , Document $project , Event $queueForEvents ) {
2024-02-18 12:31:25 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
}
$deployment = $dbForProject -> getDocument ( 'deployments' , $deploymentId );
if ( $deployment -> isEmpty ()) {
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND );
}
2024-03-04 20:30:06 +00:00
$build = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'builds' , $deployment -> getAttribute ( 'buildId' , '' )));
2024-02-18 12:31:25 +00:00
if ( $build -> isEmpty ()) {
2024-05-30 20:23:29 +00:00
$buildId = ID :: unique ();
$build = $dbForProject -> createDocument ( 'builds' , new Document ([
'$id' => $buildId ,
'$permissions' => [],
'startTime' => DateTime :: now (),
'deploymentInternalId' => $deployment -> getInternalId (),
'deploymentId' => $deployment -> getId (),
2024-06-12 10:21:48 +00:00
'status' => 'canceled' ,
2024-05-30 20:23:29 +00:00
'path' => '' ,
'runtime' => $function -> getAttribute ( 'runtime' ),
'source' => $deployment -> getAttribute ( 'path' , '' ),
'sourceType' => '' ,
'logs' => '' ,
'duration' => 0 ,
'size' => 0
]));
2024-02-18 12:31:25 +00:00
2024-05-30 20:23:29 +00:00
$deployment -> setAttribute ( 'buildId' , $build -> getId ());
$deployment -> setAttribute ( 'buildInternalId' , $build -> getInternalId ());
$deployment = $dbForProject -> updateDocument ( 'deployments' , $deployment -> getId (), $deployment );
} else {
if ( \in_array ( $build -> getAttribute ( 'status' ), [ 'ready' , 'failed' ])) {
throw new Exception ( Exception :: BUILD_ALREADY_COMPLETED );
}
2024-06-11 14:50:50 +00:00
2024-06-11 14:48:55 +00:00
$startTime = new \DateTime ( $build -> getAttribute ( 'startTime' ));
$endTime = new \DateTime ( 'now' );
$duration = $endTime -> getTimestamp () - $startTime -> getTimestamp ();
2024-06-07 11:23:33 +00:00
$build = $dbForProject -> updateDocument ( 'builds' , $build -> getId (), $build -> setAttributes ([
'endTime' => DateTime :: now (),
'duration' => $duration ,
2024-06-12 10:21:48 +00:00
'status' => 'canceled'
2024-06-07 11:23:33 +00:00
]));
2024-04-30 10:16:58 +00:00
}
2024-08-22 10:51:08 +00:00
$dbForProject -> purgeCachedDocument ( 'deployments' , $deployment -> getId ());
try {
$executor = new Executor ( App :: getEnv ( '_APP_EXECUTOR_HOST' ));
$executor -> deleteRuntime ( $project -> getId (), $deploymentId . " -build " );
} catch ( \Throwable $th ) {
// Don't throw if the deployment doesn't exist
if ( $th -> getCode () !== 404 ) {
throw $th ;
}
}
2024-02-18 12:31:25 +00:00
$queueForEvents
-> setParam ( 'functionId' , $function -> getId ())
2023-08-17 21:37:52 +00:00
-> setParam ( 'deploymentId' , $deployment -> getId ());
2024-02-18 17:55:57 +00:00
$response -> dynamic ( $build , Response :: MODEL_BUILD );
2022-09-29 08:42:46 +00:00
});
2020-07-12 21:18:52 +00:00
App :: post ( '/v1/functions/:functionId/executions' )
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'Create execution' )
2020-12-30 07:26:01 +00:00
-> label ( 'scope' , 'execution.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2022-04-13 12:39:31 +00:00
-> label ( 'event' , 'functions.[functionId].executions.[executionId].create' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2025-02-03 09:32:01 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'createExecution' ,
description : '/docs/references/functions/create-execution.md' ,
auth : [ AuthType :: SESSION , AuthType :: KEY , AuthType :: JWT ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_CREATED ,
model : Response :: MODEL_EXECUTION ,
)
],
contentType : ContentType :: MULTIPART ,
requestType : 'application/json' ,
))
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2024-10-08 01:17:34 +00:00
-> param ( 'body' , '' , new Text ( 10485760 , 0 ), 'HTTP body of execution. Default value is empty string.' , true )
2024-09-20 19:00:59 +00:00
-> param ( 'async' , false , new Boolean ( true ), 'Execute code in the background. Default value is false.' , true )
2023-09-06 08:16:01 +00:00
-> param ( 'path' , '/' , new Text ( 2048 ), 'HTTP path of execution. Path can include query params. Default value is /' , true )
2023-07-01 06:46:21 +00:00
-> param ( 'method' , 'POST' , new Whitelist ([ 'GET' , 'POST' , 'PUT' , 'PATCH' , 'DELETE' , 'OPTIONS' ], true ), 'HTTP method of execution. Default value is GET.' , true )
2024-08-15 12:12:15 +00:00
-> param ( 'headers' , [], new AnyOf ([ new Assoc (), new Text ( 65535 )], AnyOf :: TYPE_MIXED ), 'HTTP headers of execution. Defaults to empty.' , true )
2024-11-04 08:50:22 +00:00
-> param ( 'scheduledAt' , null , new DatetimeValidator ( requireDateInFuture : true , precision : DateTimeValidator :: PRECISION_MINUTES , offset : 60 ), 'Scheduled execution time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.' , true )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2024-08-05 13:08:41 +00:00
-> inject ( 'request' )
2020-12-26 15:20:08 +00:00
-> inject ( 'project' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2025-02-03 09:32:01 +00:00
-> inject ( 'dbForPlatform' )
2021-03-10 16:58:46 +00:00
-> inject ( 'user' )
2022-12-20 16:11:30 +00:00
-> inject ( 'queueForEvents' )
2023-10-25 07:39:59 +00:00
-> inject ( 'queueForUsage' )
2022-11-16 10:33:11 +00:00
-> inject ( 'queueForFunctions' )
2023-06-22 10:59:41 +00:00
-> inject ( 'geodb' )
2025-02-03 09:32:01 +00:00
-> action ( function ( string $functionId , string $body , mixed $async , string $path , string $method , mixed $headers , ? string $scheduledAt , Response $response , Request $request , Document $project , Database $dbForProject , Database $dbForPlatform , Document $user , Event $queueForEvents , Usage $queueForUsage , Func $queueForFunctions , Reader $geodb ) {
2024-09-25 08:59:51 +00:00
$async = \strval ( $async ) === 'true' || \strval ( $async ) === '1' ;
2024-06-07 19:05:29 +00:00
2024-09-05 02:25:11 +00:00
if ( ! $async && ! is_null ( $scheduledAt )) {
2024-06-13 08:24:51 +00:00
throw new Exception ( Exception :: GENERAL_BAD_REQUEST , 'Scheduled executions must run asynchronously. Set scheduledAt to a future date, or set async to true.' );
2024-06-07 19:05:29 +00:00
}
2020-07-16 21:50:37 +00:00
2024-08-05 13:08:41 +00:00
/**
* @ var array < string , mixed > $headers
*/
$assocParams = [ 'headers' ];
foreach ( $assocParams as $assocParam ) {
if ( ! empty ( 'headers' ) && ! is_array ( $$assocParam )) {
$$assocParam = \json_decode ( $$assocParam , true );
}
}
2024-08-06 17:31:09 +00:00
$booleanParams = [ 'async' ];
foreach ( $booleanParams as $booleamParam ) {
if ( ! empty ( $$booleamParam ) && ! is_bool ( $$booleamParam )) {
$$booleamParam = $$booleamParam === " true " ? true : false ;
}
}
2024-08-05 13:08:41 +00:00
// 'headers' validator
2024-08-06 08:19:28 +00:00
$validator = new Headers ();
2024-08-05 13:08:41 +00:00
if ( ! $validator -> isValid ( $headers )) {
throw new Exception ( $validator -> getDescription (), 400 );
}
2022-04-13 12:39:31 +00:00
$function = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'functions' , $functionId ));
2020-07-12 21:18:52 +00:00
2023-08-16 21:58:25 +00:00
$isAPIKey = Auth :: isAppUser ( Authorization :: getRoles ());
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: getRoles ());
2023-07-31 18:24:21 +00:00
2023-08-16 21:58:25 +00:00
if ( $function -> isEmpty () || ( ! $function -> getAttribute ( 'enabled' ) && ! $isAPIKey && ! $isPrivilegedUser )) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2020-07-16 21:50:37 +00:00
2023-09-04 17:53:25 +00:00
$version = $function -> getAttribute ( 'version' , 'v2' );
$runtimes = Config :: getParam ( $version === 'v2' ? 'runtimes-v2' : 'runtimes' , []);
2024-10-27 16:22:45 +00:00
$spec = Config :: getParam ( 'runtime-specifications' )[ $function -> getAttribute ( 'specification' , APP_COMPUTE_SPECIFICATION_DEFAULT )];
2022-02-05 19:49:57 +00:00
$runtime = ( isset ( $runtimes [ $function -> getAttribute ( 'runtime' , '' )])) ? $runtimes [ $function -> getAttribute ( 'runtime' , '' )] : null ;
2022-02-04 01:29:40 +00:00
if ( \is_null ( $runtime )) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: FUNCTION_RUNTIME_UNSUPPORTED , 'Runtime "' . $function -> getAttribute ( 'runtime' , '' ) . '" is not supported' );
2022-02-04 01:29:40 +00:00
}
2022-04-13 12:39:31 +00:00
$deployment = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'deployments' , $function -> getAttribute ( 'deployment' , '' )));
2020-07-16 21:50:37 +00:00
2022-01-25 16:51:05 +00:00
if ( $deployment -> getAttribute ( 'resourceId' ) !== $function -> getId ()) {
2022-08-14 06:56:12 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND , 'Deployment not found. Create a deployment before trying to execute a function' );
2020-07-16 21:50:37 +00:00
}
2022-01-24 23:16:53 +00:00
if ( $deployment -> isEmpty ()) {
2022-08-14 06:56:12 +00:00
throw new Exception ( Exception :: DEPLOYMENT_NOT_FOUND , 'Deployment not found. Create a deployment before trying to execute a function' );
2020-07-16 21:50:37 +00:00
}
2020-12-29 23:00:44 +00:00
2022-02-03 00:05:03 +00:00
/** Check if build has completed */
2022-04-13 12:39:31 +00:00
$build = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'builds' , $deployment -> getAttribute ( 'buildId' , '' )));
2022-02-03 00:05:03 +00:00
if ( $build -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: BUILD_NOT_FOUND );
2022-02-03 00:05:03 +00:00
}
if ( $build -> getAttribute ( 'status' ) !== 'ready' ) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: BUILD_NOT_READY );
2022-02-03 00:05:03 +00:00
}
2022-01-18 06:38:10 +00:00
$validator = new Authorization ( 'execute' );
2020-12-29 23:00:44 +00:00
2021-05-04 21:25:17 +00:00
if ( ! $validator -> isValid ( $function -> getAttribute ( 'execute' ))) { // Check if user has write access to execute function
2022-07-26 14:56:59 +00:00
throw new Exception ( Exception :: USER_UNAUTHORIZED , $validator -> getDescription ());
2020-12-29 23:00:44 +00:00
}
2024-01-29 11:21:51 +00:00
$jwt = '' ; // initialize
2021-06-20 13:59:36 +00:00
if ( ! $user -> isEmpty ()) { // If userId exists, generate a JWT for function
2021-07-22 14:49:52 +00:00
$sessions = $user -> getAttribute ( 'sessions' , []);
$current = new Document ();
2021-03-10 16:58:46 +00:00
2022-04-13 12:39:31 +00:00
foreach ( $sessions as $session ) {
/** @var Utopia\Database\Document $session */
2021-07-22 14:49:52 +00:00
if ( $session -> getAttribute ( 'secret' ) == Auth :: hash ( Auth :: $secret )) { // If current session delete the cookies too
$current = $session ;
2021-03-10 16:58:46 +00:00
}
}
2022-04-13 12:39:31 +00:00
if ( ! $current -> isEmpty ()) {
2024-05-28 09:25:54 +00:00
$jwtExpiry = $function -> getAttribute ( 'timeout' , 900 );
2024-05-29 07:51:51 +00:00
$jwtObj = new JWT ( System :: getEnv ( '_APP_OPENSSL_KEY_V1' ), 'HS256' , $jwtExpiry , 0 );
2024-01-29 11:21:51 +00:00
$jwt = $jwtObj -> encode ([
2021-03-10 17:48:05 +00:00
'userId' => $user -> getId (),
2021-07-22 14:49:52 +00:00
'sessionId' => $current -> getId (),
2021-03-10 17:48:05 +00:00
]);
2021-03-10 16:58:46 +00:00
}
}
2024-01-29 11:15:07 +00:00
$jwtExpiry = $function -> getAttribute ( 'timeout' , 900 );
2024-05-29 07:51:51 +00:00
$jwtObj = new JWT ( System :: getEnv ( '_APP_OPENSSL_KEY_V1' ), 'HS256' , $jwtExpiry , 0 );
2024-05-06 09:55:59 +00:00
$apiKey = $jwtObj -> encode ([
2024-01-29 11:15:07 +00:00
'projectId' => $project -> getId (),
2024-05-06 09:55:59 +00:00
'scopes' => $function -> getAttribute ( 'scopes' , [])
2024-01-29 11:15:07 +00:00
]);
2024-05-14 11:58:31 +00:00
$headers [ 'x-appwrite-key' ] = API_KEY_DYNAMIC . '_' . $apiKey ;
2023-06-22 10:59:41 +00:00
$headers [ 'x-appwrite-trigger' ] = 'http' ;
$headers [ 'x-appwrite-user-id' ] = $user -> getId () ? ? '' ;
2024-01-29 11:21:51 +00:00
$headers [ 'x-appwrite-user-jwt' ] = $jwt ? ? '' ;
2023-06-22 10:59:41 +00:00
$headers [ 'x-appwrite-country-code' ] = '' ;
$headers [ 'x-appwrite-continent-code' ] = '' ;
$headers [ 'x-appwrite-continent-eu' ] = 'false' ;
$ip = $headers [ 'x-real-ip' ] ? ? '' ;
if ( ! empty ( $ip )) {
$record = $geodb -> get ( $ip );
if ( $record ) {
$eu = Config :: getParam ( 'locale-eu' );
2023-06-23 07:01:51 +00:00
$headers [ 'x-appwrite-country-code' ] = $record [ 'country' ][ 'iso_code' ] ? ? '' ;
2023-06-22 10:59:41 +00:00
$headers [ 'x-appwrite-continent-code' ] = $record [ 'continent' ][ 'code' ] ? ? '' ;
$headers [ 'x-appwrite-continent-eu' ] = ( \in_array ( $record [ 'country' ][ 'iso_code' ], $eu )) ? 'true' : 'false' ;
}
}
2023-07-30 07:17:23 +00:00
$headersFiltered = [];
foreach ( $headers as $key => $value ) {
2023-08-11 13:34:57 +00:00
if ( \in_array ( \strtolower ( $key ), FUNCTION_ALLOWLIST_HEADERS_REQUEST )) {
2023-08-09 15:53:58 +00:00
$headersFiltered [] = [ 'name' => $key , 'value' => $value ];
2023-07-30 07:17:23 +00:00
}
}
2023-07-29 16:20:20 +00:00
$executionId = ID :: unique ();
2024-06-07 19:05:29 +00:00
$status = $async ? 'waiting' : 'processing' ;
2024-09-05 02:25:11 +00:00
if ( ! is_null ( $scheduledAt )) {
2024-06-07 19:05:29 +00:00
$status = 'scheduled' ;
}
2023-07-29 16:20:20 +00:00
$execution = new Document ([
'$id' => $executionId ,
'$permissions' => ! $user -> isEmpty () ? [ Permission :: read ( Role :: user ( $user -> getId ()))] : [],
2024-11-29 12:57:51 +00:00
'resourceInternalId' => $function -> getInternalId (),
'resourceId' => $function -> getId (),
'resourceType' => 'functions' ,
2023-07-29 16:20:20 +00:00
'deploymentInternalId' => $deployment -> getInternalId (),
'deploymentId' => $deployment -> getId (),
2024-06-17 12:44:12 +00:00
'trigger' => ( ! is_null ( $scheduledAt )) ? 'schedule' : 'http' ,
2024-06-27 22:12:23 +00:00
'status' => $status , // waiting / processing / completed / failed / scheduled
2023-07-29 16:20:20 +00:00
'responseStatusCode' => 0 ,
'responseHeaders' => [],
'requestPath' => $path ,
'requestMethod' => $method ,
2023-07-30 07:17:23 +00:00
'requestHeaders' => $headersFiltered ,
2023-07-29 16:20:20 +00:00
'errors' => '' ,
'logs' => '' ,
'duration' => 0.0 ,
'search' => implode ( ' ' , [ $functionId , $executionId ]),
]);
2022-12-20 16:11:30 +00:00
$queueForEvents
2022-04-13 12:39:31 +00:00
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'executionId' , $execution -> getId ())
2022-06-22 10:51:49 +00:00
-> setContext ( 'function' , $function );
2022-04-13 12:39:31 +00:00
2021-08-24 09:32:27 +00:00
if ( $async ) {
2024-09-05 02:25:11 +00:00
if ( is_null ( $scheduledAt )) {
2024-07-22 10:36:18 +00:00
$execution = Authorization :: skip ( fn () => $dbForProject -> createDocument ( 'executions' , $execution ));
2024-06-07 19:05:29 +00:00
$queueForFunctions
-> setType ( 'http' )
-> setExecution ( $execution )
-> setFunction ( $function )
-> setBody ( $body )
-> setHeaders ( $headers )
-> setPath ( $path )
-> setMethod ( $method )
-> setJWT ( $jwt )
-> setProject ( $project )
-> setUser ( $user )
-> setParam ( 'functionId' , $function -> getId ())
-> setParam ( 'executionId' , $execution -> getId ())
-> trigger ();
} else {
2024-06-28 21:42:55 +00:00
$data = [
2024-06-26 11:30:23 +00:00
'headers' => $headers ,
'path' => $path ,
'method' => $method ,
'body' => $body ,
2024-09-07 10:20:23 +00:00
'userId' => $user -> getId ()
2024-06-26 11:30:23 +00:00
];
2025-02-03 09:32:01 +00:00
$schedule = $dbForPlatform -> createDocument ( 'schedules' , new Document ([
2024-06-07 19:05:29 +00:00
'region' => System :: getEnv ( '_APP_REGION' , 'default' ),
2024-06-27 22:12:23 +00:00
'resourceType' => ScheduleExecutions :: getSupportedResource (),
2024-06-17 12:44:12 +00:00
'resourceId' => $execution -> getId (),
'resourceInternalId' => $execution -> getInternalId (),
2024-06-07 19:05:29 +00:00
'resourceUpdatedAt' => DateTime :: now (),
'projectId' => $project -> getId (),
'schedule' => $scheduledAt ,
2024-06-28 21:42:55 +00:00
'data' => $data ,
2024-06-07 19:05:29 +00:00
'active' => true ,
]));
2024-07-22 10:36:18 +00:00
$execution = $execution
-> setAttribute ( 'scheduleId' , $schedule -> getId ())
-> setAttribute ( 'scheduleInternalId' , $schedule -> getInternalId ())
-> setAttribute ( 'scheduledAt' , $scheduledAt );
$execution = Authorization :: skip ( fn () => $dbForProject -> createDocument ( 'executions' , $execution ));
2024-06-07 19:05:29 +00:00
}
2020-07-12 21:18:52 +00:00
2022-09-07 11:02:36 +00:00
return $response
-> setStatusCode ( Response :: STATUS_CODE_ACCEPTED )
-> dynamic ( $execution , Response :: MODEL_EXECUTION );
2021-08-24 09:32:27 +00:00
}
2022-01-10 14:18:33 +00:00
2023-03-28 13:21:42 +00:00
$durationStart = \microtime ( true );
2022-08-10 13:43:05 +00:00
2023-03-11 16:06:02 +00:00
$vars = [];
2023-09-11 10:22:16 +00:00
// V2 vars
if ( $version === 'v2' ) {
$vars = \array_merge ( $vars , [
'APPWRITE_FUNCTION_TRIGGER' => $headers [ 'x-appwrite-trigger' ] ? ? '' ,
'APPWRITE_FUNCTION_DATA' => $body ? ? '' ,
'APPWRITE_FUNCTION_USER_ID' => $headers [ 'x-appwrite-user-id' ] ? ? '' ,
'APPWRITE_FUNCTION_JWT' => $headers [ 'x-appwrite-user-jwt' ] ? ? ''
]);
}
2023-03-11 16:06:02 +00:00
// Shared vars
2023-09-05 08:21:36 +00:00
foreach ( $function -> getAttribute ( 'varsProject' , []) as $var ) {
2023-08-18 06:55:44 +00:00
$vars [ $var -> getAttribute ( 'key' )] = $var -> getAttribute ( 'value' , '' );
}
2022-08-10 13:43:05 +00:00
2023-03-11 16:06:02 +00:00
// Function vars
2023-09-05 08:21:36 +00:00
foreach ( $function -> getAttribute ( 'vars' , []) as $var ) {
$vars [ $var -> getAttribute ( 'key' )] = $var -> getAttribute ( 'value' , '' );
}
2023-03-11 16:06:02 +00:00
2024-05-06 11:27:28 +00:00
$protocol = System :: getEnv ( '_APP_OPTIONS_FORCE_HTTPS' ) == 'disabled' ? 'http' : 'https' ;
$hostname = System :: getEnv ( '_APP_DOMAIN' );
2024-01-29 11:15:07 +00:00
$endpoint = $protocol . '://' . $hostname . " /v1 " ;
2023-03-11 16:06:02 +00:00
// Appwrite vars
2022-08-10 13:43:05 +00:00
$vars = \array_merge ( $vars , [
2024-05-09 11:50:45 +00:00
'APPWRITE_FUNCTION_API_ENDPOINT' => $endpoint ,
2023-06-22 10:59:41 +00:00
'APPWRITE_FUNCTION_ID' => $functionId ,
2022-09-30 09:52:35 +00:00
'APPWRITE_FUNCTION_NAME' => $function -> getAttribute ( 'name' ),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment -> getId (),
'APPWRITE_FUNCTION_PROJECT_ID' => $project -> getId (),
2022-09-19 11:58:41 +00:00
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime [ 'name' ] ? ? '' ,
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime [ 'version' ] ? ? '' ,
2024-10-27 16:22:45 +00:00
'APPWRITE_COMPUTE_CPUS' => $spec [ 'cpus' ] ? ? APP_COMPUTE_CPUS_DEFAULT ,
'APPWRITE_COMPUTE_MEMORY' => $spec [ 'memory' ] ? ? APP_COMPUTE_MEMORY_DEFAULT ,
2024-07-15 07:10:11 +00:00
'APPWRITE_VERSION' => APP_VERSION_STABLE ,
'APPWRITE_REGION' => $project -> getAttribute ( 'region' ),
2024-09-06 10:50:29 +00:00
'APPWRITE_DEPLOYMENT_TYPE' => $deployment -> getAttribute ( 'type' , '' ),
'APPWRITE_VCS_REPOSITORY_ID' => $deployment -> getAttribute ( 'providerRepositoryId' , '' ),
'APPWRITE_VCS_REPOSITORY_NAME' => $deployment -> getAttribute ( 'providerRepositoryName' , '' ),
'APPWRITE_VCS_REPOSITORY_OWNER' => $deployment -> getAttribute ( 'providerRepositoryOwner' , '' ),
'APPWRITE_VCS_REPOSITORY_URL' => $deployment -> getAttribute ( 'providerRepositoryUrl' , '' ),
'APPWRITE_VCS_REPOSITORY_BRANCH' => $deployment -> getAttribute ( 'providerBranch' , '' ),
'APPWRITE_VCS_REPOSITORY_BRANCH_URL' => $deployment -> getAttribute ( 'providerBranchUrl' , '' ),
'APPWRITE_VCS_COMMIT_HASH' => $deployment -> getAttribute ( 'providerCommitHash' , '' ),
'APPWRITE_VCS_COMMIT_MESSAGE' => $deployment -> getAttribute ( 'providerCommitMessage' , '' ),
'APPWRITE_VCS_COMMIT_URL' => $deployment -> getAttribute ( 'providerCommitUrl' , '' ),
'APPWRITE_VCS_COMMIT_AUTHOR_NAME' => $deployment -> getAttribute ( 'providerCommitAuthor' , '' ),
'APPWRITE_VCS_COMMIT_AUTHOR_URL' => $deployment -> getAttribute ( 'providerCommitAuthorUrl' , '' ),
'APPWRITE_VCS_ROOT_DIRECTORY' => $deployment -> getAttribute ( 'providerRootDirectory' , '' ),
2022-02-04 01:29:40 +00:00
]);
2022-02-03 00:05:03 +00:00
2022-02-05 19:49:57 +00:00
/** Execute function */
2024-04-01 11:02:47 +00:00
$executor = new Executor ( System :: getEnv ( '_APP_EXECUTOR_HOST' ));
2022-02-15 17:39:03 +00:00
try {
2023-09-04 17:53:25 +00:00
$version = $function -> getAttribute ( 'version' , 'v2' );
2023-07-24 06:23:23 +00:00
$command = $runtime [ 'startCommand' ];
2023-09-04 17:53:25 +00:00
$command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"' ;
2022-02-15 17:39:03 +00:00
$executionResponse = $executor -> createExecution (
projectId : $project -> getId (),
deploymentId : $deployment -> getId (),
2023-06-22 10:59:41 +00:00
body : \strlen ( $body ) > 0 ? $body : null ,
2022-11-08 08:49:45 +00:00
variables : $vars ,
2022-02-15 17:39:03 +00:00
timeout : $function -> getAttribute ( 'timeout' , 0 ),
2022-11-08 08:49:45 +00:00
image : $runtime [ 'image' ],
2023-03-15 06:08:43 +00:00
source : $build -> getAttribute ( 'path' , '' ),
2022-02-15 17:39:03 +00:00
entrypoint : $deployment -> getAttribute ( 'entrypoint' , '' ),
2023-09-04 17:53:25 +00:00
version : $version ,
2023-02-14 11:01:38 +00:00
path : $path ,
method : $method ,
headers : $headers ,
2023-10-15 17:41:09 +00:00
runtimeEntrypoint : $command ,
2024-10-27 16:22:45 +00:00
cpus : $spec [ 'cpus' ] ? ? APP_COMPUTE_CPUS_DEFAULT ,
memory : $spec [ 'memory' ] ? ? APP_COMPUTE_MEMORY_DEFAULT ,
2024-06-25 09:33:07 +00:00
logging : $function -> getAttribute ( 'logging' , true ),
2023-10-15 17:41:09 +00:00
requestTimeout : 30
2022-02-15 17:39:03 +00:00
);
2023-07-30 07:17:23 +00:00
$headersFiltered = [];
foreach ( $executionResponse [ 'headers' ] as $key => $value ) {
2023-08-11 13:34:57 +00:00
if ( \in_array ( \strtolower ( $key ), FUNCTION_ALLOWLIST_HEADERS_RESPONSE )) {
2023-08-09 15:53:58 +00:00
$headersFiltered [] = [ 'name' => $key , 'value' => $value ];
2023-07-30 07:17:23 +00:00
}
}
2023-07-29 16:20:20 +00:00
2022-02-15 17:39:03 +00:00
/** Update execution status */
2024-08-08 08:38:15 +00:00
$status = $executionResponse [ 'statusCode' ] >= 500 ? 'failed' : 'completed' ;
2023-02-02 19:21:00 +00:00
$execution -> setAttribute ( 'status' , $status );
2023-07-29 16:20:20 +00:00
$execution -> setAttribute ( 'responseStatusCode' , $executionResponse [ 'statusCode' ]);
2023-07-30 07:17:23 +00:00
$execution -> setAttribute ( 'responseHeaders' , $headersFiltered );
2023-02-14 11:01:38 +00:00
$execution -> setAttribute ( 'logs' , $executionResponse [ 'logs' ]);
$execution -> setAttribute ( 'errors' , $executionResponse [ 'errors' ]);
2022-09-09 12:02:04 +00:00
$execution -> setAttribute ( 'duration' , $executionResponse [ 'duration' ]);
2022-02-15 17:39:03 +00:00
} catch ( \Throwable $th ) {
2023-03-28 13:21:42 +00:00
$durationEnd = \microtime ( true );
2022-07-02 14:25:44 +00:00
$execution
2023-03-28 13:21:42 +00:00
-> setAttribute ( 'duration' , $durationEnd - $durationStart )
2022-07-02 14:25:44 +00:00
-> setAttribute ( 'status' , 'failed' )
2023-07-29 16:20:20 +00:00
-> setAttribute ( 'responseStatusCode' , 500 )
2023-02-15 08:36:20 +00:00
-> setAttribute ( 'errors' , $th -> getMessage () . '\nError Code: ' . $th -> getCode ());
2022-02-15 17:39:03 +00:00
Console :: error ( $th -> getMessage ());
2024-05-20 10:44:08 +00:00
if ( $th instanceof AppwriteException ) {
throw $th ;
}
2023-12-14 04:49:16 +00:00
} finally {
2023-12-12 19:21:47 +00:00
$queueForUsage
-> addMetric ( METRIC_EXECUTIONS , 1 )
-> addMetric ( str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_EXECUTIONS ), 1 )
2023-12-14 04:49:16 +00:00
-> addMetric ( METRIC_EXECUTIONS_COMPUTE , ( int )( $execution -> getAttribute ( 'duration' ) * 1000 )) // per project
-> addMetric ( str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE ), ( int )( $execution -> getAttribute ( 'duration' ) * 1000 )) // per function
2024-10-27 16:22:45 +00:00
-> addMetric ( METRIC_EXECUTIONS_MB_SECONDS , ( int )(( $spec [ 'memory' ] ? ? APP_COMPUTE_MEMORY_DEFAULT ) * $execution -> getAttribute ( 'duration' , 0 ) * ( $spec [ 'cpus' ] ? ? APP_COMPUTE_CPUS_DEFAULT )))
-> addMetric ( str_replace ( '{functionInternalId}' , $function -> getInternalId (), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS ), ( int )(( $spec [ 'memory' ] ? ? APP_COMPUTE_MEMORY_DEFAULT ) * $execution -> getAttribute ( 'duration' , 0 ) * ( $spec [ 'cpus' ] ? ? APP_COMPUTE_CPUS_DEFAULT )))
2023-12-12 19:21:47 +00:00
;
2022-02-04 01:29:40 +00:00
2024-07-02 11:38:59 +00:00
$execution = Authorization :: skip ( fn () => $dbForProject -> createDocument ( 'executions' , $execution ));
2023-10-05 13:15:39 +00:00
}
2022-08-14 08:44:17 +00:00
$roles = Authorization :: getRoles ();
$isPrivilegedUser = Auth :: isPrivilegedUser ( $roles );
$isAppUser = Auth :: isAppUser ( $roles );
2022-09-06 00:28:57 +00:00
2022-08-16 05:23:51 +00:00
if ( ! $isPrivilegedUser && ! $isAppUser ) {
2023-02-14 11:01:38 +00:00
$execution -> setAttribute ( 'logs' , '' );
$execution -> setAttribute ( 'errors' , '' );
2022-08-16 04:34:49 +00:00
}
2022-07-13 08:57:57 +00:00
2023-08-06 13:11:30 +00:00
$headers = [];
2023-08-16 06:19:42 +00:00
foreach (( $executionResponse [ 'headers' ] ? ? []) as $key => $value ) {
2023-08-09 15:53:58 +00:00
$headers [] = [ 'name' => $key , 'value' => $value ];
2023-08-06 13:11:30 +00:00
}
2023-08-16 06:19:42 +00:00
$execution -> setAttribute ( 'responseBody' , $executionResponse [ 'body' ] ? ? '' );
2023-08-06 13:11:30 +00:00
$execution -> setAttribute ( 'responseHeaders' , $headers );
2023-02-27 10:20:37 +00:00
2024-08-11 17:36:05 +00:00
$acceptTypes = \explode ( ', ' , $request -> getHeader ( 'accept' ));
2024-08-05 13:08:41 +00:00
foreach ( $acceptTypes as $acceptType ) {
2024-09-05 02:25:11 +00:00
if ( \str_starts_with ( $acceptType , 'application/json' ) || \str_starts_with ( $acceptType , 'application/*' )) {
2024-08-11 17:36:05 +00:00
$response -> setContentType ( Response :: CONTENT_TYPE_JSON );
break ;
} elseif ( \str_starts_with ( $acceptType , 'multipart/form-data' ) || \str_starts_with ( $acceptType , 'multipart/*' )) {
$response -> setContentType ( Response :: CONTENT_TYPE_MULTIPART );
2024-08-05 13:08:41 +00:00
break ;
}
}
2024-08-07 13:15:53 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_CREATED )
-> dynamic ( $execution , Response :: MODEL_EXECUTION );
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
App :: get ( '/v1/functions/:functionId/executions' )
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'List executions' )
2020-12-30 07:26:01 +00:00
-> label ( 'scope' , 'execution.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2025-02-03 09:32:01 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'listExecutions' ,
description : '/docs/references/functions/list-executions.md' ,
auth : [ AuthType :: SESSION , AuthType :: KEY , AuthType :: JWT ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_EXECUTION_LIST ,
)
]
))
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
2023-03-29 19:38:39 +00:00
-> param ( 'queries' , [], new Executions (), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode ( ', ' , Executions :: ALLOWED_ATTRIBUTES ), true )
2021-09-27 10:12:42 +00:00
-> param ( 'search' , '' , new Text ( 256 ), 'Search term to filter your list results. Max length: 256 chars.' , true )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-09-09 08:49:18 +00:00
-> inject ( 'mode' )
-> action ( function ( string $functionId , array $queries , string $search , Response $response , Database $dbForProject , string $mode ) {
2022-04-13 12:39:31 +00:00
$function = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'functions' , $functionId ));
2020-05-05 17:30:12 +00:00
2023-08-16 21:58:25 +00:00
$isAPIKey = Auth :: isAppUser ( Authorization :: getRoles ());
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: getRoles ());
2023-07-31 18:24:21 +00:00
2023-08-16 21:58:25 +00:00
if ( $function -> isEmpty () || ( ! $function -> getAttribute ( 'enabled' ) && ! $isAPIKey && ! $isPrivilegedUser )) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2021-05-27 10:09:14 +00:00
2024-02-12 16:02:04 +00:00
try {
$queries = Query :: parseQueries ( $queries );
} catch ( QueryException $e ) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $e -> getMessage ());
}
2021-08-06 12:36:05 +00:00
2022-08-11 23:53:52 +00:00
if ( ! empty ( $search )) {
2022-08-23 09:26:34 +00:00
$queries [] = Query :: search ( 'search' , $search );
2021-08-06 12:36:05 +00:00
}
2022-08-23 09:26:34 +00:00
// Set internal queries
2024-12-02 13:18:50 +00:00
$queries [] = Query :: equal ( 'resourceInternalId' , [ $function -> getInternalId ()]);
2024-11-29 12:57:51 +00:00
$queries [] = Query :: equal ( 'resourceType' , [ 'functions' ]);
2021-09-27 10:12:42 +00:00
2024-02-12 09:55:45 +00:00
/**
2024-02-12 10:03:31 +00:00
* Get cursor document if there was a cursor query , we use array_filter and reset for reference $cursor to $queries
2024-02-12 09:55:45 +00:00
*/
2023-08-22 03:25:55 +00:00
$cursor = \array_filter ( $queries , function ( $query ) {
2023-12-06 14:10:40 +00:00
return \in_array ( $query -> getMethod (), [ Query :: TYPE_CURSOR_AFTER , Query :: TYPE_CURSOR_BEFORE ]);
2023-08-22 03:25:55 +00:00
});
2022-08-30 23:31:43 +00:00
$cursor = reset ( $cursor );
2022-08-30 11:55:23 +00:00
if ( $cursor ) {
2022-08-23 09:26:34 +00:00
/** @var Query $cursor */
2024-10-17 05:41:24 +00:00
$validator = new Cursor ();
if ( ! $validator -> isValid ( $cursor )) {
throw new Exception ( Exception :: GENERAL_QUERY_INVALID , $validator -> getDescription ());
}
2022-08-23 09:26:34 +00:00
$executionId = $cursor -> getValue ();
$cursorDocument = $dbForProject -> getDocument ( 'executions' , $executionId );
2021-08-06 12:36:05 +00:00
2022-08-11 23:53:52 +00:00
if ( $cursorDocument -> isEmpty ()) {
2022-08-23 09:26:34 +00:00
throw new Exception ( Exception :: GENERAL_CURSOR_NOT_FOUND , " Execution ' { $executionId } ' for the 'cursor' value not found. " );
2021-08-06 12:36:05 +00:00
}
2022-08-23 09:26:34 +00:00
$cursor -> setValue ( $cursorDocument );
2021-09-27 10:12:42 +00:00
}
2022-08-23 09:26:34 +00:00
$filterQueries = Query :: groupByType ( $queries )[ 'filters' ];
$results = $dbForProject -> find ( 'executions' , $queries );
2022-08-11 23:53:52 +00:00
$total = $dbForProject -> count ( 'executions' , $filterQueries , APP_LIMIT_COUNT );
2020-07-12 21:18:52 +00:00
2022-08-14 08:54:11 +00:00
$roles = Authorization :: getRoles ();
$isPrivilegedUser = Auth :: isPrivilegedUser ( $roles );
$isAppUser = Auth :: isAppUser ( $roles );
2022-08-16 04:34:49 +00:00
if ( ! $isPrivilegedUser && ! $isAppUser ) {
2022-08-14 08:54:11 +00:00
$results = array_map ( function ( $execution ) {
2023-02-14 11:01:38 +00:00
$execution -> setAttribute ( 'logs' , '' );
$execution -> setAttribute ( 'errors' , '' );
2022-08-14 09:23:41 +00:00
return $execution ;
2022-08-14 08:54:11 +00:00
}, $results );
}
2020-07-12 21:18:52 +00:00
2020-10-30 19:53:27 +00:00
$response -> dynamic ( new Document ([
2021-05-27 10:09:14 +00:00
'executions' => $results ,
2022-02-27 09:57:09 +00:00
'total' => $total ,
2020-10-30 19:53:27 +00:00
]), Response :: MODEL_EXECUTION_LIST );
2020-12-26 15:20:08 +00:00
});
2020-07-12 21:18:52 +00:00
App :: get ( '/v1/functions/:functionId/executions/:executionId' )
-> groups ([ 'api' , 'functions' ])
2023-10-02 14:02:48 +00:00
-> desc ( 'Get execution' )
2020-12-30 07:26:01 +00:00
-> label ( 'scope' , 'execution.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2025-02-03 09:32:01 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'getExecution' ,
description : '/docs/references/functions/get-execution.md' ,
auth : [ AuthType :: SESSION , AuthType :: KEY , AuthType :: JWT ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_EXECUTION ,
)
]
))
2021-12-10 12:27:11 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
-> param ( 'executionId' , '' , new UID (), 'Execution ID.' )
2020-12-26 15:20:08 +00:00
-> inject ( 'response' )
2021-12-27 12:45:23 +00:00
-> inject ( 'dbForProject' )
2022-09-09 08:49:18 +00:00
-> inject ( 'mode' )
-> action ( function ( string $functionId , string $executionId , Response $response , Database $dbForProject , string $mode ) {
2022-04-13 12:39:31 +00:00
$function = Authorization :: skip ( fn () => $dbForProject -> getDocument ( 'functions' , $functionId ));
2020-05-05 17:30:12 +00:00
2023-08-16 21:58:25 +00:00
$isAPIKey = Auth :: isAppUser ( Authorization :: getRoles ());
$isPrivilegedUser = Auth :: isPrivilegedUser ( Authorization :: getRoles ());
2023-07-31 18:24:21 +00:00
2023-08-16 21:58:25 +00:00
if ( $function -> isEmpty () || ( ! $function -> getAttribute ( 'enabled' ) && ! $isAPIKey && ! $isPrivilegedUser )) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2020-05-05 17:30:12 +00:00
2021-12-27 12:45:23 +00:00
$execution = $dbForProject -> getDocument ( 'executions' , $executionId );
2020-05-05 17:30:12 +00:00
2024-12-02 13:18:50 +00:00
if ( $execution -> getAttribute ( 'resourceType' ) !== 'functions' && $execution -> getAttribute ( 'resourceInternalId' ) !== $function -> getInternalId ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: EXECUTION_NOT_FOUND );
2020-07-12 21:18:52 +00:00
}
2020-05-05 17:30:12 +00:00
2021-06-20 13:59:36 +00:00
if ( $execution -> isEmpty ()) {
2022-07-26 14:24:32 +00:00
throw new Exception ( Exception :: EXECUTION_NOT_FOUND );
2020-05-05 17:30:12 +00:00
}
2020-07-12 21:18:52 +00:00
2022-08-14 08:44:17 +00:00
$roles = Authorization :: getRoles ();
$isPrivilegedUser = Auth :: isPrivilegedUser ( $roles );
$isAppUser = Auth :: isAppUser ( $roles );
2022-08-16 05:23:51 +00:00
if ( ! $isPrivilegedUser && ! $isAppUser ) {
2023-02-14 11:01:38 +00:00
$execution -> setAttribute ( 'logs' , '' );
$execution -> setAttribute ( 'errors' , '' );
2020-05-05 17:30:12 +00:00
}
2020-07-12 21:18:52 +00:00
2020-10-30 19:53:27 +00:00
$response -> dynamic ( $execution , Response :: MODEL_EXECUTION );
2020-12-26 15:20:08 +00:00
});
2021-12-06 14:12:41 +00:00
2024-06-27 15:17:14 +00:00
App :: delete ( '/v1/functions/:functionId/executions/:executionId' )
-> groups ([ 'api' , 'functions' ])
-> desc ( 'Delete execution' )
-> label ( 'scope' , 'execution.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2024-06-27 15:17:14 +00:00
-> label ( 'event' , 'functions.[functionId].executions.[executionId].delete' )
-> label ( 'audits.event' , 'executions.delete' )
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2025-02-03 09:32:01 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'deleteExecution' ,
description : '/docs/references/functions/delete-execution.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_NOCONTENT ,
model : Response :: MODEL_NONE ,
)
],
contentType : ContentType :: NONE
))
2024-06-27 15:17:14 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function ID.' )
-> param ( 'executionId' , '' , new UID (), 'Execution ID.' )
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2025-02-03 09:32:01 +00:00
-> inject ( 'dbForPlatform' )
2024-06-27 15:17:14 +00:00
-> inject ( 'queueForEvents' )
2025-02-03 09:32:01 +00:00
-> action ( function ( string $functionId , string $executionId , Response $response , Database $dbForProject , Database $dbForPlatform , Event $queueForEvents ) {
2024-06-27 15:17:14 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
}
$execution = $dbForProject -> getDocument ( 'executions' , $executionId );
if ( $execution -> isEmpty ()) {
throw new Exception ( Exception :: EXECUTION_NOT_FOUND );
}
2024-12-02 13:18:50 +00:00
if ( $execution -> getAttribute ( 'resourceType' ) !== 'functions' && $execution -> getAttribute ( 'resourceInternalId' ) !== $function -> getInternalId ()) {
2024-06-27 15:17:14 +00:00
throw new Exception ( Exception :: EXECUTION_NOT_FOUND );
}
2024-06-27 22:12:23 +00:00
$status = $execution -> getAttribute ( 'status' );
2024-06-27 15:17:14 +00:00
2024-06-27 22:12:23 +00:00
if ( ! in_array ( $status , [ 'completed' , 'failed' , 'scheduled' ])) {
2024-06-27 15:17:14 +00:00
throw new Exception ( Exception :: EXECUTION_IN_PROGRESS );
}
if ( ! $dbForProject -> deleteDocument ( 'executions' , $execution -> getId ())) {
throw new Exception ( Exception :: GENERAL_SERVER_ERROR , 'Failed to remove execution from DB' );
}
2024-06-27 22:12:23 +00:00
if ( $status === 'scheduled' ) {
2025-02-03 09:32:01 +00:00
$schedule = $dbForPlatform -> findOne ( 'schedules' , [
2024-06-27 22:12:23 +00:00
Query :: equal ( 'resourceId' , [ $execution -> getId ()]),
Query :: equal ( 'resourceType' , [ ScheduleExecutions :: getSupportedResource ()]),
Query :: equal ( 'active' , [ true ]),
]);
2024-10-31 08:13:23 +00:00
if ( ! $schedule -> isEmpty ()) {
2024-06-27 22:12:23 +00:00
$schedule
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
-> setAttribute ( 'active' , false );
2025-02-03 09:32:01 +00:00
Authorization :: skip ( fn () => $dbForPlatform -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
2024-06-27 22:12:23 +00:00
}
}
2024-06-27 15:17:14 +00:00
$queueForEvents
-> setParam ( 'functionId' , $function -> getId ())
2024-07-03 07:02:41 +00:00
-> setParam ( 'executionId' , $execution -> getId ())
-> setPayload ( $response -> output ( $execution , Response :: MODEL_EXECUTION ));
2024-06-27 15:17:14 +00:00
$response -> noContent ();
});
2022-08-01 15:13:47 +00:00
// Variables
2022-08-09 15:29:24 +00:00
App :: post ( '/v1/functions/:functionId/variables' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Create variable' )
2022-08-01 15:13:47 +00:00
-> groups ([ 'api' , 'functions' ])
-> label ( 'scope' , 'functions.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'variable.create' )
2022-09-04 08:13:44 +00:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2025-02-03 11:38:27 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'createVariable' ,
description : '/docs/references/functions/create-variable.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_CREATED ,
model : Response :: MODEL_VARIABLE ,
)
]
))
2022-09-19 10:05:42 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function unique ID.' , false )
2022-08-30 07:27:44 +00:00
-> param ( 'key' , null , new Text ( Database :: LENGTH_KEY ), 'Variable key. Max length: ' . Database :: LENGTH_KEY . ' chars.' , false )
2023-08-12 19:08:44 +00:00
-> param ( 'value' , null , new Text ( 8192 , 0 ), 'Variable value. Max length: 8192 chars.' , false )
2024-10-24 08:59:47 +00:00
-> param ( 'secret' , false , new Boolean (), 'Is secret? Secret variables can only be updated or deleted, they cannot be read.' , true )
2022-08-01 15:13:47 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2025-02-03 09:32:01 +00:00
-> inject ( 'dbForPlatform' )
-> action ( function ( string $functionId , string $key , string $value , bool $secret , Response $response , Database $dbForProject , Database $dbForPlatform ) {
2022-08-01 15:13:47 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
2022-08-30 07:27:54 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
2022-08-26 13:38:39 +00:00
$variableId = ID :: unique ();
2022-08-01 15:13:47 +00:00
$variable = new Document ([
2022-08-26 13:38:39 +00:00
'$id' => $variableId ,
2022-08-24 15:07:18 +00:00
'$permissions' => [
Permission :: read ( Role :: any ()),
Permission :: update ( Role :: any ()),
Permission :: delete ( Role :: any ()),
],
2023-03-11 16:06:02 +00:00
'resourceInternalId' => $function -> getInternalId (),
'resourceId' => $function -> getId (),
'resourceType' => 'function' ,
2022-08-01 15:13:47 +00:00
'key' => $key ,
2022-08-26 13:38:39 +00:00
'value' => $value ,
2024-10-21 14:33:57 +00:00
'secret' => $secret ,
2023-03-11 16:06:02 +00:00
'search' => implode ( ' ' , [ $variableId , $function -> getId (), $key , 'function' ]),
2022-08-01 15:13:47 +00:00
]);
try {
$variable = $dbForProject -> createDocument ( 'variables' , $variable );
} catch ( DuplicateException $th ) {
2022-08-30 07:28:01 +00:00
throw new Exception ( Exception :: VARIABLE_ALREADY_EXISTS );
2022-08-01 15:13:47 +00:00
}
2023-06-22 10:59:41 +00:00
$dbForProject -> updateDocument ( 'functions' , $function -> getId (), $function -> setAttribute ( 'live' , false ));
2022-08-01 15:13:47 +00:00
2023-07-28 07:56:07 +00:00
// Inform scheduler to pull the latest changes
2025-02-03 09:32:01 +00:00
$schedule = $dbForPlatform -> getDocument ( 'schedules' , $function -> getAttribute ( 'scheduleId' ));
2023-06-11 10:29:04 +00:00
$schedule
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
-> setAttribute ( 'schedule' , $function -> getAttribute ( 'schedule' ))
-> setAttribute ( 'active' , ! empty ( $function -> getAttribute ( 'schedule' )) && ! empty ( $function -> getAttribute ( 'deployment' )));
2025-02-03 09:32:01 +00:00
Authorization :: skip ( fn () => $dbForPlatform -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
2023-06-11 10:29:04 +00:00
2022-09-07 11:11:10 +00:00
$response
-> setStatusCode ( Response :: STATUS_CODE_CREATED )
-> dynamic ( $variable , Response :: MODEL_VARIABLE );
2022-08-01 15:13:47 +00:00
});
2022-08-09 15:29:24 +00:00
App :: get ( '/v1/functions/:functionId/variables' )
2023-10-02 14:02:48 +00:00
-> desc ( 'List variables' )
2022-08-01 15:13:47 +00:00
-> groups ([ 'api' , 'functions' ])
-> label ( 'scope' , 'functions.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2025-02-03 11:38:27 +00:00
-> label (
'sdk' ,
new Method (
namespace : 'functions' ,
name : 'listVariables' ,
description : '/docs/references/functions/list-variables.md' ,
auth : [ AuthType :: KEY ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_VARIABLE_LIST ,
)
],
)
)
2022-09-19 10:05:42 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function unique ID.' , false )
2022-08-01 15:13:47 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2022-09-12 09:02:14 +00:00
-> action ( function ( string $functionId , Response $response , Database $dbForProject ) {
2022-08-01 15:13:47 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
2022-08-30 07:28:48 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
$response -> dynamic ( new Document ([
2023-09-05 08:21:36 +00:00
'variables' => $function -> getAttribute ( 'vars' , []),
'total' => \count ( $function -> getAttribute ( 'vars' , [])),
2022-08-01 15:13:47 +00:00
]), Response :: MODEL_VARIABLE_LIST );
});
2022-08-09 15:29:24 +00:00
App :: get ( '/v1/functions/:functionId/variables/:variableId' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Get variable' )
2022-08-01 15:13:47 +00:00
-> groups ([ 'api' , 'functions' ])
-> label ( 'scope' , 'functions.read' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2022-08-03 13:32:50 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'getVariable' )
2022-08-09 12:11:50 +00:00
-> label ( 'sdk.description' , '/docs/references/functions/get-variable.md' )
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_VARIABLE )
2022-09-19 10:05:42 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function unique ID.' , false )
-> param ( 'variableId' , '' , new UID (), 'Variable unique ID.' , false )
2022-08-01 15:13:47 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2022-08-02 10:05:58 +00:00
-> action ( function ( string $functionId , string $variableId , Response $response , Database $dbForProject ) {
2022-08-01 15:13:47 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
2022-08-30 07:28:37 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
2023-07-24 13:12:36 +00:00
$variable = $dbForProject -> getDocument ( 'variables' , $variableId );
2023-08-18 06:55:44 +00:00
if (
$variable === false ||
$variable -> isEmpty () ||
$variable -> getAttribute ( 'resourceInternalId' ) !== $function -> getInternalId () ||
$variable -> getAttribute ( 'resourceType' ) !== 'function'
) {
2023-07-24 13:12:36 +00:00
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
}
2022-08-01 15:13:47 +00:00
if ( $variable === false || $variable -> isEmpty ()) {
2022-08-30 07:29:09 +00:00
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
$response -> dynamic ( $variable , Response :: MODEL_VARIABLE );
});
2022-08-09 15:29:24 +00:00
App :: put ( '/v1/functions/:functionId/variables/:variableId' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Update variable' )
2022-08-01 15:13:47 +00:00
-> groups ([ 'api' , 'functions' ])
-> label ( 'scope' , 'functions.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'variable.update' )
2022-09-04 08:13:44 +00:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2022-08-03 13:32:50 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'updateVariable' )
2022-08-09 12:11:50 +00:00
-> label ( 'sdk.description' , '/docs/references/functions/update-variable.md' )
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_OK )
-> label ( 'sdk.response.type' , Response :: CONTENT_TYPE_JSON )
-> label ( 'sdk.response.model' , Response :: MODEL_VARIABLE )
2022-09-19 10:05:42 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function unique ID.' , false )
-> param ( 'variableId' , '' , new UID (), 'Variable unique ID.' , false )
2022-08-31 07:33:43 +00:00
-> param ( 'key' , null , new Text ( 255 ), 'Variable key. Max length: 255 chars.' , false )
2023-08-12 19:08:44 +00:00
-> param ( 'value' , null , new Text ( 8192 , 0 ), 'Variable value. Max length: 8192 chars.' , true )
2022-08-01 15:13:47 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2025-02-03 09:32:01 +00:00
-> inject ( 'dbForPlatform' )
-> action ( function ( string $functionId , string $variableId , string $key , ? string $value , Response $response , Database $dbForProject , Database $dbForPlatform ) {
2022-08-01 15:13:47 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
2022-08-30 07:29:20 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
2023-07-24 13:12:36 +00:00
$variable = $dbForProject -> getDocument ( 'variables' , $variableId );
if ( $variable === false || $variable -> isEmpty () || $variable -> getAttribute ( 'resourceInternalId' ) !== $function -> getInternalId () || $variable -> getAttribute ( 'resourceType' ) !== 'function' ) {
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
}
2022-08-01 15:13:47 +00:00
if ( $variable === false || $variable -> isEmpty ()) {
2022-08-30 07:29:30 +00:00
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
$variable
2022-08-31 07:33:43 +00:00
-> setAttribute ( 'key' , $key )
2022-08-01 15:13:47 +00:00
-> setAttribute ( 'value' , $value ? ? $variable -> getAttribute ( 'value' ))
2023-03-28 13:21:42 +00:00
-> setAttribute ( 'search' , implode ( ' ' , [ $variableId , $function -> getId (), $key , 'function' ]));
2022-08-01 15:13:47 +00:00
try {
$dbForProject -> updateDocument ( 'variables' , $variable -> getId (), $variable );
} catch ( DuplicateException $th ) {
2022-08-30 07:28:09 +00:00
throw new Exception ( Exception :: VARIABLE_ALREADY_EXISTS );
2022-08-01 15:13:47 +00:00
}
2023-06-22 10:59:41 +00:00
$dbForProject -> updateDocument ( 'functions' , $function -> getId (), $function -> setAttribute ( 'live' , false ));
2022-08-01 15:13:47 +00:00
2023-07-28 07:56:07 +00:00
// Inform scheduler to pull the latest changes
2025-02-03 09:32:01 +00:00
$schedule = $dbForPlatform -> getDocument ( 'schedules' , $function -> getAttribute ( 'scheduleId' ));
2023-06-11 10:29:04 +00:00
$schedule
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
-> setAttribute ( 'schedule' , $function -> getAttribute ( 'schedule' ))
-> setAttribute ( 'active' , ! empty ( $function -> getAttribute ( 'schedule' )) && ! empty ( $function -> getAttribute ( 'deployment' )));
2025-02-03 09:32:01 +00:00
Authorization :: skip ( fn () => $dbForPlatform -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
2023-06-11 10:29:04 +00:00
2022-08-01 15:13:47 +00:00
$response -> dynamic ( $variable , Response :: MODEL_VARIABLE );
});
2022-08-09 15:29:24 +00:00
App :: delete ( '/v1/functions/:functionId/variables/:variableId' )
2023-10-02 14:02:48 +00:00
-> desc ( 'Delete variable' )
2022-08-01 15:13:47 +00:00
-> groups ([ 'api' , 'functions' ])
-> label ( 'scope' , 'functions.write' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2022-09-05 08:00:08 +00:00
-> label ( 'audits.event' , 'variable.delete' )
2022-09-04 08:13:44 +00:00
-> label ( 'audits.resource' , 'function/{request.functionId}' )
2022-08-03 13:32:50 +00:00
-> label ( 'sdk.auth' , [ APP_AUTH_TYPE_KEY ])
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.namespace' , 'functions' )
-> label ( 'sdk.method' , 'deleteVariable' )
2022-08-09 12:11:50 +00:00
-> label ( 'sdk.description' , '/docs/references/functions/delete-variable.md' )
2022-08-01 15:13:47 +00:00
-> label ( 'sdk.response.code' , Response :: STATUS_CODE_NOCONTENT )
-> label ( 'sdk.response.model' , Response :: MODEL_NONE )
2022-09-19 10:05:42 +00:00
-> param ( 'functionId' , '' , new UID (), 'Function unique ID.' , false )
-> param ( 'variableId' , '' , new UID (), 'Variable unique ID.' , false )
2022-08-01 15:13:47 +00:00
-> inject ( 'response' )
-> inject ( 'dbForProject' )
2025-02-03 09:32:01 +00:00
-> inject ( 'dbForPlatform' )
-> action ( function ( string $functionId , string $variableId , Response $response , Database $dbForProject , Database $dbForPlatform ) {
2022-08-01 15:13:47 +00:00
$function = $dbForProject -> getDocument ( 'functions' , $functionId );
if ( $function -> isEmpty ()) {
2022-08-30 07:28:20 +00:00
throw new Exception ( Exception :: FUNCTION_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
2023-07-24 13:12:36 +00:00
$variable = $dbForProject -> getDocument ( 'variables' , $variableId );
if ( $variable === false || $variable -> isEmpty () || $variable -> getAttribute ( 'resourceInternalId' ) !== $function -> getInternalId () || $variable -> getAttribute ( 'resourceType' ) !== 'function' ) {
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
}
2022-08-01 15:13:47 +00:00
if ( $variable === false || $variable -> isEmpty ()) {
2022-08-30 07:28:27 +00:00
throw new Exception ( Exception :: VARIABLE_NOT_FOUND );
2022-08-01 15:13:47 +00:00
}
$dbForProject -> deleteDocument ( 'variables' , $variable -> getId ());
2023-06-11 10:29:04 +00:00
2023-06-22 10:59:41 +00:00
$dbForProject -> updateDocument ( 'functions' , $function -> getId (), $function -> setAttribute ( 'live' , false ));
2022-08-01 15:13:47 +00:00
2023-07-28 07:56:07 +00:00
// Inform scheduler to pull the latest changes
2025-02-03 09:32:01 +00:00
$schedule = $dbForPlatform -> getDocument ( 'schedules' , $function -> getAttribute ( 'scheduleId' ));
2023-06-11 10:29:04 +00:00
$schedule
-> setAttribute ( 'resourceUpdatedAt' , DateTime :: now ())
-> setAttribute ( 'schedule' , $function -> getAttribute ( 'schedule' ))
-> setAttribute ( 'active' , ! empty ( $function -> getAttribute ( 'schedule' )) && ! empty ( $function -> getAttribute ( 'deployment' )));
2025-02-03 09:32:01 +00:00
Authorization :: skip ( fn () => $dbForPlatform -> updateDocument ( 'schedules' , $schedule -> getId (), $schedule ));
2023-06-11 10:29:04 +00:00
2022-08-01 15:13:47 +00:00
$response -> noContent ();
});
2024-07-19 12:09:18 +00:00
App :: get ( '/v1/functions/templates' )
2024-08-13 12:59:37 +00:00
-> groups ([ 'api' ])
2024-07-29 20:55:18 +00:00
-> desc ( 'List function templates' )
2024-07-29 12:25:58 +00:00
-> label ( 'scope' , 'public' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2025-02-03 09:32:01 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'listTemplates' ,
description : '/docs/references/functions/list-templates.md' ,
auth : [ AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_TEMPLATE_FUNCTION_LIST ,
)
]
))
2024-07-29 13:29:16 +00:00
-> param ( 'runtimes' , [], new ArrayList ( new WhiteList ( array_keys ( Config :: getParam ( 'runtimes' )), true ), APP_LIMIT_ARRAY_PARAMS_SIZE ), 'List of runtimes allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' runtimes are allowed.' , true )
2024-07-30 10:41:04 +00:00
-> param ( 'useCases' , [], new ArrayList ( new WhiteList ([ 'dev-tools' , 'starter' , 'databases' , 'ai' , 'messaging' , 'utilities' ]), APP_LIMIT_ARRAY_PARAMS_SIZE ), 'List of use cases allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' use cases are allowed.' , true )
2024-07-30 09:43:38 +00:00
-> param ( 'limit' , 25 , new Range ( 1 , 5000 ), 'Limit the number of templates returned in the response. Default limit is 25, and maximum limit is 5000.' , true )
-> param ( 'offset' , 0 , new Range ( 0 , 5000 ), 'Offset the list of returned templates. Maximum offset is 5000.' , true )
2024-07-19 12:09:18 +00:00
-> inject ( 'response' )
2024-07-29 13:29:16 +00:00
-> action ( function ( array $runtimes , array $usecases , int $limit , int $offset , Response $response ) {
2024-07-29 12:25:58 +00:00
$templates = Config :: getParam ( 'function-templates' , []);
2024-07-29 13:29:16 +00:00
if ( ! empty ( $runtimes )) {
$templates = \array_filter ( $templates , function ( $template ) use ( $runtimes ) {
return \count ( \array_intersect ( $runtimes , \array_column ( $template [ 'runtimes' ], 'name' ))) > 0 ;
});
}
2024-07-29 12:25:58 +00:00
if ( ! empty ( $usecases )) {
$templates = \array_filter ( $templates , function ( $template ) use ( $usecases ) {
2024-07-30 10:41:04 +00:00
return \count ( \array_intersect ( $usecases , $template [ 'useCases' ])) > 0 ;
2024-07-29 12:25:58 +00:00
});
}
$responseTemplates = \array_slice ( $templates , $offset , $limit );
2024-07-19 12:09:18 +00:00
$response -> dynamic ( new Document ([
2024-07-29 12:25:58 +00:00
'templates' => $responseTemplates ,
'total' => \count ( $responseTemplates ),
2024-07-26 12:17:03 +00:00
]), Response :: MODEL_TEMPLATE_FUNCTION_LIST );
2024-07-19 12:09:18 +00:00
});
2024-07-30 11:46:45 +00:00
App :: get ( '/v1/functions/templates/:templateId' )
-> desc ( 'Get function template' )
-> label ( 'scope' , 'public' )
2024-10-29 15:07:12 +00:00
-> label ( 'resourceType' , RESOURCE_TYPE_FUNCTIONS )
2025-02-03 09:32:01 +00:00
-> label ( 'sdk' , new Method (
namespace : 'functions' ,
name : 'getTemplate' ,
description : '/docs/references/functions/get-template.md' ,
auth : [ AuthType :: ADMIN ],
responses : [
new SDKResponse (
code : Response :: STATUS_CODE_OK ,
model : Response :: MODEL_TEMPLATE_FUNCTION ,
)
]
))
2024-07-30 11:46:45 +00:00
-> param ( 'templateId' , '' , new Text ( 128 ), 'Template ID.' )
-> inject ( 'response' )
-> action ( function ( string $templateId , Response $response ) {
$templates = Config :: getParam ( 'function-templates' , []);
2024-11-04 08:49:20 +00:00
$filtered = \array_filter ( $templates , function ( $template ) use ( $templateId ) {
2024-07-30 11:46:45 +00:00
return $template [ 'id' ] === $templateId ;
2024-11-04 08:49:20 +00:00
});
$template = array_shift ( $filtered );
2024-07-30 11:46:45 +00:00
if ( empty ( $template )) {
throw new Exception ( Exception :: FUNCTION_TEMPLATE_NOT_FOUND );
}
$response -> dynamic ( new Document ( $template ), Response :: MODEL_TEMPLATE_FUNCTION );
});