Merge pull request #2739 from appwrite/feat-decouple-database

Decouple database dependencies from the executor
This commit is contained in:
Christy Jacob 2022-02-17 04:13:11 +04:00 committed by GitHub
commit 039d993730
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 2061 additions and 2305 deletions

1
.env
View file

@ -48,3 +48,4 @@ _APP_MAINTENANCE_RETENTION_AUDIT=1209600
_APP_USAGE_STATS=enabled
_APP_LOGGING_PROVIDER=
_APP_LOGGING_CONFIG=
OPENRUNTIMES_INACTIVE_THRESHOLD=60

View file

@ -1993,7 +1993,7 @@ $collections = [
'filters' => [],
],
[
'$id' => 'deploy',
'$id' => 'activate',
'type' => Database::VAR_BOOLEAN,
'format' => '',
'size' => 0,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -25,6 +25,7 @@ use Utopia\Validator\Range;
use Utopia\Validator\WhiteList;
use Utopia\Config\Config;
use Cron\CronExpression;
use Executor\Executor;
use Utopia\CLI\Console;
use Utopia\Validator\Boolean;
@ -331,7 +332,7 @@ App::put('/v1/functions/:functionId')
])));
if ($next && $schedule !== $original) {
ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [
ResqueScheduler::enqueueAt($next, Event::FUNCTIONS_QUEUE_NAME, Event::FUNCTIONS_CLASS_NAME, [
'projectId' => $project->getId(),
'webhooks' => $project->getAttribute('webhooks', []),
'functionId' => $function->getId(),
@ -344,7 +345,7 @@ App::put('/v1/functions/:functionId')
$response->dynamic($function, Response::MODEL_FUNCTION);
});
App::patch('/v1/functions/:functionId/deployment')
App::patch('/v1/functions/:functionId/deployments/:deploymentId')
->groups(['api', 'functions'])
->desc('Update Function Deployment')
->label('scope', 'functions.write')
@ -357,17 +358,17 @@ App::patch('/v1/functions/:functionId/deployment')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION)
->param('functionId', '', new UID(), 'Function ID.')
->param('deployment', '', new UID(), 'Deployment ID.')
->param('deploymentId', '', new UID(), 'Deployment ID.')
->inject('response')
->inject('dbForProject')
->inject('project')
->action(function ($functionId, $deployment, $response, $dbForProject, $project) {
->action(function ($functionId, $deploymentId, $response, $dbForProject, $project) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $project */
$function = $dbForProject->getDocument('functions', $functionId);
$deployment = $dbForProject->getDocument('deployments', $deployment);
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
$build = $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''));
if ($function->isEmpty()) {
@ -404,7 +405,6 @@ App::patch('/v1/functions/:functionId/deployment')
'trigger' => 'schedule',
]); // Async task rescheduale
}
$response->dynamic($function, Response::MODEL_FUNCTION);
});
@ -463,14 +463,14 @@ App::post('/v1/functions/:functionId/deployments')
->param('functionId', '', new UID(), 'Function ID.')
->param('entrypoint', '', new Text('1028'), 'Entrypoint File.')
->param('code', [], new File(), 'Gzip file with your code package. When used with the Appwrite CLI, pass the path to your code directory, and the CLI will automatically package your code. Use a path that is within the current directory.', false)
->param('deploy', false, new Boolean(true), 'Automatically deploy the function when it is finished building.', false)
->param('activate', false, new Boolean(true), 'Automatically activate the deployment when it is finished building.', false)
->inject('request')
->inject('response')
->inject('dbForProject')
->inject('usage')
->inject('user')
->inject('project')
->action(function ($functionId, $entrypoint, $file, $deploy, $request, $response, $dbForProject, $usage, $user, $project) {
->action(function ($functionId, $entrypoint, $file, $activate, $request, $response, $dbForProject, $usage, $user, $project) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
@ -519,16 +519,16 @@ App::post('/v1/functions/:functionId/deployments')
throw new Exception('Failed moving file', 500);
}
if ((bool) $deploy) {
if ((bool) $activate) {
// Remove deploy for all other deployments.
$deployments = $dbForProject->find('deployments', [
new Query('deploy', Query::TYPE_EQUAL, [true]),
new Query('activate', Query::TYPE_EQUAL, [true]),
new Query('resourceId', Query::TYPE_EQUAL, [$functionId]),
new Query('resourceType', Query::TYPE_EQUAL, ['functions'])
]);
foreach ($deployments as $deployment) {
$deployment->setAttribute('deploy', false);
$deployment->setAttribute('activate', false);
$dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
}
}
@ -545,7 +545,7 @@ App::post('/v1/functions/:functionId/deployments')
'path' => $path,
'size' => $size,
'search' => implode(' ', [$deploymentId, $entrypoint]),
'deploy' => ($deploy === 'true'),
'activate' => ((bool) $activate === true),
]));
// Enqueue a message to start the build
@ -616,8 +616,8 @@ App::get('/v1/functions/:functionId/deployments')
$sum = $dbForProject->count('deployments', $queries, APP_LIMIT_COUNT);
foreach ($results as $result) {
$build = $dbForProject->getDocument('builds', $result->getAttribute('buildId'));
$result->setAttribute('status', $build->getAttribute('status', 'pending'));
$build = $dbForProject->getDocument('builds', $result->getAttribute('buildId', ''));
$result->setAttribute('status', $build->getAttribute('status', 'processing'));
$result->setAttribute('buildStderr', $build->getAttribute('stderr', ''));
$result->setAttribute('buildStdout', $build->getAttribute('stdout', ''));
}
@ -758,7 +758,15 @@ App::post('/v1/functions/:functionId/executions')
throw new Exception('Function not found', 404);
}
$deployment = Authorization::skip(fn() => $dbForProject->getDocument('deployments', $function->getAttribute('deployment')));
$runtimes = Config::getParam('runtimes', []);
$runtime = (isset($runtimes[$function->getAttribute('runtime', '')])) ? $runtimes[$function->getAttribute('runtime', '')] : null;
if (\is_null($runtime)) {
throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported', 400);
}
$deployment = Authorization::skip(fn() => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', '')));
if ($deployment->getAttribute('resourceId') !== $function->getId()) {
throw new Exception('Deployment not found. Deploy deployment before trying to execute a function', 404);
@ -768,6 +776,16 @@ App::post('/v1/functions/:functionId/executions')
throw new Exception('Deployment not found. Deploy deployment before trying to execute a function', 404);
}
/** Check if build has completed */
$build = Authorization::skip(fn() => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
if ($build->isEmpty()) {
throw new Exception('Build not found', 404);
}
if ($build->getAttribute('status') !== 'ready') {
throw new Exception('Build not ready', 400);
}
$validator = new Authorization('execute');
if (!$validator->isValid($function->getAttribute('execute'))) { // Check if user has write access to execute function
@ -779,7 +797,7 @@ App::post('/v1/functions/:functionId/executions')
$execution = Authorization::skip(fn() => $dbForProject->createDocument('executions', new Document([
'$id' => $executionId,
'$read' => (!$user->isEmpty()) ? ['user:' . $user->getId()] : [],
'$write' => ['role:all'],
'$write' => [],
'dateCreated' => time(),
'functionId' => $function->getId(),
'deploymentId' => $deployment->getId(),
@ -814,56 +832,71 @@ App::post('/v1/functions/:functionId/executions')
}
if ($async) {
Resque::enqueue('v1-functions', 'FunctionsV1', [
Resque::enqueue(Event::FUNCTIONS_QUEUE_NAME, Event::FUNCTIONS_CLASS_NAME, [
'projectId' => $project->getId(),
'webhooks' => $project->getAttribute('webhooks', []),
'functionId' => $function->getId(),
'webhooks' => $project->getAttribute('webhooks', []),
'executionId' => $execution->getId(),
'trigger' => 'http',
'data' => $data,
'userId' => $user->getId(),
'jwt' => $jwt
'jwt' => $jwt,
]);
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($execution, Response::MODEL_EXECUTION);
return $response;
return $response->dynamic($execution, Response::MODEL_EXECUTION);
}
// Directly execute function.
$ch = \curl_init();
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/functions/{$function->getId()}/executions");
\curl_setopt($ch, CURLOPT_POST, true);
\curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'trigger' => 'http',
'projectId' => $project->getId(),
'executionId' => $execution->getId(),
'data' => $data,
'webhooks' => $project->getAttribute('webhooks', []),
'userId' => $user->getId(),
'jwt' => $jwt,
]));
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
\curl_setopt($ch, CURLOPT_TIMEOUT, App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900) + 200); // + 200 for safety margin
\curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'x-appwrite-project: '.$project->getId(),
'x-appwrite-executor-key: '. App::getEnv('_APP_EXECUTOR_SECRET', '')
/** Collect environment variables */
$vars = \array_merge($function->getAttribute('vars', []), [
'APPWRITE_FUNCTION_ID' => $function->getId(),
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(),
'APPWRITE_FUNCTION_TRIGGER' => 'http',
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'],
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'],
'APPWRITE_FUNCTION_DATA' => $data,
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
'APPWRITE_FUNCTION_USER_ID' => $user->getId(),
'APPWRITE_FUNCTION_JWT' => $jwt,
]);
$responseExecute = \curl_exec($ch);
$error = \curl_error($ch);
if (!empty($error)) {
Console::error('Curl error: '.$error);
/** Execute function */
$executor = new Executor();
$executionResponse = [];
try {
$executionResponse = $executor->createExecution(
projectId: $project->getId(),
functionId: $function->getId(),
deploymentId: $deployment->getId(),
path: $build->getAttribute('outputPath', ''),
vars: $vars,
data: $data,
entrypoint: $deployment->getAttribute('entrypoint', ''),
runtime: $function->getAttribute('runtime', ''),
timeout: $function->getAttribute('timeout', 0),
baseImage: $runtime['image']
);
/** Update execution status */
$execution->setAttribute('status', $executionResponse['status']);
$execution->setAttribute('statusCode', $executionResponse['statusCode']);
$execution->setAttribute('stdout', $executionResponse['stdout']);
$execution->setAttribute('stderr', $executionResponse['stderr']);
$execution->setAttribute('time', $executionResponse['time']);
} catch (\Throwable $th) {
$execution->setAttribute('status', 'failed');
$execution->setAttribute('statusCode', $th->getCode());
$execution->setAttribute('stderr', $th->getMessage());
Console::error($th->getMessage());
}
\curl_close($ch);
Authorization::skip(fn() => $dbForProject->updateDocument('executions', $executionId, $execution));
$executionResponse['response'] = ($executionResponse['status'] !== 'completed') ? $executionResponse['stderr'] : $executionResponse['stdout'];
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic(new Document(json_decode($responseExecute, true)), Response::MODEL_SYNC_EXECUTION);
->dynamic(new Document($executionResponse), Response::MODEL_SYNC_EXECUTION);
});
App::get('/v1/functions/:functionId/executions')
@ -958,7 +991,7 @@ App::get('/v1/functions/:functionId/executions/:executionId')
$response->dynamic($execution, Response::MODEL_EXECUTION);
});
App::post('/v1/builds/:buildId')
App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
->groups(['api', 'functions'])
->desc('Retry Build')
->label('scope', 'functions.write')
@ -969,15 +1002,28 @@ App::post('/v1/builds/:buildId')
->label('sdk.description', '/docs/references/functions/retry-build.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('functionId', '', new UID(), 'Function ID.')
->param('deploymentId', '', new UID(), 'Deployment ID.')
->param('buildId', '', new UID(), 'Build unique ID.')
->inject('response')
->inject('dbForProject')
->inject('project')
->action(function ($buildId, $response, $dbForProject, $project) {
->action(function ($functionId, $deploymentId, $buildId, $response, $dbForProject, $project) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $project */
$function = $dbForProject->getDocument('functions', $functionId);
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
if ($function->isEmpty()) {
throw new Exception('Function not found', 404);
}
if ($deployment->isEmpty()) {
throw new Exception('Deployment not found', 404);
}
$build = Authorization::skip(fn() => $dbForProject->getDocument('builds', $buildId));
if ($build->isEmpty()) {
@ -991,7 +1037,8 @@ App::post('/v1/builds/:buildId')
// Enqueue a message to start the build
Resque::enqueue(Event::BUILDS_QUEUE_NAME, Event::BUILDS_CLASS_NAME, [
'projectId' => $project->getId(),
'buildId' => $buildId,
'functionId' => $function->getId(),
'deploymentId' => $deploymentId,
'type' => BUILD_TYPE_RETRY
]);

File diff suppressed because it is too large Load diff

View file

@ -117,7 +117,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
data-failure="alert"
data-failure-param-alert-text="Failed to update deployment"
data-failure-param-alert-classname="error">
<input type="hidden" name="deployment" data-ls-bind="{{deployment.$id}}">
<input type="hidden" name="deploymentId" data-ls-bind="{{deployment.$id}}">
<button>Activate</button>
</form>
<form data-ls-if="({{deployment.$id}} !== {{project-function.deployment}} && {{deployment.status}} == 'failed')" name="functions.retryBuild" class="pull-end"
@ -135,6 +135,8 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
data-failure-param-alert-text="Failed to retry build"
data-failure-param-alert-classname="error">
<input type="hidden" name="buildId" data-ls-bind="{{deployment.buildId}}">
<input type="hidden" name="functionId" data-ls-bind="{{router.params.id}}">
<input type="hidden" name="deploymentId" data-ls-bind="{{deployment.$id}}">
<button>Retry Build</button>
</form>
@ -685,7 +687,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
<input type="file" name="code" id="deployment-code" size="1" required accept="application/x-gzip,.gz">
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">(Max file size allowed: <?php echo $fileLimitHuman; ?>)</div>
<label for="deployment-deploy" class="margin-bottom-large">Auto Deploy Deployment after build <input type="checkbox" class="margin-start-small" id="deployment-deploy" name="deploy" /></label>
<label for="deployment-activate" class="margin-bottom-large">Auto Activate Deployment after build <input type="checkbox" class="margin-start-small" id="deployment-activate" name="activate" /></label>
<footer>
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>

View file

@ -2,6 +2,7 @@
use Appwrite\Resque\Worker;
use Cron\CronExpression;
use Executor\Executor;
use Utopia\Database\Validator\Authorization;
use Utopia\App;
use Utopia\CLI\Console;
@ -20,71 +21,40 @@ Console::success(APP_NAME.' build worker v1 has started');
// TODO: Executor should return appropriate response codes.
class BuildsV1 extends Worker
{
/**
* @var Executor
*/
private $executor = null;
public function getName(): string
{
return "builds";
}
public function init(): void {}
public function init(): void {
$this->executor = new Executor();
}
public function run(): void
{
$type = $this->args['type'] ?? '';
$projectId = $this->args['projectId'] ?? '';
$functionId = $this->args['functionId'] ?? '';
$deploymentId = $this->args['deploymentId'] ?? '';
switch ($type) {
case BUILD_TYPE_DEPLOYMENT:
$functionId = $this->args['functionId'] ?? '';
$deploymentId = $this->args['deploymentId'] ?? '';
case BUILD_TYPE_RETRY:
Console::info("Creating build for deployment: $deploymentId");
$this->buildDeployment($projectId, $functionId, $deploymentId);
break;
case BUILD_TYPE_RETRY:
$buildId = $this->args['buildId'] ?? '';
$functionId = $this->args['functionId'] ?? '';
$deploymentId = $this->args['deploymentId'] ?? '';
Console::info("Retrying build for id: $buildId");
$this->createBuild($projectId, $functionId, $deploymentId, $buildId);
break;
default:
throw new \Exception('Invalid build type');
break;
}
}
protected function createBuild(string $projectId, string $functionId, string $deploymentId, string $buildId)
{
// TODO: What is a reasonable time to wait for a build to complete?
$ch = \curl_init();
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/functions/$functionId/deployments/$deploymentId/builds/$buildId");
\curl_setopt($ch, CURLOPT_POST, true);
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
\curl_setopt($ch, CURLOPT_TIMEOUT, 900);
\curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'x-appwrite-project: '.$projectId,
'x-appwrite-executor-key: '. App::getEnv('_APP_EXECUTOR_SECRET', '')
]);
$response = \curl_exec($ch);
$responseStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = \curl_error($ch);
if (!empty($error)) {
throw new \Exception($error);
}
\curl_close($ch);
if ($responseStatus >= 400) {
throw new \Exception("Build failed with status code: $responseStatus");
}
}
protected function buildDeployment(string $projectId, string $functionId, string $deploymentId)
{
$dbForProject = $this->getProjectDB($projectId);
@ -94,7 +64,6 @@ class BuildsV1 extends Worker
throw new Exception('Function not found', 404);
}
// Get deployment document
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
if ($deployment->isEmpty()) {
throw new Exception('Deployment not found', 404);
@ -108,46 +77,84 @@ class BuildsV1 extends Worker
}
$buildId = $deployment->getAttribute('buildId', '');
// If build ID is empty, create a new build
$build = null;
$startTime = \time();
if (empty($buildId)) {
try {
$buildId = $dbForProject->getId();
$dbForProject->createDocument('builds', new Document([
'$id' => $buildId,
'$read' => [],
'$write' => [],
'startTime' => time(),
'deploymentId' => $deploymentId,
'status' => 'processing',
'outputPath' => '',
'runtime' => $function->getAttribute('runtime'),
'source' => $deployment->getAttribute('path'),
'sourceType' => Storage::DEVICE_LOCAL,
'stdout' => '',
'stderr' => '',
'endTime' => 0,
'duration' => 0
]));
} catch (\Throwable $th) {
$deployment->setAttribute('buildId', '');
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
Console::error($th->getMessage());
throw $th;
}
}
// Build the Code
try {
$buildId = $dbForProject->getId();
$build = $dbForProject->createDocument('builds', new Document([
'$id' => $buildId,
'$read' => [],
'$write' => [],
'startTime' => $startTime,
'deploymentId' => $deploymentId,
'status' => 'processing',
'outputPath' => '',
'runtime' => $function->getAttribute('runtime'),
'source' => $deployment->getAttribute('path'),
'sourceType' => Storage::DEVICE_LOCAL,
'stdout' => '',
'stderr' => '',
'endTime' => 0,
'duration' => 0
]));
$deployment->setAttribute('buildId', $buildId);
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
$this->createBuild($projectId, $functionId, $deploymentId, $buildId);
} catch (\Throwable $th) {
Console::error($th->getMessage());
throw $th;
} else {
$build = $dbForProject->getDocument('builds', $buildId);
}
Console::success("Build id: $buildId started");
/** Request the executor to build the code... */
$build->setAttribute('status', 'building');
$build = $dbForProject->updateDocument('builds', $buildId, $build);
$source = $deployment->getAttribute('path');
$vars = $function->getAttribute('vars', []);
$baseImage = $runtime['image'];
try {
$response = $this->executor->createRuntime(
projectId: $projectId,
functionId: $functionId,
deploymentId: $deploymentId,
source: $source,
vars: $vars,
runtime: $key,
baseImage: $baseImage
);
/** Update the build document */
$build->setAttribute('endTime', $response['endTime']);
$build->setAttribute('duration', $response['duration']);
$build->setAttribute('status', $response['status']);
$build->setAttribute('outputPath', $response['outputPath']);
$build->setAttribute('stderr', $response['stderr']);
$build->setAttribute('stdout', $response['stdout']);
Console::success("Build id: $buildId created");
/** Set auto deploy */
if ($deployment->getAttribute('activate') === true) {
$function->setAttribute('deployment', $deployment->getId());
$function = $dbForProject->updateDocument('functions', $functionId, $function);
}
/** Update function schedule */
$schedule = $function->getAttribute('schedule', '');
$cron = (empty($function->getAttribute('deployment')) && !empty($schedule)) ? new CronExpression($schedule) : null;
$next = (empty($function->getAttribute('deployment')) && !empty($schedule)) ? $cron->getNextRunDate()->format('U') : 0;
$function->setAttribute('scheduleNext', (int)$next);
$function = $dbForProject->updateDocument('functions', $functionId, $function);
} catch (\Throwable $th) {
$endtime = \time();
$build->setAttribute('endTime', $endtime);
$build->setAttribute('duration', $endtime - $startTime);
$build->setAttribute('status', 'failed');
$build->setAttribute('stderr', $th->getMessage());
Console::error($th->getMessage());
} finally {
$build = $dbForProject->updateDocument('builds', $buildId, $build);
}
}
public function shutdown(): void {}

View file

@ -5,6 +5,7 @@ use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Appwrite\Resque\Worker;
use Executor\Executor;
use Utopia\Storage\Device\Local;
use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\TimeLimit;
@ -313,44 +314,16 @@ class DeletesV1 extends Worker
protected function deleteFunction(Document $document, string $projectId): void
{
$dbForProject = $this->getProjectDB($projectId);
/**
* Request executor to delete all deployment containers
*/
try {
$ch = \curl_init();
\curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/functions/{$document->getId()}");
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'x-appwrite-project: '. $projectId,
'x-appwrite-executor-key: '. App::getEnv('_APP_EXECUTOR_SECRET', '')
]);
$executorResponse = \curl_exec($ch);
$error = \curl_error($ch);
if (!empty($error)) {
throw new Exception($error, 500);
}
$statusCode = \curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($statusCode >= 400) {
throw new Exception('Executor error: ' . $executorResponse, $statusCode);
}
\curl_close($ch);
} catch (Throwable $th) {
Console::error($th->getMessage());
}
$functionId = $document->getId();
/**
* Delete Deployments
*/
Console::info("Deleting deployments for function " . $functionId);
$storageFunctions = new Local(APP_STORAGE_FUNCTIONS . '/app-' . $projectId);
$deploymentIds = [];
$this->deleteByGroup('deployments', [
new Query('resourceId', Query::TYPE_EQUAL, [$document->getId()])
new Query('resourceId', Query::TYPE_EQUAL, [$functionId])
], $dbForProject, function (Document $document) use ($storageFunctions, &$deploymentIds) {
$deploymentIds[] = $document->getId();
if ($storageFunctions->delete($document->getAttribute('path', ''), true)) {
@ -363,11 +336,12 @@ class DeletesV1 extends Worker
/**
* Delete builds
*/
if (!empty($deploymentIds)) {
$storageBuilds = new Local(APP_STORAGE_BUILDS . '/app-' . $projectId);
Console::info("Deleting builds for function " . $functionId);
$storageBuilds = new Local(APP_STORAGE_BUILDS . '/app-' . $projectId);
foreach ($deploymentIds as $deploymentId) {
$this->deleteByGroup('builds', [
new Query('deploymentId', Query::TYPE_EQUAL, $deploymentIds)
], $dbForProject, function (Document $document) use ($storageBuilds) {
new Query('deploymentId', Query::TYPE_EQUAL, [$deploymentId])
], $dbForProject, function (Document $document) use ($storageBuilds, $deploymentId) {
if ($storageBuilds->delete($document->getAttribute('outputPath', ''), true)) {
Console::success('Deleted build files: ' . $document->getAttribute('outputPath', ''));
} else {
@ -376,11 +350,27 @@ class DeletesV1 extends Worker
});
}
// Delete Executions
/**
* Delete Executions
*/
Console::info("Deleting executions for function " . $functionId);
$this->deleteByGroup('executions', [
new Query('functionId', Query::TYPE_EQUAL, [$document->getId()])
new Query('functionId', Query::TYPE_EQUAL, [$functionId])
], $dbForProject);
/**
* Request executor to delete all deployment containers
*/
Console::info("Requesting executor to delete all deployment containers for function " . $functionId);
$executor = new Executor();
foreach ($deploymentIds as $deploymentId) {
try {
$executor->deleteRuntime($projectId, $functionId, $deploymentId);
} catch (Throwable $th) {
Console::error($th->getMessage());
}
}
}
/**
@ -390,41 +380,13 @@ class DeletesV1 extends Worker
protected function deleteDeployment(Document $document, string $projectId): void
{
$dbForProject = $this->getProjectDB($projectId);
/**
* Request executor to delete the deployment containers
*/
try {
$ch = \curl_init();
\curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
// TODO: Implement coroutines.
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/deployments/{$document->getId()}");
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'x-appwrite-project: '. $projectId,
'x-appwrite-executor-key: '. App::getEnv('_APP_EXECUTOR_SECRET', '')
]);
$executorResponse = \curl_exec($ch);
$error = \curl_error($ch);
if (!empty($error)) {
throw new Exception($error, 500);
}
$statusCode = \curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($statusCode >= 400) {
throw new Exception('Executor error: ' . $executorResponse, $statusCode);
}
\curl_close($ch);
} catch (Throwable $th) {
Console::error($th->getMessage());
}
$deploymentId = $document->getId();
$functionId = $document->getAttribute('resourceId');
/**
* Delete deployment files
*/
Console::info("Deleting deployment files for deployment " . $deploymentId);
$storageFunctions = new Local(APP_STORAGE_FUNCTIONS . '/app-' . $projectId);
if ($storageFunctions->delete($document->getAttribute('path', ''), true)) {
Console::success('Deleted deployment files: ' . $document->getAttribute('path', ''));
@ -435,9 +397,10 @@ class DeletesV1 extends Worker
/**
* Delete builds
*/
Console::info("Deleting builds for deployment " . $deploymentId);
$storageBuilds = new Local(APP_STORAGE_BUILDS . '/app-' . $projectId);
$this->deleteByGroup('builds', [
new Query('deploymentId', Query::TYPE_EQUAL, [$document->getId()])
new Query('deploymentId', Query::TYPE_EQUAL, [$deploymentId])
], $dbForProject, function (Document $document) use ($storageBuilds) {
if ($storageBuilds->delete($document->getAttribute('outputPath', ''), true)) {
Console::success('Deleted build files: ' . $document->getAttribute('outputPath', ''));
@ -446,6 +409,16 @@ class DeletesV1 extends Worker
}
});
/**
* Request executor to delete the deployment container
*/
Console::info("Requesting executor to delete deployment container for deployment " . $deploymentId);
try {
$executor = new Executor();
$executor->deleteRuntime($projectId, $functionId, $deploymentId);
} catch (Throwable $th) {
Console::error($th->getMessage());
}
}

View file

@ -1,7 +1,12 @@
<?php
use Appwrite\Event\Event;
use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Resque\Worker;
use Appwrite\Stats\Stats;
use Appwrite\Utopia\Response\Model\Execution;
use Cron\CronExpression;
use Executor\Executor;
use Swoole\Runtime;
use Utopia\App;
use Utopia\CLI\Console;
@ -9,30 +14,19 @@ use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Orchestration\Adapter\DockerAPI;
use Utopia\Orchestration\Orchestration;
require_once __DIR__.'/../init.php';
Runtime::enableCoroutine(0);
Console::title('Functions V1 Worker');
Console::success(APP_NAME . ' functions worker v1 has started');
$runtimes = Config::getParam('runtimes');
$dockerUser = App::getEnv('DOCKERHUB_PULL_USERNAME', null);
$dockerPass = App::getEnv('DOCKERHUB_PULL_PASSWORD', null);
$dockerEmail = App::getEnv('DOCKERHUB_PULL_EMAIL', null);
$orchestration = new Orchestration(new DockerAPI($dockerUser, $dockerPass, $dockerEmail));
$warmupEnd = \microtime(true);
$warmupTime = $warmupEnd - $warmupStart;
Console::success('Finished warmup in ' . $warmupTime . ' seconds');
class FunctionsV1 extends Worker
{
/**
* @var Executor
*/
private $executor = null;
public array $args = [];
public array $allowed = [];
@ -43,6 +37,7 @@ class FunctionsV1 extends Worker
public function init(): void
{
$this->executor = new Executor();
}
public function run(): void
@ -70,13 +65,7 @@ class FunctionsV1 extends Worker
/** @var Document[] $functions */
while ($sum >= $limit) {
Authorization::disable();
$functions = $database->find('functions', [], $limit, $offset, ['name'], [Database::ORDER_ASC]);
Authorization::reset();
$functions = Authorization::skip(fn() => $database->find('functions', [], $limit, $offset, ['name'], [Database::ORDER_ASC]));
$sum = \count($functions);
$offset = $offset + $limit;
@ -84,31 +73,31 @@ class FunctionsV1 extends Worker
foreach ($functions as $function) {
$events = $function->getAttribute('events', []);
$deployment = $function->getAttribute('deployment', []);
Console::success('Itterating function: ' . $function->getAttribute('name'));
if (!\in_array($event, $events) || empty($deployment)) {
if (!\in_array($event, $events)) {
continue;
}
Console::success('Triggered function: ' . $event);
Console::success('Iterating function: ' . $function->getAttribute('name'));
$this->execute(
trigger: 'event',
projectId: $projectId,
executionId: '',
database: $database,
function: $function,
dbForProject: $database,
executionId: $executionId,
webhooks: $webhooks,
trigger: $trigger,
event: $event,
eventData: $eventData,
data: $data,
webhooks: $webhooks,
userId: $userId,
jwt: $jwt
);
Console::success('Triggered function: ' . $event);
}
}
break;
case 'schedule':
@ -126,9 +115,7 @@ class FunctionsV1 extends Worker
*/
// Reschedule
Authorization::disable();
$function = $database->getDocument('functions', $functionId);
Authorization::reset();
$function = Authorization::skip(fn() => $database->getDocument('functions', $functionId));
if (empty($function->getId())) {
throw new Exception('Function not found ('.$functionId.')');
@ -145,19 +132,18 @@ class FunctionsV1 extends Worker
->setAttribute('scheduleNext', $next)
->setAttribute('schedulePrevious', \time());
Authorization::disable();
$function = Authorization::skip(function() use ($database, $function, $next, $functionId) {
$function = $database->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
'scheduleNext' => (int)$next,
])));
if ($function === false) {
throw new Exception('Function update failed (' . $functionId . ')');
}
return $function;
});
$function = $database->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
'scheduleNext' => (int)$next,
])));
if ($function === false) {
throw new Exception('Function update failed (' . $functionId . ')');
}
Authorization::reset();
ResqueScheduler::enqueueAt($next, 'v1-functions', 'FunctionsV1', [
ResqueScheduler::enqueueAt($next, Event::FUNCTIONS_QUEUE_NAME, Event::FUNCTIONS_CLASS_NAME, [
'projectId' => $projectId,
'webhooks' => $webhooks,
'functionId' => $function->getId(),
@ -168,92 +154,205 @@ class FunctionsV1 extends Worker
]); // Async task reschedule
$this->execute(
trigger: $trigger,
projectId: $projectId,
executionId: $executionId,
database: $database,
function: $function,
data: $data,
dbForProject: $database,
executionId: $executionId,
webhooks: $webhooks,
trigger: $trigger,
event: $event,
eventData: $eventData,
data: $data,
userId: $userId,
jwt: $jwt
);
break;
case 'http':
Authorization::disable();
$function = $database->getDocument('functions', $functionId);
Authorization::reset();
$function = Authorization::skip(fn() => $database->getDocument('functions', $functionId));
if (empty($function->getId())) {
throw new Exception('Function not found ('.$functionId.')');
}
$this->execute(
trigger: $trigger,
projectId: $projectId,
executionId: $executionId,
database: $database,
function: $function,
data: $data,
dbForProject: $database,
executionId: $executionId,
webhooks: $webhooks,
trigger: $trigger,
event: $event,
eventData: $eventData,
data: $data,
userId: $userId,
jwt: $jwt
);
break;
}
}
/**
* Execute function deployment
*
* @param string $trigger
* @param string $projectId
* @param string $executionId
* @param Database $database
* @param Document $function
* @param string $event
* @param string $eventData
* @param string $data
* @param array $webhooks
* @param string $userId
* @param string $jwt
*
* @return void
*/
public function execute(string $trigger, string $projectId, string $executionId, Database $database, Document $function, string $event = '', string $eventData = '', string $data = '', array $webhooks = [], string $userId = '', string $jwt = ''): void
{
$ch = \curl_init();
\curl_setopt($ch, CURLOPT_URL, "http://appwrite-executor/v1/functions/{$function->getId()}/executions");
\curl_setopt($ch, CURLOPT_POST, true);
\curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'trigger' => $trigger,
'projectId' => $projectId,
'executionId' => $executionId,
'event' => $event,
'eventData' => $eventData,
'data' => $data,
'webhooks' => $webhooks,
'userId' => $userId,
'jwt' => $jwt,
]));
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
\curl_setopt($ch, CURLOPT_TIMEOUT, App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900) + 200); // + 200 for safety margin
\curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'x-appwrite-project: '.$projectId,
'x-appwrite-executor-key: '. App::getEnv('_APP_EXECUTOR_SECRET', '')
]);
private function execute(
string $projectId,
Document $function,
Database $dbForProject,
string $executionId,
array $webhooks,
string $trigger,
string $event,
string $eventData,
string $data,
string $userId,
string $jwt
) {
\curl_exec($ch);
$functionId = $function->getId();
$deploymentId = $function->getAttribute('deployment', '');
$error = \curl_error($ch);
if (!empty($error)) {
Console::error('Curl error: '.$error);
/** Check if deployment exists */
$deployment = Authorization::skip(fn() => $dbForProject->getDocument('deployments', $deploymentId));
if ($deployment->getAttribute('resourceId') !== $functionId) {
throw new Exception('Deployment not found. Create deployment before trying to execute a function', 404);
}
\curl_close($ch);
if ($deployment->isEmpty()) {
throw new Exception('Deployment not found. Create deployment before trying to execute a function', 404);
}
/** Check if build has exists */
$build = Authorization::skip(fn() => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
if ($build->isEmpty()) {
throw new Exception('Build not found', 404);
}
if ($build->getAttribute('status') !== 'ready') {
throw new Exception('Build not ready', 400);
}
/** Check if runtime is supported */
$runtimes = Config::getParam('runtimes', []);
$runtime = (isset($runtimes[$function->getAttribute('runtime', '')])) ? $runtimes[$function->getAttribute('runtime', '')] : null;
if (\is_null($runtime)) {
throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported', 400);
}
/** Create execution or update execution status */
$execution = Authorization::skip(function() use ($dbForProject, &$executionId, $functionId, $deploymentId, $trigger, $userId) {
$execution = $dbForProject->getDocument('executions', $executionId);
if ($execution->isEmpty()) {
$executionId = $dbForProject->getId();
$execution = $dbForProject->createDocument('executions', new Document([
'$id' => $executionId,
'$read' => $userId ? ['user:' . $userId] : [],
'$write' => [],
'dateCreated' => time(),
'functionId' => $functionId,
'deploymentId' => $deploymentId,
'trigger' => $trigger,
'status' => 'waiting',
'statusCode' => 0,
'stdout' => '',
'stderr' => '',
'time' => 0.0,
'search' => implode(' ', [$functionId, $executionId]),
]));
if ($execution->isEmpty()) {
throw new Exception('Failed to create or read execution');
}
}
$execution->setAttribute('status', 'processing');
$execution = $dbForProject->updateDocument('executions', $executionId, $execution);
return $execution;
});
/** Collect environment variables */
$vars = [
'APPWRITE_FUNCTION_ID' => $functionId,
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deploymentId,
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'],
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'],
'APPWRITE_FUNCTION_TRIGGER' => $trigger,
'APPWRITE_FUNCTION_EVENT' => $event,
'APPWRITE_FUNCTION_EVENT_DATA' => $eventData,
'APPWRITE_FUNCTION_DATA' => $data,
'APPWRITE_FUNCTION_PROJECT_ID' => $projectId,
'APPWRITE_FUNCTION_USER_ID' => $userId,
'APPWRITE_FUNCTION_JWT' => $jwt,
];
$vars = \array_merge($function->getAttribute('vars', []), $vars);
/** Execute function */
try {
$executionResponse = $this->executor->createExecution(
projectId: $projectId,
functionId: $functionId,
deploymentId: $deploymentId,
path: $build->getAttribute('outputPath', ''),
vars: $vars,
entrypoint: $deployment->getAttribute('entrypoint', ''),
data: $vars['APPWRITE_FUNCTION_DATA'],
runtime: $function->getAttribute('runtime', ''),
timeout: $function->getAttribute('timeout', 0),
baseImage: $runtime['image']
);
/** Update execution status */
$execution->setAttribute('status', $executionResponse['status']);
$execution->setAttribute('statusCode', $executionResponse['statusCode']);
$execution->setAttribute('stdout', $executionResponse['stdout']);
$execution->setAttribute('stderr', $executionResponse['stderr']);
$execution->setAttribute('time', $executionResponse['time']);
} catch (\Throwable $th) {
$execution->setAttribute('status', 'failed');
$execution->setAttribute('statusCode', $th->getCode());
$execution->setAttribute('stderr', $th->getMessage());
Console::error($th->getMessage());
}
$execution = Authorization::skip(fn() => $dbForProject->updateDocument('executions', $executionId, $execution));
/** Trigger Webhook */
$executionModel = new Execution();
$executionUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME);
$executionUpdate
->setParam('projectId', $projectId)
->setParam('userId', $userId)
->setParam('webhooks', $webhooks)
->setParam('event', 'functions.executions.update')
->setParam('eventData', $execution->getArrayCopy(array_keys($executionModel->getRules())));
$executionUpdate->trigger();
/** Trigger realtime event */
$target = Realtime::fromPayload('functions.executions.update', $execution);
Realtime::send(
projectId: $projectId,
payload: $execution->getArrayCopy(),
event: 'functions.executions.update',
channels: $target['channels'],
roles: $target['roles']
);
/** Update usage stats */
global $register;
if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') {
$statsd = $register->get('statsd');
$usage = new Stats($statsd);
$usage
->setParam('projectId', $projectId)
->setParam('functionId', $function->getId())
->setParam('functionExecution', 1)
->setParam('functionStatus', $execution->getAttribute('status', ''))
->setParam('functionExecutionTime', $execution->getAttribute('time') * 1000) // ms
->setParam('networkRequestSize', 0)
->setParam('networkResponseSize', 0)
->submit();
$usage->submit();
}
}
public function shutdown(): void

View file

@ -11,7 +11,8 @@
],
"autoload": {
"psr-4": {
"Appwrite\\": "src/Appwrite"
"Appwrite\\": "src/Appwrite",
"Executor\\": "src/Executor"
}
},
"autoload-dev": {

40
composer.lock generated
View file

@ -119,7 +119,7 @@
"source": {
"type": "git",
"url": "https://github.com/appwrite/runtimes.git",
"reference": "96a536e0ad7e3788cd2493e39cdf94b44206411d"
"reference": "991a8674c75f78644b557c9723869c370b3290b5"
},
"require": {
"php": ">=8.0",
@ -160,7 +160,7 @@
"php",
"runtimes"
],
"time": "2022-01-19T09:26:05+00:00"
"time": "2022-02-01T10:58:43+00:00"
},
{
"name": "chillerlan/php-qrcode",
@ -2135,16 +2135,16 @@
},
{
"name": "utopia-php/database",
"version": "0.14.0",
"version": "0.14.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "2f2527bb080cf578fba327ea2ec637064561d403"
"reference": "ecc143f2cfe16b23675407035c6b5375ba263285"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/2f2527bb080cf578fba327ea2ec637064561d403",
"reference": "2f2527bb080cf578fba327ea2ec637064561d403",
"url": "https://api.github.com/repos/utopia-php/database/zipball/ecc143f2cfe16b23675407035c6b5375ba263285",
"reference": "ecc143f2cfe16b23675407035c6b5375ba263285",
"shasum": ""
},
"require": {
@ -2192,9 +2192,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.14.0"
"source": "https://github.com/utopia-php/database/tree/0.14.1"
},
"time": "2022-01-21T16:34:34+00:00"
"time": "2022-01-25T13:01:20+00:00"
},
{
"name": "utopia-php/domains",
@ -5715,16 +5715,16 @@
},
{
"name": "symfony/console",
"version": "v6.0.2",
"version": "v6.0.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "dd434fa8d69325e5d210f63070014d889511fcb3"
"reference": "22e8efd019c3270c4f79376234a3f8752cd25490"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/dd434fa8d69325e5d210f63070014d889511fcb3",
"reference": "dd434fa8d69325e5d210f63070014d889511fcb3",
"url": "https://api.github.com/repos/symfony/console/zipball/22e8efd019c3270c4f79376234a3f8752cd25490",
"reference": "22e8efd019c3270c4f79376234a3f8752cd25490",
"shasum": ""
},
"require": {
@ -5790,7 +5790,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v6.0.2"
"source": "https://github.com/symfony/console/tree/v6.0.3"
},
"funding": [
{
@ -5806,7 +5806,7 @@
"type": "tidelift"
}
],
"time": "2021-12-27T21:05:08+00:00"
"time": "2022-01-26T17:23:29+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
@ -6216,16 +6216,16 @@
},
{
"name": "symfony/string",
"version": "v6.0.2",
"version": "v6.0.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "bae261d0c3ac38a1f802b4dfed42094296100631"
"reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/bae261d0c3ac38a1f802b4dfed42094296100631",
"reference": "bae261d0c3ac38a1f802b4dfed42094296100631",
"url": "https://api.github.com/repos/symfony/string/zipball/522144f0c4c004c80d56fa47e40e17028e2eefc2",
"reference": "522144f0c4c004c80d56fa47e40e17028e2eefc2",
"shasum": ""
},
"require": {
@ -6281,7 +6281,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v6.0.2"
"source": "https://github.com/symfony/string/tree/v6.0.3"
},
"funding": [
{
@ -6297,7 +6297,7 @@
"type": "tidelift"
}
],
"time": "2021-12-16T22:13:01+00:00"
"time": "2022-01-02T09:55:41+00:00"
},
{
"name": "textalk/websocket",

View file

@ -394,29 +394,15 @@ services:
networks:
appwrite:
runtimes:
ports:
- 9519:80
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- appwrite-functions:/storage/functions:rw
- appwrite-builds:/storage/builds:rw
- /tmp:/tmp:rw
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
- ./dev:/usr/local/dev
depends_on:
- redis
- mariadb
environment:
- _APP_ENV
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_FUNCTIONS_TIMEOUT
- _APP_FUNCTIONS_BUILD_TIMEOUT
- _APP_FUNCTIONS_CONTAINERS
@ -426,13 +412,11 @@ services:
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_RUNTIME_NETWORK
- _APP_USAGE_STATS
- _APP_STATSD_HOST
- _APP_STATSD_PORT
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- DOCKERHUB_PULL_USERNAME
- DOCKERHUB_PULL_PASSWORD
- OPENRUNTIMES_INACTIVE_THRESHOLD
appwrite-worker-mails:
entrypoint: worker-mails

View file

@ -106,78 +106,7 @@ if(typeof size!=='undefined'){payload['size']=size;}
if(typeof margin!=='undefined'){payload['margin']=margin;}
if(typeof download!=='undefined'){payload['download']=download;}
const uri=new URL(this.config.endpoint+path);payload['project']=this.config.project;for(const[key,value]of Object.entries(this.flatten(payload))){uri.searchParams.append(key,value);}
return uri;}};this.functions={builds:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/builds';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getBuild:(buildId)=>__awaiter(this,void 0,void 0,function*(){if(typeof buildId==='undefined'){throw new AppwriteException('Missing required parameter: "buildId"');}
let path='/builds/{buildId}'.replace('{buildId}',buildId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),retryBuild:(buildId)=>__awaiter(this,void 0,void 0,function*(){if(typeof buildId==='undefined'){throw new AppwriteException('Missing required parameter: "buildId"');}
let path='/builds/{buildId}'.replace('{buildId}',buildId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),list:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/functions';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),create:(functionId,name,execute,runtime,vars,events,schedule,timeout)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof execute==='undefined'){throw new AppwriteException('Missing required parameter: "execute"');}
if(typeof runtime==='undefined'){throw new AppwriteException('Missing required parameter: "runtime"');}
let path='/functions';let payload={};if(typeof functionId!=='undefined'){payload['functionId']=functionId;}
if(typeof name!=='undefined'){payload['name']=name;}
if(typeof execute!=='undefined'){payload['execute']=execute;}
if(typeof runtime!=='undefined'){payload['runtime']=runtime;}
if(typeof vars!=='undefined'){payload['vars']=vars;}
if(typeof events!=='undefined'){payload['events']=events;}
if(typeof schedule!=='undefined'){payload['schedule']=schedule;}
if(typeof timeout!=='undefined'){payload['timeout']=timeout;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),listRuntimes:()=>__awaiter(this,void 0,void 0,function*(){let path='/functions/runtimes';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),get:(functionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),update:(functionId,name,execute,vars,events,schedule,timeout)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof execute==='undefined'){throw new AppwriteException('Missing required parameter: "execute"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
if(typeof execute!=='undefined'){payload['execute']=execute;}
if(typeof vars!=='undefined'){payload['vars']=vars;}
if(typeof events!=='undefined'){payload['events']=events;}
if(typeof schedule!=='undefined'){payload['schedule']=schedule;}
if(typeof timeout!=='undefined'){payload['timeout']=timeout;}
const uri=new URL(this.config.endpoint+path);return yield this.call('put',uri,{'content-type':'application/json',},payload);}),delete:(functionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listDeployments:(functionId,search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/deployments'.replace('{functionId}',functionId);let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createDeployment:(functionId,entrypoint,code,deploy)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof entrypoint==='undefined'){throw new AppwriteException('Missing required parameter: "entrypoint"');}
if(typeof code==='undefined'){throw new AppwriteException('Missing required parameter: "code"');}
if(typeof deploy==='undefined'){throw new AppwriteException('Missing required parameter: "deploy"');}
let path='/functions/{functionId}/deployments'.replace('{functionId}',functionId);let payload={};if(typeof entrypoint!=='undefined'){payload['entrypoint']=entrypoint;}
if(typeof code!=='undefined'){payload['code']=code;}
if(typeof deploy!=='undefined'){payload['deploy']=deploy;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'multipart/form-data',},payload);}),updateDeployment:(functionId,deployment)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deployment==='undefined'){throw new AppwriteException('Missing required parameter: "deployment"');}
let path='/functions/{functionId}/deployments'.replace('{functionId}',functionId);let payload={};if(typeof deployment!=='undefined'){payload['deployment']=deployment;}
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),getDeployment:(functionId,deploymentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deploymentId==='undefined'){throw new AppwriteException('Missing required parameter: "deploymentId"');}
let path='/functions/{functionId}/deployments/{deploymentId}'.replace('{functionId}',functionId).replace('{deploymentId}',deploymentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),deleteDeployment:(functionId,deploymentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deploymentId==='undefined'){throw new AppwriteException('Missing required parameter: "deploymentId"');}
let path='/functions/{functionId}/deployments/{deploymentId}'.replace('{functionId}',functionId).replace('{deploymentId}',deploymentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listExecutions:(functionId,limit,offset,search,cursor,cursorDirection)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/executions'.replace('{functionId}',functionId);let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof search!=='undefined'){payload['search']=search;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createExecution:(functionId,data,async)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/executions'.replace('{functionId}',functionId);let payload={};if(typeof data!=='undefined'){payload['data']=data;}
if(typeof async!=='undefined'){payload['async']=async;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getExecution:(functionId,executionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof executionId==='undefined'){throw new AppwriteException('Missing required parameter: "executionId"');}
let path='/functions/{functionId}/executions/{executionId}'.replace('{functionId}',functionId).replace('{executionId}',executionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getUsage:(functionId,range)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/usage'.replace('{functionId}',functionId);let payload={};if(typeof range!=='undefined'){payload['range']=range;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.database={listCollections:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/database/collections';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
return uri;}};this.database={listCollections:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/database/collections';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
@ -320,6 +249,71 @@ if(typeof offset!=='undefined'){payload['offset']=offset;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getUsage:(range)=>__awaiter(this,void 0,void 0,function*(){let path='/database/usage';let payload={};if(typeof range!=='undefined'){payload['range']=range;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCollectionUsage:(collectionId,range)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
let path='/database/{collectionId}/usage'.replace('{collectionId}',collectionId);let payload={};if(typeof range!=='undefined'){payload['range']=range;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.functions={list:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/functions';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),create:(functionId,name,execute,runtime,vars,events,schedule,timeout)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof execute==='undefined'){throw new AppwriteException('Missing required parameter: "execute"');}
if(typeof runtime==='undefined'){throw new AppwriteException('Missing required parameter: "runtime"');}
let path='/functions';let payload={};if(typeof functionId!=='undefined'){payload['functionId']=functionId;}
if(typeof name!=='undefined'){payload['name']=name;}
if(typeof execute!=='undefined'){payload['execute']=execute;}
if(typeof runtime!=='undefined'){payload['runtime']=runtime;}
if(typeof vars!=='undefined'){payload['vars']=vars;}
if(typeof events!=='undefined'){payload['events']=events;}
if(typeof schedule!=='undefined'){payload['schedule']=schedule;}
if(typeof timeout!=='undefined'){payload['timeout']=timeout;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),listRuntimes:()=>__awaiter(this,void 0,void 0,function*(){let path='/functions/runtimes';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),get:(functionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),update:(functionId,name,execute,vars,events,schedule,timeout)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof execute==='undefined'){throw new AppwriteException('Missing required parameter: "execute"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
if(typeof execute!=='undefined'){payload['execute']=execute;}
if(typeof vars!=='undefined'){payload['vars']=vars;}
if(typeof events!=='undefined'){payload['events']=events;}
if(typeof schedule!=='undefined'){payload['schedule']=schedule;}
if(typeof timeout!=='undefined'){payload['timeout']=timeout;}
const uri=new URL(this.config.endpoint+path);return yield this.call('put',uri,{'content-type':'application/json',},payload);}),delete:(functionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listDeployments:(functionId,search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/deployments'.replace('{functionId}',functionId);let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createDeployment:(functionId,entrypoint,code,activate)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof entrypoint==='undefined'){throw new AppwriteException('Missing required parameter: "entrypoint"');}
if(typeof code==='undefined'){throw new AppwriteException('Missing required parameter: "code"');}
if(typeof activate==='undefined'){throw new AppwriteException('Missing required parameter: "activate"');}
let path='/functions/{functionId}/deployments'.replace('{functionId}',functionId);let payload={};if(typeof entrypoint!=='undefined'){payload['entrypoint']=entrypoint;}
if(typeof code!=='undefined'){payload['code']=code;}
if(typeof activate!=='undefined'){payload['activate']=activate;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'multipart/form-data',},payload);}),getDeployment:(functionId,deploymentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deploymentId==='undefined'){throw new AppwriteException('Missing required parameter: "deploymentId"');}
let path='/functions/{functionId}/deployments/{deploymentId}'.replace('{functionId}',functionId).replace('{deploymentId}',deploymentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateDeployment:(functionId,deploymentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deploymentId==='undefined'){throw new AppwriteException('Missing required parameter: "deploymentId"');}
let path='/functions/{functionId}/deployments/{deploymentId}'.replace('{functionId}',functionId).replace('{deploymentId}',deploymentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),deleteDeployment:(functionId,deploymentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deploymentId==='undefined'){throw new AppwriteException('Missing required parameter: "deploymentId"');}
let path='/functions/{functionId}/deployments/{deploymentId}'.replace('{functionId}',functionId).replace('{deploymentId}',deploymentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),retryBuild:(functionId,deploymentId,buildId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deploymentId==='undefined'){throw new AppwriteException('Missing required parameter: "deploymentId"');}
if(typeof buildId==='undefined'){throw new AppwriteException('Missing required parameter: "buildId"');}
let path='/functions/{functionId}/deployments/{deploymentId}/builds/{buildId}'.replace('{functionId}',functionId).replace('{deploymentId}',deploymentId).replace('{buildId}',buildId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),listExecutions:(functionId,limit,offset,search,cursor,cursorDirection)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/executions'.replace('{functionId}',functionId);let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof search!=='undefined'){payload['search']=search;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createExecution:(functionId,data,async)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/executions'.replace('{functionId}',functionId);let payload={};if(typeof data!=='undefined'){payload['data']=data;}
if(typeof async!=='undefined'){payload['async']=async;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getExecution:(functionId,executionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof executionId==='undefined'){throw new AppwriteException('Missing required parameter: "executionId"');}
let path='/functions/{functionId}/executions/{executionId}'.replace('{functionId}',functionId).replace('{executionId}',executionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getUsage:(functionId,range)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/usage'.replace('{functionId}',functionId);let payload={};if(typeof range!=='undefined'){payload['range']=range;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.health={get:()=>__awaiter(this,void 0,void 0,function*(){let path='/health';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getAntivirus:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/anti-virus';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCache:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/cache';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getDB:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/db';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueCertificates:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/certificates';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueFunctions:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/functions';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueLogs:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/logs';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueUsage:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/usage';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueWebhooks:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/webhooks';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getStorageLocal:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/storage/local';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getTime:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/time';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.locale={get:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getContinents:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/continents';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCountries:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/countries';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCountriesEU:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/countries/eu';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCountriesPhones:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/countries/phones';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCurrencies:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/currencies';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getLanguages:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/languages';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.projects={list:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/projects';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}

View file

@ -106,78 +106,7 @@ if(typeof size!=='undefined'){payload['size']=size;}
if(typeof margin!=='undefined'){payload['margin']=margin;}
if(typeof download!=='undefined'){payload['download']=download;}
const uri=new URL(this.config.endpoint+path);payload['project']=this.config.project;for(const[key,value]of Object.entries(this.flatten(payload))){uri.searchParams.append(key,value);}
return uri;}};this.functions={builds:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/builds';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getBuild:(buildId)=>__awaiter(this,void 0,void 0,function*(){if(typeof buildId==='undefined'){throw new AppwriteException('Missing required parameter: "buildId"');}
let path='/builds/{buildId}'.replace('{buildId}',buildId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),retryBuild:(buildId)=>__awaiter(this,void 0,void 0,function*(){if(typeof buildId==='undefined'){throw new AppwriteException('Missing required parameter: "buildId"');}
let path='/builds/{buildId}'.replace('{buildId}',buildId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),list:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/functions';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),create:(functionId,name,execute,runtime,vars,events,schedule,timeout)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof execute==='undefined'){throw new AppwriteException('Missing required parameter: "execute"');}
if(typeof runtime==='undefined'){throw new AppwriteException('Missing required parameter: "runtime"');}
let path='/functions';let payload={};if(typeof functionId!=='undefined'){payload['functionId']=functionId;}
if(typeof name!=='undefined'){payload['name']=name;}
if(typeof execute!=='undefined'){payload['execute']=execute;}
if(typeof runtime!=='undefined'){payload['runtime']=runtime;}
if(typeof vars!=='undefined'){payload['vars']=vars;}
if(typeof events!=='undefined'){payload['events']=events;}
if(typeof schedule!=='undefined'){payload['schedule']=schedule;}
if(typeof timeout!=='undefined'){payload['timeout']=timeout;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),listRuntimes:()=>__awaiter(this,void 0,void 0,function*(){let path='/functions/runtimes';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),get:(functionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),update:(functionId,name,execute,vars,events,schedule,timeout)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof execute==='undefined'){throw new AppwriteException('Missing required parameter: "execute"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
if(typeof execute!=='undefined'){payload['execute']=execute;}
if(typeof vars!=='undefined'){payload['vars']=vars;}
if(typeof events!=='undefined'){payload['events']=events;}
if(typeof schedule!=='undefined'){payload['schedule']=schedule;}
if(typeof timeout!=='undefined'){payload['timeout']=timeout;}
const uri=new URL(this.config.endpoint+path);return yield this.call('put',uri,{'content-type':'application/json',},payload);}),delete:(functionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listDeployments:(functionId,search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/deployments'.replace('{functionId}',functionId);let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createDeployment:(functionId,entrypoint,code,deploy)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof entrypoint==='undefined'){throw new AppwriteException('Missing required parameter: "entrypoint"');}
if(typeof code==='undefined'){throw new AppwriteException('Missing required parameter: "code"');}
if(typeof deploy==='undefined'){throw new AppwriteException('Missing required parameter: "deploy"');}
let path='/functions/{functionId}/deployments'.replace('{functionId}',functionId);let payload={};if(typeof entrypoint!=='undefined'){payload['entrypoint']=entrypoint;}
if(typeof code!=='undefined'){payload['code']=code;}
if(typeof deploy!=='undefined'){payload['deploy']=deploy;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'multipart/form-data',},payload);}),updateDeployment:(functionId,deployment)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deployment==='undefined'){throw new AppwriteException('Missing required parameter: "deployment"');}
let path='/functions/{functionId}/deployments'.replace('{functionId}',functionId);let payload={};if(typeof deployment!=='undefined'){payload['deployment']=deployment;}
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),getDeployment:(functionId,deploymentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deploymentId==='undefined'){throw new AppwriteException('Missing required parameter: "deploymentId"');}
let path='/functions/{functionId}/deployments/{deploymentId}'.replace('{functionId}',functionId).replace('{deploymentId}',deploymentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),deleteDeployment:(functionId,deploymentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deploymentId==='undefined'){throw new AppwriteException('Missing required parameter: "deploymentId"');}
let path='/functions/{functionId}/deployments/{deploymentId}'.replace('{functionId}',functionId).replace('{deploymentId}',deploymentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listExecutions:(functionId,limit,offset,search,cursor,cursorDirection)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/executions'.replace('{functionId}',functionId);let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof search!=='undefined'){payload['search']=search;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createExecution:(functionId,data,async)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/executions'.replace('{functionId}',functionId);let payload={};if(typeof data!=='undefined'){payload['data']=data;}
if(typeof async!=='undefined'){payload['async']=async;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getExecution:(functionId,executionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof executionId==='undefined'){throw new AppwriteException('Missing required parameter: "executionId"');}
let path='/functions/{functionId}/executions/{executionId}'.replace('{functionId}',functionId).replace('{executionId}',executionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getUsage:(functionId,range)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/usage'.replace('{functionId}',functionId);let payload={};if(typeof range!=='undefined'){payload['range']=range;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.database={listCollections:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/database/collections';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
return uri;}};this.database={listCollections:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/database/collections';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
@ -320,6 +249,71 @@ if(typeof offset!=='undefined'){payload['offset']=offset;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getUsage:(range)=>__awaiter(this,void 0,void 0,function*(){let path='/database/usage';let payload={};if(typeof range!=='undefined'){payload['range']=range;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCollectionUsage:(collectionId,range)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
let path='/database/{collectionId}/usage'.replace('{collectionId}',collectionId);let payload={};if(typeof range!=='undefined'){payload['range']=range;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.functions={list:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/functions';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),create:(functionId,name,execute,runtime,vars,events,schedule,timeout)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof execute==='undefined'){throw new AppwriteException('Missing required parameter: "execute"');}
if(typeof runtime==='undefined'){throw new AppwriteException('Missing required parameter: "runtime"');}
let path='/functions';let payload={};if(typeof functionId!=='undefined'){payload['functionId']=functionId;}
if(typeof name!=='undefined'){payload['name']=name;}
if(typeof execute!=='undefined'){payload['execute']=execute;}
if(typeof runtime!=='undefined'){payload['runtime']=runtime;}
if(typeof vars!=='undefined'){payload['vars']=vars;}
if(typeof events!=='undefined'){payload['events']=events;}
if(typeof schedule!=='undefined'){payload['schedule']=schedule;}
if(typeof timeout!=='undefined'){payload['timeout']=timeout;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),listRuntimes:()=>__awaiter(this,void 0,void 0,function*(){let path='/functions/runtimes';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),get:(functionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),update:(functionId,name,execute,vars,events,schedule,timeout)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
if(typeof execute==='undefined'){throw new AppwriteException('Missing required parameter: "execute"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
if(typeof execute!=='undefined'){payload['execute']=execute;}
if(typeof vars!=='undefined'){payload['vars']=vars;}
if(typeof events!=='undefined'){payload['events']=events;}
if(typeof schedule!=='undefined'){payload['schedule']=schedule;}
if(typeof timeout!=='undefined'){payload['timeout']=timeout;}
const uri=new URL(this.config.endpoint+path);return yield this.call('put',uri,{'content-type':'application/json',},payload);}),delete:(functionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}'.replace('{functionId}',functionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listDeployments:(functionId,search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/deployments'.replace('{functionId}',functionId);let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createDeployment:(functionId,entrypoint,code,activate)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof entrypoint==='undefined'){throw new AppwriteException('Missing required parameter: "entrypoint"');}
if(typeof code==='undefined'){throw new AppwriteException('Missing required parameter: "code"');}
if(typeof activate==='undefined'){throw new AppwriteException('Missing required parameter: "activate"');}
let path='/functions/{functionId}/deployments'.replace('{functionId}',functionId);let payload={};if(typeof entrypoint!=='undefined'){payload['entrypoint']=entrypoint;}
if(typeof code!=='undefined'){payload['code']=code;}
if(typeof activate!=='undefined'){payload['activate']=activate;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'multipart/form-data',},payload);}),getDeployment:(functionId,deploymentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deploymentId==='undefined'){throw new AppwriteException('Missing required parameter: "deploymentId"');}
let path='/functions/{functionId}/deployments/{deploymentId}'.replace('{functionId}',functionId).replace('{deploymentId}',deploymentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateDeployment:(functionId,deploymentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deploymentId==='undefined'){throw new AppwriteException('Missing required parameter: "deploymentId"');}
let path='/functions/{functionId}/deployments/{deploymentId}'.replace('{functionId}',functionId).replace('{deploymentId}',deploymentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),deleteDeployment:(functionId,deploymentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deploymentId==='undefined'){throw new AppwriteException('Missing required parameter: "deploymentId"');}
let path='/functions/{functionId}/deployments/{deploymentId}'.replace('{functionId}',functionId).replace('{deploymentId}',deploymentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),retryBuild:(functionId,deploymentId,buildId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deploymentId==='undefined'){throw new AppwriteException('Missing required parameter: "deploymentId"');}
if(typeof buildId==='undefined'){throw new AppwriteException('Missing required parameter: "buildId"');}
let path='/functions/{functionId}/deployments/{deploymentId}/builds/{buildId}'.replace('{functionId}',functionId).replace('{deploymentId}',deploymentId).replace('{buildId}',buildId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),listExecutions:(functionId,limit,offset,search,cursor,cursorDirection)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/executions'.replace('{functionId}',functionId);let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof search!=='undefined'){payload['search']=search;}
if(typeof cursor!=='undefined'){payload['cursor']=cursor;}
if(typeof cursorDirection!=='undefined'){payload['cursorDirection']=cursorDirection;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createExecution:(functionId,data,async)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/executions'.replace('{functionId}',functionId);let payload={};if(typeof data!=='undefined'){payload['data']=data;}
if(typeof async!=='undefined'){payload['async']=async;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getExecution:(functionId,executionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof executionId==='undefined'){throw new AppwriteException('Missing required parameter: "executionId"');}
let path='/functions/{functionId}/executions/{executionId}'.replace('{functionId}',functionId).replace('{executionId}',executionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getUsage:(functionId,range)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
let path='/functions/{functionId}/usage'.replace('{functionId}',functionId);let payload={};if(typeof range!=='undefined'){payload['range']=range;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.health={get:()=>__awaiter(this,void 0,void 0,function*(){let path='/health';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getAntivirus:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/anti-virus';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCache:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/cache';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getDB:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/db';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueCertificates:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/certificates';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueFunctions:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/functions';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueLogs:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/logs';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueUsage:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/usage';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getQueueWebhooks:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/queue/webhooks';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getStorageLocal:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/storage/local';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getTime:()=>__awaiter(this,void 0,void 0,function*(){let path='/health/time';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.locale={get:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getContinents:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/continents';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCountries:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/countries';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCountriesEU:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/countries/eu';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCountriesPhones:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/countries/phones';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCurrencies:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/currencies';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getLanguages:()=>__awaiter(this,void 0,void 0,function*(){let path='/locale/languages';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.projects={list:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/projects';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}

File diff suppressed because it is too large Load diff

View file

@ -52,9 +52,9 @@ class Deployment extends Model
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('deploy', [
->addRule('activate', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether the deployment should be automatically deployed.',
'description' => 'Whether the deployment should be automatically activated.',
'default' => false,
'example' => true,
])

274
src/Executor/Executor.php Normal file
View file

@ -0,0 +1,274 @@
<?php
namespace Executor;
use Exception;
use Utopia\App;
class Executor
{
const METHOD_GET = 'GET';
const METHOD_POST = 'POST';
const METHOD_PUT = 'PUT';
const METHOD_PATCH = 'PATCH';
const METHOD_DELETE = 'DELETE';
const METHOD_HEAD = 'HEAD';
const METHOD_OPTIONS = 'OPTIONS';
const METHOD_CONNECT = 'CONNECT';
const METHOD_TRACE = 'TRACE';
private $endpoint;
private $selfSigned = false;
protected $headers = [
'content-type' => '',
];
public function __construct(string $endpoint = 'http://appwrite-executor/v1')
{
$this->endpoint = $endpoint;
}
public function createRuntime(
string $functionId,
string $deploymentId,
string $projectId,
string $source,
array $vars,
string $runtime,
string $baseImage)
{
$route = "/runtimes";
$headers = [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-executor-key' => App::getEnv('_APP_EXECUTOR_SECRET', '')
];
$params = [
'runtimeId' => "$projectId-$deploymentId",
'source' => $source,
'destination' => APP_STORAGE_BUILDS . "/app-$projectId",
'vars' => $vars,
'runtime' => $runtime,
'baseImage' => $baseImage
];
$response = $this->call(self::METHOD_POST, $route, $headers, $params, true, 30);
$status = $response['headers']['status-code'];
if ($status >= 400) {
throw new \Exception($response['body']['message'], $status);
}
return $response['body'];
}
public function deleteRuntime(string $projectId, string $functionId, string $deploymentId)
{
$runtimeId = "$projectId-$deploymentId";
$route = "/runtimes/$runtimeId";
$headers = [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-executor-key' => App::getEnv('_APP_EXECUTOR_SECRET', '')
];
$params = [];
$response = $this->call(self::METHOD_DELETE, $route, $headers, $params, true, 30);
$status = $response['headers']['status-code'];
if ($status >= 400) {
throw new \Exception($response['body']['message'], $status);
}
return $response['body'];
}
public function createExecution(
string $projectId,
string $functionId,
string $deploymentId,
string $path,
array $vars,
string $entrypoint,
string $data,
string $runtime,
string $baseImage,
$timeout
)
{
$route = "/execution";
$headers = [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-executor-key' => App::getEnv('_APP_EXECUTOR_SECRET', '')
];
$params = [
'runtimeId' => "$projectId-$deploymentId",
'path' => $path,
'vars' => $vars,
'data' => $data,
'runtime' => $runtime,
'entrypoint' => $entrypoint,
'timeout' => $timeout,
'baseImage' => $baseImage,
];
$response = $this->call(self::METHOD_POST, $route, $headers, $params, true, 30);
$status = $response['headers']['status-code'];
if ($status >= 400) {
throw new \Exception($response['body']['message'], $status);
}
return $response['body'];
}
/**
* Call
*
* Make an API call
*
* @param string $method
* @param string $path
* @param array $params
* @param array $headers
* @param bool $decode
* @return array|string
* @throws Exception
*/
public function call(string $method, string $path = '', array $headers = [], array $params = [], bool $decode = true, int $timeout = 15)
{
$headers = array_merge($this->headers, $headers);
$ch = curl_init($this->endpoint . $path . (($method == self::METHOD_GET && !empty($params)) ? '?' . http_build_query($params) : ''));
$responseHeaders = [];
$responseStatus = -1;
$responseType = '';
$responseBody = '';
switch ($headers['content-type']) {
case 'application/json':
$query = json_encode($params);
break;
case 'multipart/form-data':
$query = $this->flatten($params);
break;
default:
$query = http_build_query($params);
break;
}
foreach ($headers as $i => $header) {
$headers[] = $i . ':' . $header;
unset($headers[$i]);
}
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($curl, $header) use (&$responseHeaders) {
$len = strlen($header);
$header = explode(':', $header, 2);
if (count($header) < 2) { // ignore invalid headers
return $len;
}
$responseHeaders[strtolower(trim($header[0]))] = trim($header[1]);
return $len;
});
if ($method != self::METHOD_GET) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
}
// Allow self signed certificates
if ($this->selfSigned) {
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
}
$responseBody = curl_exec($ch);
$responseType = $responseHeaders['content-type'] ?? '';
$responseStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($decode) {
switch (substr($responseType, 0, strpos($responseType, ';'))) {
case 'application/json':
$json = json_decode($responseBody, true);
if ($json === null) {
throw new Exception('Failed to parse response: '.$responseBody);
}
$responseBody = $json;
$json = null;
break;
}
}
if ((curl_errno($ch)/* || 200 != $responseStatus*/)) {
throw new Exception(curl_error($ch) . ' with status code ' . $responseStatus, $responseStatus);
}
curl_close($ch);
$responseHeaders['status-code'] = $responseStatus;
if ($responseStatus === 500) {
echo 'Server error('.$method.': '.$path.'. Params: '.json_encode($params).'): '.json_encode($responseBody)."\n";
}
return [
'headers' => $responseHeaders,
'body' => $responseBody
];
}
/**
* Parse Cookie String
*
* @param string $cookie
* @return array
*/
public function parseCookie(string $cookie): array
{
$cookies = [];
parse_str(strtr($cookie, array('&' => '%26', '+' => '%2B', ';' => '&')), $cookies);
return $cookies;
}
/**
* Flatten params array to PHP multiple format
*
* @param array $data
* @param string $prefix
* @return array
*/
protected function flatten(array $data, string $prefix = ''): array
{
$output = [];
foreach ($data as $key => $value) {
$finalKey = $prefix ? "{$prefix}[{$key}]" : $key;
if (is_array($value)) {
$output += $this->flatten($value, $finalKey); // @todo: handle name collision here if needed
} else {
$output[$finalKey] = $value;
}
}
return $output;
}
}

View file

@ -89,15 +89,11 @@ class FunctionsCustomClientTest extends Scope
// Wait for deployment to be built.
sleep(10);
$function = $this->client->call(Client::METHOD_PATCH, '/functions/'.$function['body']['$id'].'/deployment', [
$function = $this->client->call(Client::METHOD_PATCH, '/functions/'.$function['body']['$id'].'/deployments/'.$deploymentId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'deployment' => $deploymentId,
]);
// var_dump($function);
], []);
$this->assertEquals(200, $function['headers']['status-code']);
@ -176,13 +172,11 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals(201, $deployment['headers']['status-code']);
$function = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/deployment', [
$function = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/deployments/'.$deploymentId, [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'deployment' => $deploymentId,
]);
], []);
$this->assertEquals(200, $function['headers']['status-code']);
@ -360,13 +354,11 @@ class FunctionsCustomClientTest extends Scope
// Wait for deployment to be built.
sleep(10);
$function = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/deployment', [
$function = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/deployments/'.$deploymentId, [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apikey,
], [
'deployment' => $deploymentId,
]);
], []);
$this->assertEquals(200, $function['headers']['status-code']);

View file

@ -310,12 +310,10 @@ class FunctionsCustomServerTest extends Scope
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_PATCH, '/functions/'.$data['functionId'].'/deployment', array_merge([
$response = $this->client->call(Client::METHOD_PATCH, '/functions/'.$data['functionId'].'/deployments/'.$data['deploymentId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'deployment' => $data['deploymentId'],
]);
], $this->getHeaders()), []);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
@ -687,13 +685,10 @@ class FunctionsCustomServerTest extends Scope
// Allow build step to run
sleep(10);
$deployment = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/deployment', array_merge([
$deployment = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/deployments/'.$deploymentId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'functionId' => $functionId,
'deployment' => $deploymentId,
]);
], $this->getHeaders()), []);
$this->assertEquals(200, $deployment['headers']['status-code']);
@ -779,12 +774,10 @@ class FunctionsCustomServerTest extends Scope
// Allow build step to run
sleep(10);
$deployment = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/deployment', array_merge([
$deployment = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/deployments/'.$deploymentId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'deployment' => $deploymentId,
]);
], $this->getHeaders()), []);
$this->assertEquals(200, $deployment['headers']['status-code']);

View file

@ -1002,13 +1002,11 @@ class RealtimeCustomClientTest extends Scope
// Wait for deployment to be built.
sleep(5);
$response = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/deployment', array_merge([
$response = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/deployments/'.$deploymentId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'deployment' => $deploymentId,
]);
]), []);
$this->assertEquals($response['headers']['status-code'], 200);
$this->assertNotEmpty($response['body']['$id']);
@ -1049,6 +1047,15 @@ class RealtimeCustomClientTest extends Scope
$this->assertNotEmpty($responseUpdate['data']['payload']);
$client->close();
// Cleanup : Delete function
$response = $this->client->call(Client::METHOD_DELETE, '/functions/'. $functionId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], []);
$this->assertEquals(204, $response['headers']['status-code']);
}
public function testChannelTeams(): array

View file

@ -434,12 +434,10 @@ class WebhooksCustomServerTest extends Scope
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_PATCH, '/functions/'.$data['functionId'].'/deployment', array_merge([
$response = $this->client->call(Client::METHOD_PATCH, '/functions/'.$data['functionId'].'/deployments/'.$data['deploymentId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'deployment' => $data['deploymentId'],
]);
], $this->getHeaders()), []);
$this->assertEquals($response['headers']['status-code'], 200);
$this->assertNotEmpty($response['body']['$id']);
@ -476,7 +474,7 @@ class WebhooksCustomServerTest extends Scope
$execution = $this->client->call(Client::METHOD_POST, '/functions/'.$data['functionId'].'/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), ['executionId' => 'unique()',]);
], $this->getHeaders()), []);
$this->assertEquals($execution['headers']['status-code'], 201);
$this->assertNotEmpty($execution['body']['$id']);