1
0
Fork 0
mirror of synced 2024-06-26 18:20:43 +12:00

Merge branch 'feat-functions-refactor' of https://github.com/appwrite/appwrite into feat-fx-review

This commit is contained in:
Torsten Dittmann 2022-01-25 17:35:53 +01:00
commit 0763e9eeb7
22 changed files with 1592 additions and 1342 deletions

View file

@ -1,6 +1,8 @@
# Unreleased Version 0.13.0
- Added ability to create syncronous function executions
- Introduced new execution model for functions
- Improved functions execution times
- Improved functions execution times
# Version 0.12.1
## Bugs

View file

@ -270,7 +270,8 @@ RUN chmod +x /usr/local/bin/doctor && \
chmod +x /usr/local/bin/worker-deletes && \
chmod +x /usr/local/bin/worker-functions && \
chmod +x /usr/local/bin/worker-mails && \
chmod +x /usr/local/bin/worker-webhooks
chmod +x /usr/local/bin/worker-webhooks && \
chmod +x /usr/local/bin/executor
# Letsencrypt Permissions
RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/

View file

@ -2015,7 +2015,7 @@ $collections = [
'filters' => [],
],
[
'$id' => 'automaticDeploy',
'$id' => 'deploy',
'type' => Database::VAR_BOOLEAN,
'format' => '',
'size' => 0,
@ -2116,7 +2116,7 @@ $collections = [
'filters' => [],
],
[
'$id' => 'buildTime',
'$id' => 'time',
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
@ -2127,7 +2127,7 @@ $collections = [
'filters' => [],
],
[
'$id' => 'envVars',
'$id' => 'vars',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
@ -2159,6 +2159,17 @@ $collections = [
'array' => false,
'filters' => [],
],
[
'$id' => 'search',
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
@ -2168,6 +2179,13 @@ $collections = [
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_search',
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [2048],
'orders' => [Database::ORDER_ASC],
],
],
],

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

@ -45,7 +45,7 @@ App::post('/v1/functions')
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('runtime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution runtime.')
->param('vars', new stdClass(), new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
->param('vars', [], new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
->param('events', [], new ArrayList(new WhiteList(array_keys(Config::getParam('events')), true)), 'Events list.', true)
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
->param('timeout', 15, new Range(1, 900), 'Function maximum execution time in seconds.', true)
@ -496,14 +496,14 @@ App::post('/v1/functions/:functionId/tags')
->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('automaticDeploy', false, new Boolean(true), 'Automatically deploy the function when it is finished building.', false)
->param('deploy', false, new Boolean(true), 'Automatically deploy the function when it is finished building.', false)
->inject('request')
->inject('response')
->inject('dbForProject')
->inject('usage')
->inject('user')
->inject('project')
->action(function ($functionId, $entrypoint, $file, $automaticDeploy, $request, $response, $dbForProject, $usage, $user, $project) {
->action(function ($functionId, $entrypoint, $file, $deploy, $request, $response, $dbForProject, $usage, $user, $project) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
@ -552,15 +552,15 @@ App::post('/v1/functions/:functionId/tags')
throw new Exception('Failed moving file', 500);
}
if ((bool) $automaticDeploy) {
// Remove automaticDeploy for all other tags.
if ((bool) $deploy) {
// Remove deploy for all other tags.
$tags = $dbForProject->find('tags', [
new Query('automaticDeploy', Query::TYPE_EQUAL, [true]),
new Query('deploy', Query::TYPE_EQUAL, [true]),
new Query('functionId', Query::TYPE_EQUAL, [$functionId])
]);
foreach ($tags as $tag) {
$tag->setAttribute('automaticDeploy', false);
$tag->setAttribute('deploy', false);
$dbForProject->updateDocument('tags', $tag->getId(), $tag);
}
}
@ -579,7 +579,7 @@ App::post('/v1/functions/:functionId/tags')
'status' => 'processing',
'buildStdout' => '',
'buildStderr' => '',
'automaticDeploy' => ($automaticDeploy === 'true'),
'deploy' => ($deploy === 'true'),
]));
$usage
@ -1055,50 +1055,47 @@ App::get('/v1/functions/:functionId/executions/:executionId')
});
App::get('/v1/builds')
->groups(['api', 'functions'])
->desc('Get Builds')
->label('scope', 'execution.read')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'listBuilds')
->label('sdk.description', '/docs/references/functions/list-builds.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_BUILD_LIST)
->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('cursor', '', new UID(), 'ID of the build used as the starting point for the query, excluding the build itself. Should be used for efficient pagination when working with large sets of data.', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->inject('response')
->inject('dbForProject')
->action(function ($limit, $offset, $search, $cursor, $cursorDirection, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
->groups(['api', 'functions'])
->desc('List Builds')
->label('scope', 'functions.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'builds')
->label('sdk.description', '/docs/references/functions/list-builds.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_BUILD_LIST)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('limit', 25, new Range(0, 100), 'Maximum number of builds to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this value to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursor', '', new UID(), 'ID of the function used as the starting point for the query, excluding the function itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
->inject('response')
->inject('dbForProject')
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
if (!empty($cursor)) {
$cursorExecution = $dbForProject->getDocument('builds', $cursor);
if (!empty($cursor)) {
$cursorFunction = $dbForProject->getDocument('builds', $cursor);
if ($cursorExecution->isEmpty()) {
throw new Exception("Execution '{$cursor}' for the 'cursor' value not found.", 400);
}
if ($cursorFunction->isEmpty()) {
throw new Exception("Build '{$cursor}' for the 'cursor' value not found.", 400);
}
}
$queries = [];
$queries = [];
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
}
if (!empty($search)) {
$queries[] = new Query('search', Query::TYPE_SEARCH, [$search]);
}
$results = $dbForProject->find('builds', $queries, $limit, $offset, [], [Database::ORDER_DESC], $cursorExecution ?? null, $cursorDirection);
$sum = $dbForProject->count('builds', $queries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
'builds' => $results,
'sum' => $sum,
]), Response::MODEL_BUILD_LIST);
});
$response->dynamic(new Document([
'builds' => $dbForProject->find('builds', $queries, $limit, $offset, [], [$orderType], $cursorFunction ?? null, $cursorDirection),
'sum' => $dbForProject->count('builds', $queries, APP_LIMIT_COUNT),
]), Response::MODEL_BUILD_LIST);
});
App::get('/v1/builds/:buildId')
->groups(['api', 'functions'])

View file

@ -99,19 +99,22 @@ try {
Co\run(function () use ($runtimes, $orchestrationPool) {
foreach ($runtimes as $runtime) {
go(function () use ($runtime, $orchestrationPool) {
$orchestration = $orchestrationPool->get();
try {
$orchestration = $orchestrationPool->get();
Console::info('Warming up ' . $runtime['name'] . ' ' . $runtime['version'] . ' environment...');
Console::info('Warming up ' . $runtime['name'] . ' ' . $runtime['version'] . ' environment...');
$response = $orchestration->pull($runtime['image']);
$response = $orchestration->pull($runtime['image']);
if ($response) {
Console::success("Successfully Warmed up {$runtime['name']} {$runtime['version']}!");
} else {
Console::warning("Failed to Warmup {$runtime['name']} {$runtime['version']}!");
if ($response) {
Console::success("Successfully Warmed up {$runtime['name']} {$runtime['version']}!");
} else {
Console::warning("Failed to Warmup {$runtime['name']} {$runtime['version']}!");
}
} catch (\Throwable $th) {
} finally {
$orchestrationPool->put($orchestration);
}
$orchestrationPool->put($orchestration);
});
}
});
@ -124,11 +127,15 @@ try {
$activeFunctions->create();
Co\run(function () use ($orchestrationPool, $activeFunctions) {
$orchestration = $orchestrationPool->get();
$executionStart = \microtime(true);
try {
$orchestration = $orchestrationPool->get();
$executionStart = \microtime(true);
$residueList = $orchestration->list(['label' => 'appwrite-type=function']);
} catch (\Throwable $th) {
} finally {
$orchestrationPool->put($orchestration);
}
$residueList = $orchestration->list(['label' => 'appwrite-type=function']);
$orchestrationPool->put($orchestration);
foreach ($residueList as $value) {
go(fn () => $activeFunctions->set($value->getName(), [
@ -196,7 +203,7 @@ function createRuntimeServer(string $functionId, string $projectId, string $tagI
'INTERNAL_RUNTIME_KEY' => $secret
]);
$vars = \array_merge($vars, $build->getAttribute('envVars', [])); // for gettng endpoint.
$vars = \array_merge($vars, $build->getAttribute('vars', [])); // for gettng endpoint.
$container = 'appwrite-function-' . $tag->getId();
@ -235,7 +242,9 @@ function createRuntimeServer(string $functionId, string $projectId, string $tagI
$device = Storage::getDevice('builds');
if (!\file_exists($tagPathTargetDir)) {
if (!\mkdir($tagPathTargetDir, 0777, true)) {
if (@\mkdir($tagPathTargetDir, 0777, true)) {
\chmod($tagPathTargetDir, 0777);
} else {
throw new Exception('Can\'t create directory ' . $tagPathTargetDir);
}
}
@ -269,9 +278,7 @@ function createRuntimeServer(string $functionId, string $projectId, string $tagI
->setMemory(App::getEnv('_APP_FUNCTIONS_MEMORY', '256'))
->setSwap(App::getEnv('_APP_FUNCTIONS_MEMORY_SWAP', '256'));
foreach ($vars as $key => $value) {
$vars[$key] = strval($value);
}
$vars = array_map(fn ($v) => strval($v), $vars);
// Launch runtime server
$id = $orchestration->run(
@ -312,9 +319,8 @@ function createRuntimeServer(string $functionId, string $projectId, string $tagI
} catch (\Throwable $th) {
$orchestrationPool->put($orchestration);
throw $th;
} finally {
$orchestrationPool->put($orchestration);
}
$orchestrationPool->put($orchestration);
};
function execute(string $trigger, string $projectId, string $executionId, string $functionId, Database $database, string $event = '', string $eventData = '', string $data = '', array $webhooks = [], string $userId = '', string $jwt = ''): array
@ -394,7 +400,7 @@ function execute(string $trigger, string $projectId, string $executionId, string
'APPWRITE_FUNCTION_PROJECT_ID' => $projectId,
]);
$vars = \array_merge($vars, $build->getAttribute('envVars', []));
$vars = \array_merge($vars, $build->getAttribute('vars', []));
$container = 'appwrite-function-' . $tag->getId();
@ -405,7 +411,7 @@ function execute(string $trigger, string $projectId, string $executionId, string
$database->createDocument('builds', new Document([
'$id' => $buildId,
'$read' => ($userId !== '') ? ['user:' . $userId] : [],
'$write' => ['role:all'],
'$write' => [],
'dateCreated' => time(),
'status' => 'processing',
'outputPath' => '',
@ -414,8 +420,8 @@ function execute(string $trigger, string $projectId, string $executionId, string
'sourceType' => Storage::DEVICE_LOCAL,
'stdout' => '',
'stderr' => '',
'buildTime' => 0,
'envVars' => [
'time' => 0,
'vars' => [
'ENTRYPOINT_NAME' => $tag->getAttribute('entrypoint'),
'APPWRITE_FUNCTION_ID' => $function->getId(),
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''),
@ -490,7 +496,7 @@ function execute(string $trigger, string $projectId, string $executionId, string
'APPWRITE_FUNCTION_PROJECT_ID' => $projectId
]);
$vars = \array_merge($vars, $build->getAttribute('envVars', []));
$vars = \array_merge($vars, $build->getAttribute('vars', []));
$stdout = '';
$stderr = '';
@ -512,7 +518,7 @@ function execute(string $trigger, string $projectId, string $executionId, string
$body = \json_encode([
'path' => '/usr/code',
'file' => $build->getAttribute('envVars', [])['ENTRYPOINT_NAME'],
'file' => $build->getAttribute('vars', [])['ENTRYPOINT_NAME'],
'env' => $vars,
'payload' => $data,
'timeout' => $function->getAttribute('timeout', (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900))
@ -706,7 +712,9 @@ function runBuildStage(string $buildId, string $projectID): Document
// Perform various checks
if (!\file_exists($tagPathTargetDir)) {
if (!\mkdir($tagPathTargetDir, 0777, true)) {
if (@\mkdir($tagPathTargetDir, 0777, true)) {
\chmod($tagPathTargetDir, 0777);
} else {
throw new Exception('Can\'t create directory ' . $tagPathTargetDir);
}
}
@ -726,26 +734,27 @@ function runBuildStage(string $buildId, string $projectID): Document
throw new Exception('Code is not readable: ' . $build->getAttribute('source', ''));
}
$vars = $build->getAttribute('envVars', []);
$vars = $build->getAttribute('vars', []);
// Start tracking time
$buildStart = \microtime(true);
$buildTime = \time();
$time = \time();
$orchestration
->setCpus(App::getEnv('_APP_FUNCTIONS_CPUS', 0))
->setMemory(App::getEnv('_APP_FUNCTIONS_MEMORY', 256))
->setSwap(App::getEnv('_APP_FUNCTIONS_MEMORY_SWAP', 256));
foreach ($vars as &$value) {
$value = strval($value);
}
$vars = array_map(fn ($v) => strval($v), $vars);
$path = '/tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode';
if (!\file_exists('/tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode')) {
if (!\mkdir('/tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode', 0777, true)) {
if (!\file_exists($path)) {
if (@\mkdir($path, 0777, true)) {
\chmod($path, 0777);
} else {
throw new Exception('Can\'t create directory /tmp/project-' . $projectID . '/' . $build->getId() . '/builtCode');
}
};
}
// Launch build container
$id = $orchestration->run(
@ -755,7 +764,7 @@ function runBuildStage(string $buildId, string $projectID): Document
workdir: '/usr/code',
labels: [
'appwrite-type' => 'function',
'appwrite-created' => strval($buildTime),
'appwrite-created' => strval($time),
'appwrite-runtime' => $build->getAttribute('runtime', ''),
'appwrite-project' => $projectID,
'appwrite-build' => $build->getId(),
@ -843,7 +852,9 @@ function runBuildStage(string $buildId, string $projectID): Document
$path = $device->getPath(\uniqid() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
if (!\file_exists(\dirname($path))) { // Checks if directory path to file exists
if (!@\mkdir(\dirname($path), 0777, true)) {
if (@\mkdir(\dirname($path), 0777, true)) {
\chmod(\dirname($path), 0777);
} else {
throw new Exception('Can\'t create directory: ' . \dirname($path));
}
}
@ -867,7 +878,7 @@ function runBuildStage(string $buildId, string $projectID): Document
->setAttribute('status', 'ready')
->setAttribute('stdout', \utf8_encode(\mb_substr($buildStdout, -4096)))
->setAttribute('stderr', \utf8_encode(\mb_substr($buildStderr, -4096)))
->setAttribute('buildTime', $buildTime);
->setAttribute('time', $time);
// Update build with built code attribute
$build = $database->updateDocument('builds', $buildId, $build);
@ -887,19 +898,18 @@ function runBuildStage(string $buildId, string $projectID): Document
if (isset($id)) {
$orchestration->remove($id, true);
}
$orchestrationPool->put(null);
$register->get('dbPool')->put($db);
$register->get('redisPool')->put($redis);
throw new Exception('Build failed: ' . $e->getMessage());
} finally {
$orchestrationPool->put($orchestration);
$register->get('dbPool')->put($db);
$register->get('redisPool')->put($redis);
}
$orchestrationPool->put($orchestration);
$register->get('dbPool')->put($db);
$register->get('redisPool')->put($redis);
return $build;
}
@ -1086,8 +1096,8 @@ App::post('/v1/tag')
'sourceType' => Storage::DEVICE_LOCAL,
'stdout' => '',
'stderr' => '',
'buildTime' => 0,
'envVars' => [
'time' => 0,
'vars' => [
'ENTRYPOINT_NAME' => $tag->getAttribute('entrypoint'),
'APPWRITE_FUNCTION_ID' => $function->getId(),
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''),
@ -1108,45 +1118,48 @@ App::post('/v1/tag')
// Build Code
go(function () use ($projectID, $tagId, $buildId, $functionId, $function, $register) {
$db = $register->get('dbPool')->get();
$redis = $register->get('redisPool')->get();
$cache = new Cache(new RedisCache($redis));
try {
$db = $register->get('dbPool')->get();
$redis = $register->get('redisPool')->get();
$cache = new Cache(new RedisCache($redis));
$dbForProject = new Database(new MariaDB($db), $cache);
$dbForProject->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$dbForProject->setNamespace('_project_' . $projectID);
// Build Code
runBuildStage($buildId, $projectID);
$dbForProject = new Database(new MariaDB($db), $cache);
$dbForProject->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
$dbForProject->setNamespace('_project_' . $projectID);
// Build Code
runBuildStage($buildId, $projectID);
// Update the schedule
$schedule = $function->getAttribute('schedule', '');
$cron = (empty($function->getAttribute('tag')) && !empty($schedule)) ? new CronExpression($schedule) : null;
$next = (empty($function->getAttribute('tag')) && !empty($schedule)) ? $cron->getNextRunDate()->format('U') : 0;
// Update the schedule
$schedule = $function->getAttribute('schedule', '');
$cron = (empty($function->getAttribute('tag')) && !empty($schedule)) ? new CronExpression($schedule) : null;
$next = (empty($function->getAttribute('tag')) && !empty($schedule)) ? $cron->getNextRunDate()->format('U') : 0;
// Grab tag
$tag = $dbForProject->getDocument('tags', $tagId);
// Grab tag
$tag = $dbForProject->getDocument('tags', $tagId);
// Grab build
$build = $dbForProject->getDocument('builds', $buildId);
// Grab build
$build = $dbForProject->getDocument('builds', $buildId);
// If the build failed, it won't be possible to deploy
if ($build->getAttribute('status') !== 'ready') {
return;
// If the build failed, it won't be possible to deploy
if ($build->getAttribute('status') !== 'ready') {
return;
}
if ($tag->getAttribute('automaticDeploy') === true) {
// Update the function document setting the tag as the active one
$function
->setAttribute('tag', $tag->getId())
->setAttribute('scheduleNext', (int)$next);
$function = $dbForProject->updateDocument('functions', $function->getId(), $function);
}
// Deploy Runtime Server
createRuntimeServer($functionId, $projectID, $tagId, $dbForProject);
} catch (\Throwable $th) {
} finally {
$register->get('dbPool')->put($db);
$register->get('redisPool')->put($redis);
}
if ($tag->getAttribute('automaticDeploy') === true) {
// Update the function document setting the tag as the active one
$function
->setAttribute('tag', $tag->getId())
->setAttribute('scheduleNext', (int)$next);
$function = $dbForProject->updateDocument('functions', $function->getId(), $function);
}
// Deploy Runtime Server
createRuntimeServer($functionId, $projectID, $tagId, $dbForProject);
$register->get('dbPool')->put($db);
$register->get('redisPool')->put($redis);
});
if (false === $function) {
@ -1394,4 +1407,4 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
}
});
$http->start();
$http->start();

View file

@ -685,7 +685,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
<input type="file" name="code" id="tag-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="tag-automaticDeploy" class="margin-bottom-large">Auto Deploy Tag after build <input type="checkbox" class="margin-start-small" id="tag-automaticDeploy" name="automaticDeploy" /></label>
<label for="tag-deploy" class="margin-bottom-large">Auto Deploy Tag after build <input type="checkbox" class="margin-start-small" id="tag-deploy" name="deploy" /></label>
<footer>
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>

View file

@ -340,6 +340,52 @@ services:
- _APP_EXECUTOR_SECRET
- _APP_USAGE_STATS
appwrite-executor:
container_name: appwrite-executor
entrypoint: executor
stop_signal: SIGINT
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
networks:
appwrite:
runtimes:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- appwrite-functions:/storage/functions: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
- _APP_FUNCTIONS_RUNTIMES
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_EXECUTOR_SECRET
- _APP_USAGE_STATS
- _APP_STATSD_HOST
- _APP_STATSD_PORT
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- DOCKERHUB_PULL_USERNAME
- DOCKERHUB_PULL_PASSWORD
appwrite-worker-mails:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-mails
@ -475,6 +521,7 @@ services:
networks:
gateway:
appwrite:
runtimes:
volumes:
appwrite-mariadb:
@ -485,3 +532,4 @@ volumes:
appwrite-functions:
appwrite-influxdb:
appwrite-config:
appwrite-executor:

3
bin/executor Normal file
View file

@ -0,0 +1,3 @@
#!/bin/sh
php -e /usr/src/code/app/executor.php -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php

View file

@ -350,11 +350,7 @@ services:
appwrite-executor:
container_name: appwrite-executor
entrypoint:
- php
- -e
- /usr/src/code/app/executor.php
- -dopcache.preload=opcache.preload=/usr/src/code/app/preload.php
entrypoint: executor
stop_signal: SIGINT
build:
context: .

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,29 +1,29 @@
(function (exports, isomorphicFormData, crossFetch) {
'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
class AppwriteException extends Error {
@ -38,7 +38,7 @@
class Appwrite {
constructor() {
this.config = {
endpoint: 'https://appwrite.io/v1',
endpoint: 'https://HOSTNAME/v1',
endpointRealtime: '',
project: '',
key: '',
@ -48,7 +48,7 @@
};
this.headers = {
'x-sdk-version': 'appwrite:web:4.0.4',
'X-Appwrite-Response-Format': '0.11.0',
'X-Appwrite-Response-Format': '0.12.0',
};
this.realtime = {
socket: undefined,
@ -264,12 +264,14 @@
* Update Account Email
*
* Update currently logged in user account email address. After changing user
* address, user confirmation status is being reset and a new confirmation
* mail is sent. For security measures, user password is required to complete
* this request.
* address, the user confirmation status will get reset. A new confirmation
* email is not sent automatically however you can use the send confirmation
* email endpoint again to send the confirmation email. For security measures,
* user password is required to complete this request.
* This endpoint can also be used to convert an anonymous account to a normal
* one, by passing an email address and a new password.
*
*
* @param {string} email
* @param {string} password
* @throws {AppwriteException}
@ -411,8 +413,9 @@
/**
* Update Account Preferences
*
* Update currently logged in user account preferences. You can pass only the
* specific settings you wish to update.
* Update currently logged in user account preferences. The object you pass is
* stored as is, and replaces any previous value. The maximum allowed prefs
* size is 64kB and throws error if exceeded.
*
* @param {object} prefs
* @throws {AppwriteException}
@ -766,7 +769,8 @@
*
* Use this endpoint to log out the currently logged in user from all their
* account sessions across all of their different devices. When using the
* option id argument, only the session unique ID provider will be deleted.
* Session ID argument, only the unique session ID provided is deleted.
*
*
* @param {string} sessionId
* @throws {AppwriteException}
@ -1115,7 +1119,7 @@
};
this.functions = {
/**
* Get Builds
* List Builds
*
* Get a list of all the current user build logs. You can use the query params
* to filter your results. On admin mode, this endpoint will return a list of
@ -1294,6 +1298,22 @@
'content-type': 'application/json',
}, payload);
}),
/**
* List the currently active function runtimes.
*
* Get a list of all runtimes that are currently active in your project.
*
* @throws {AppwriteException}
* @returns {Promise}
*/
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 Function
*
@ -1573,11 +1593,11 @@
* @param {string} functionId
* @param {string} entrypoint
* @param {File} code
* @param {boolean} automaticDeploy
* @param {boolean} deploy
* @throws {AppwriteException}
* @returns {Promise}
*/
createTag: (functionId, entrypoint, code, automaticDeploy) => __awaiter(this, void 0, void 0, function* () {
createTag: (functionId, entrypoint, code, deploy) => __awaiter(this, void 0, void 0, function* () {
if (typeof functionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "functionId"');
}
@ -1587,8 +1607,8 @@
if (typeof code === 'undefined') {
throw new AppwriteException('Missing required parameter: "code"');
}
if (typeof automaticDeploy === 'undefined') {
throw new AppwriteException('Missing required parameter: "automaticDeploy"');
if (typeof deploy === 'undefined') {
throw new AppwriteException('Missing required parameter: "deploy"');
}
let path = '/functions/{functionId}/tags'.replace('{functionId}', functionId);
let payload = {};
@ -1598,8 +1618,8 @@
if (typeof code !== 'undefined') {
payload['code'] = code;
}
if (typeof automaticDeploy !== 'undefined') {
payload['automaticDeploy'] = automaticDeploy;
if (typeof deploy !== 'undefined') {
payload['deploy'] = deploy;
}
const uri = new URL(this.config.endpoint + path);
return yield this.call('post', uri, {
@ -1804,10 +1824,11 @@
* @param {string} permission
* @param {string[]} read
* @param {string[]} write
* @param {boolean} enabled
* @throws {AppwriteException}
* @returns {Promise}
*/
updateCollection: (collectionId, name, permission, read, write) => __awaiter(this, void 0, void 0, function* () {
updateCollection: (collectionId, name, permission, read, write, enabled) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
@ -1831,6 +1852,9 @@
if (typeof write !== 'undefined') {
payload['write'] = write;
}
if (typeof enabled !== 'undefined') {
payload['enabled'] = enabled;
}
const uri = new URL(this.config.endpoint + path);
return yield this.call('put', uri, {
'content-type': 'application/json',
@ -1883,27 +1907,27 @@
*
*
* @param {string} collectionId
* @param {string} attributeId
* @param {string} key
* @param {boolean} required
* @param {boolean} xdefault
* @param {boolean} array
* @throws {AppwriteException}
* @returns {Promise}
*/
createBooleanAttribute: (collectionId, attributeId, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
createBooleanAttribute: (collectionId, key, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
if (typeof attributeId === 'undefined') {
throw new AppwriteException('Missing required parameter: "attributeId"');
if (typeof key === 'undefined') {
throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof required === 'undefined') {
throw new AppwriteException('Missing required parameter: "required"');
}
let path = '/database/collections/{collectionId}/attributes/boolean'.replace('{collectionId}', collectionId);
let payload = {};
if (typeof attributeId !== 'undefined') {
payload['attributeId'] = attributeId;
if (typeof key !== 'undefined') {
payload['key'] = key;
}
if (typeof required !== 'undefined') {
payload['required'] = required;
@ -1926,27 +1950,27 @@
*
*
* @param {string} collectionId
* @param {string} attributeId
* @param {string} key
* @param {boolean} required
* @param {string} xdefault
* @param {boolean} array
* @throws {AppwriteException}
* @returns {Promise}
*/
createEmailAttribute: (collectionId, attributeId, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
createEmailAttribute: (collectionId, key, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
if (typeof attributeId === 'undefined') {
throw new AppwriteException('Missing required parameter: "attributeId"');
if (typeof key === 'undefined') {
throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof required === 'undefined') {
throw new AppwriteException('Missing required parameter: "required"');
}
let path = '/database/collections/{collectionId}/attributes/email'.replace('{collectionId}', collectionId);
let payload = {};
if (typeof attributeId !== 'undefined') {
payload['attributeId'] = attributeId;
if (typeof key !== 'undefined') {
payload['key'] = key;
}
if (typeof required !== 'undefined') {
payload['required'] = required;
@ -1967,7 +1991,7 @@
*
*
* @param {string} collectionId
* @param {string} attributeId
* @param {string} key
* @param {string[]} elements
* @param {boolean} required
* @param {string} xdefault
@ -1975,12 +1999,12 @@
* @throws {AppwriteException}
* @returns {Promise}
*/
createEnumAttribute: (collectionId, attributeId, elements, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
createEnumAttribute: (collectionId, key, elements, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
if (typeof attributeId === 'undefined') {
throw new AppwriteException('Missing required parameter: "attributeId"');
if (typeof key === 'undefined') {
throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof elements === 'undefined') {
throw new AppwriteException('Missing required parameter: "elements"');
@ -1990,8 +2014,8 @@
}
let path = '/database/collections/{collectionId}/attributes/enum'.replace('{collectionId}', collectionId);
let payload = {};
if (typeof attributeId !== 'undefined') {
payload['attributeId'] = attributeId;
if (typeof key !== 'undefined') {
payload['key'] = key;
}
if (typeof elements !== 'undefined') {
payload['elements'] = elements;
@ -2018,7 +2042,7 @@
*
*
* @param {string} collectionId
* @param {string} attributeId
* @param {string} key
* @param {boolean} required
* @param {string} min
* @param {string} max
@ -2027,20 +2051,20 @@
* @throws {AppwriteException}
* @returns {Promise}
*/
createFloatAttribute: (collectionId, attributeId, required, min, max, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
createFloatAttribute: (collectionId, key, required, min, max, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
if (typeof attributeId === 'undefined') {
throw new AppwriteException('Missing required parameter: "attributeId"');
if (typeof key === 'undefined') {
throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof required === 'undefined') {
throw new AppwriteException('Missing required parameter: "required"');
}
let path = '/database/collections/{collectionId}/attributes/float'.replace('{collectionId}', collectionId);
let payload = {};
if (typeof attributeId !== 'undefined') {
payload['attributeId'] = attributeId;
if (typeof key !== 'undefined') {
payload['key'] = key;
}
if (typeof required !== 'undefined') {
payload['required'] = required;
@ -2070,7 +2094,7 @@
*
*
* @param {string} collectionId
* @param {string} attributeId
* @param {string} key
* @param {boolean} required
* @param {number} min
* @param {number} max
@ -2079,20 +2103,20 @@
* @throws {AppwriteException}
* @returns {Promise}
*/
createIntegerAttribute: (collectionId, attributeId, required, min, max, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
createIntegerAttribute: (collectionId, key, required, min, max, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
if (typeof attributeId === 'undefined') {
throw new AppwriteException('Missing required parameter: "attributeId"');
if (typeof key === 'undefined') {
throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof required === 'undefined') {
throw new AppwriteException('Missing required parameter: "required"');
}
let path = '/database/collections/{collectionId}/attributes/integer'.replace('{collectionId}', collectionId);
let payload = {};
if (typeof attributeId !== 'undefined') {
payload['attributeId'] = attributeId;
if (typeof key !== 'undefined') {
payload['key'] = key;
}
if (typeof required !== 'undefined') {
payload['required'] = required;
@ -2121,27 +2145,27 @@
*
*
* @param {string} collectionId
* @param {string} attributeId
* @param {string} key
* @param {boolean} required
* @param {string} xdefault
* @param {boolean} array
* @throws {AppwriteException}
* @returns {Promise}
*/
createIpAttribute: (collectionId, attributeId, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
createIpAttribute: (collectionId, key, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
if (typeof attributeId === 'undefined') {
throw new AppwriteException('Missing required parameter: "attributeId"');
if (typeof key === 'undefined') {
throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof required === 'undefined') {
throw new AppwriteException('Missing required parameter: "required"');
}
let path = '/database/collections/{collectionId}/attributes/ip'.replace('{collectionId}', collectionId);
let payload = {};
if (typeof attributeId !== 'undefined') {
payload['attributeId'] = attributeId;
if (typeof key !== 'undefined') {
payload['key'] = key;
}
if (typeof required !== 'undefined') {
payload['required'] = required;
@ -2160,11 +2184,11 @@
/**
* Create String Attribute
*
* Create a new string attribute.
* Create a string attribute.
*
*
* @param {string} collectionId
* @param {string} attributeId
* @param {string} key
* @param {number} size
* @param {boolean} required
* @param {string} xdefault
@ -2172,12 +2196,12 @@
* @throws {AppwriteException}
* @returns {Promise}
*/
createStringAttribute: (collectionId, attributeId, size, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
createStringAttribute: (collectionId, key, size, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
if (typeof attributeId === 'undefined') {
throw new AppwriteException('Missing required parameter: "attributeId"');
if (typeof key === 'undefined') {
throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof size === 'undefined') {
throw new AppwriteException('Missing required parameter: "size"');
@ -2187,8 +2211,8 @@
}
let path = '/database/collections/{collectionId}/attributes/string'.replace('{collectionId}', collectionId);
let payload = {};
if (typeof attributeId !== 'undefined') {
payload['attributeId'] = attributeId;
if (typeof key !== 'undefined') {
payload['key'] = key;
}
if (typeof size !== 'undefined') {
payload['size'] = size;
@ -2214,27 +2238,27 @@
*
*
* @param {string} collectionId
* @param {string} attributeId
* @param {string} key
* @param {boolean} required
* @param {string} xdefault
* @param {boolean} array
* @throws {AppwriteException}
* @returns {Promise}
*/
createUrlAttribute: (collectionId, attributeId, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
createUrlAttribute: (collectionId, key, required, xdefault, array) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
if (typeof attributeId === 'undefined') {
throw new AppwriteException('Missing required parameter: "attributeId"');
if (typeof key === 'undefined') {
throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof required === 'undefined') {
throw new AppwriteException('Missing required parameter: "required"');
}
let path = '/database/collections/{collectionId}/attributes/url'.replace('{collectionId}', collectionId);
let payload = {};
if (typeof attributeId !== 'undefined') {
payload['attributeId'] = attributeId;
if (typeof key !== 'undefined') {
payload['key'] = key;
}
if (typeof required !== 'undefined') {
payload['required'] = required;
@ -2255,18 +2279,18 @@
*
*
* @param {string} collectionId
* @param {string} attributeId
* @param {string} key
* @throws {AppwriteException}
* @returns {Promise}
*/
getAttribute: (collectionId, attributeId) => __awaiter(this, void 0, void 0, function* () {
getAttribute: (collectionId, key) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
if (typeof attributeId === 'undefined') {
throw new AppwriteException('Missing required parameter: "attributeId"');
if (typeof key === 'undefined') {
throw new AppwriteException('Missing required parameter: "key"');
}
let path = '/database/collections/{collectionId}/attributes/{attributeId}'.replace('{collectionId}', collectionId).replace('{attributeId}', attributeId);
let path = '/database/collections/{collectionId}/attributes/{key}'.replace('{collectionId}', collectionId).replace('{key}', key);
let payload = {};
const uri = new URL(this.config.endpoint + path);
return yield this.call('get', uri, {
@ -2278,18 +2302,18 @@
*
*
* @param {string} collectionId
* @param {string} attributeId
* @param {string} key
* @throws {AppwriteException}
* @returns {Promise}
*/
deleteAttribute: (collectionId, attributeId) => __awaiter(this, void 0, void 0, function* () {
deleteAttribute: (collectionId, key) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
if (typeof attributeId === 'undefined') {
throw new AppwriteException('Missing required parameter: "attributeId"');
if (typeof key === 'undefined') {
throw new AppwriteException('Missing required parameter: "key"');
}
let path = '/database/collections/{collectionId}/attributes/{attributeId}'.replace('{collectionId}', collectionId).replace('{attributeId}', attributeId);
let path = '/database/collections/{collectionId}/attributes/{key}'.replace('{collectionId}', collectionId).replace('{key}', key);
let payload = {};
const uri = new URL(this.config.endpoint + path);
return yield this.call('delete', uri, {
@ -2539,19 +2563,19 @@
*
*
* @param {string} collectionId
* @param {string} indexId
* @param {string} key
* @param {string} type
* @param {string[]} attributes
* @param {string[]} orders
* @throws {AppwriteException}
* @returns {Promise}
*/
createIndex: (collectionId, indexId, type, attributes, orders) => __awaiter(this, void 0, void 0, function* () {
createIndex: (collectionId, key, type, attributes, orders) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
if (typeof indexId === 'undefined') {
throw new AppwriteException('Missing required parameter: "indexId"');
if (typeof key === 'undefined') {
throw new AppwriteException('Missing required parameter: "key"');
}
if (typeof type === 'undefined') {
throw new AppwriteException('Missing required parameter: "type"');
@ -2561,8 +2585,8 @@
}
let path = '/database/collections/{collectionId}/indexes'.replace('{collectionId}', collectionId);
let payload = {};
if (typeof indexId !== 'undefined') {
payload['indexId'] = indexId;
if (typeof key !== 'undefined') {
payload['key'] = key;
}
if (typeof type !== 'undefined') {
payload['type'] = type;
@ -2583,18 +2607,18 @@
*
*
* @param {string} collectionId
* @param {string} indexId
* @param {string} key
* @throws {AppwriteException}
* @returns {Promise}
*/
getIndex: (collectionId, indexId) => __awaiter(this, void 0, void 0, function* () {
getIndex: (collectionId, key) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
if (typeof indexId === 'undefined') {
throw new AppwriteException('Missing required parameter: "indexId"');
if (typeof key === 'undefined') {
throw new AppwriteException('Missing required parameter: "key"');
}
let path = '/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}', collectionId).replace('{indexId}', indexId);
let path = '/database/collections/{collectionId}/indexes/{key}'.replace('{collectionId}', collectionId).replace('{key}', key);
let payload = {};
const uri = new URL(this.config.endpoint + path);
return yield this.call('get', uri, {
@ -2606,18 +2630,18 @@
*
*
* @param {string} collectionId
* @param {string} indexId
* @param {string} key
* @throws {AppwriteException}
* @returns {Promise}
*/
deleteIndex: (collectionId, indexId) => __awaiter(this, void 0, void 0, function* () {
deleteIndex: (collectionId, key) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
if (typeof indexId === 'undefined') {
throw new AppwriteException('Missing required parameter: "indexId"');
if (typeof key === 'undefined') {
throw new AppwriteException('Missing required parameter: "key"');
}
let path = '/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}', collectionId).replace('{indexId}', indexId);
let path = '/database/collections/{collectionId}/indexes/{key}'.replace('{collectionId}', collectionId).replace('{key}', key);
let payload = {};
const uri = new URL(this.config.endpoint + path);
return yield this.call('delete', uri, {
@ -2713,14 +2737,14 @@
}, payload);
}),
/**
* Get Anti virus
* Get Antivirus
*
* Check the Appwrite Anti Virus server is up and connection is successful.
* Check the Appwrite Antivirus server is up and connection is successful.
*
* @throws {AppwriteException}
* @returns {Promise}
*/
getAntiVirus: () => __awaiter(this, void 0, void 0, function* () {
getAntivirus: () => __awaiter(this, void 0, void 0, function* () {
let path = '/health/anti-virus';
let payload = {};
const uri = new URL(this.config.endpoint + path);
@ -4280,10 +4304,11 @@
/**
* List Teams
*
* Get a list of all the current user teams. You can use the query params to
* filter your results. On admin mode, this endpoint will return a list of all
* of the project's teams. [Learn more about different API
* modes](/docs/admin).
* Get a list of all the teams in which the current user is a member. You can
* use the parameters to filter your results.
*
* In admin mode, this endpoint returns a list of all the teams in the current
* project. [Learn more about different API modes](/docs/admin).
*
* @param {string} search
* @param {number} limit
@ -4324,9 +4349,8 @@
* Create Team
*
* Create a new team. The user who creates the team will automatically be
* assigned as the owner of the team. The team owner can invite new members,
* who will be able add new owners and update or delete the team from your
* project.
* assigned as the owner of the team. Only the users with the owner role can
* invite new members, add new owners and delete or update the team.
*
* @param {string} teamId
* @param {string} name
@ -4360,8 +4384,7 @@
/**
* Get Team
*
* Get a team by its unique ID. All team members have read access for this
* resource.
* Get a team by its ID. All team members have read access for this resource.
*
* @param {string} teamId
* @throws {AppwriteException}
@ -4381,8 +4404,8 @@
/**
* Update Team
*
* Update a team by its unique ID. Only team owners have write access for this
* resource.
* Update a team using its ID. Only members with the owner role can update the
* team.
*
* @param {string} teamId
* @param {string} name
@ -4409,8 +4432,8 @@
/**
* Delete Team
*
* Delete a team by its unique ID. Only team owners have write access for this
* resource.
* Delete a team using its ID. Only team members with the owner role can
* delete the team.
*
* @param {string} teamId
* @throws {AppwriteException}
@ -4430,8 +4453,8 @@
/**
* Get Team Memberships
*
* Get a team members by the team unique ID. All team members have read access
* for this list of resources.
* Use this endpoint to list a team's members using the team's ID. All team
* members have read access to this endpoint.
*
* @param {string} teamId
* @param {string} search
@ -4475,22 +4498,21 @@
/**
* Create Team Membership
*
* Use this endpoint to invite a new member to join your team. If initiated
* from Client SDK, an email with a link to join the team will be sent to the
* new member's email address if the member doesn't exist in the project it
* will be created automatically. If initiated from server side SDKs, new
* member will automatically be added to the team.
* Invite a new member to join your team. If initiated from the client SDK, an
* email with a link to join the team will be sent to the member's email
* address and an account will be created for them should they not be signed
* up already. If initiated from server-side SDKs, the new member will
* automatically be added to the team.
*
* Use the 'URL' parameter to redirect the user from the invitation email back
* Use the 'url' parameter to redirect the user from the invitation email back
* to your app. When the user is redirected, use the [Update Team Membership
* Status](/docs/client/teams#teamsUpdateMembershipStatus) endpoint to allow
* the user to accept the invitation to the team. While calling from side
* SDKs the redirect url can be empty string.
* the user to accept the invitation to the team.
*
* Please note that in order to avoid a [Redirect
* Attacks](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
* Please note that to avoid a [Redirect
* Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
* the only valid redirect URL's are the once from domains you have set when
* added your platforms in the console interface.
* adding your platforms in the console interface.
*
* @param {string} teamId
* @param {string} email
@ -4560,6 +4582,9 @@
/**
* Update Membership Roles
*
* Modify the roles of a team member. Only team members with the owner role
* have access to this endpoint. Learn more about [roles and
* permissions](/docs/permissions).
*
* @param {string} teamId
* @param {string} membershipId
@ -4617,9 +4642,13 @@
* Update Team Membership Status
*
* Use this endpoint to allow a user to accept an invitation to join a team
* after being redirected back to your app from the invitation email recieved
* after being redirected back to your app from the invitation email received
* by the user.
*
* If the request is successful, a session for the user is automatically
* created.
*
*
* @param {string} teamId
* @param {string} membershipId
* @param {string} userId
@ -4932,8 +4961,9 @@
/**
* Update User Preferences
*
* Update the user preferences by its unique ID. You can pass only the
* specific settings you wish to update.
* Update the user preferences by its unique ID. The object you pass is stored
* as is, and replaces any previous value. The maximum allowed prefs size is
* 64kB and throws error if exceeded.
*
* @param {string} userId
* @param {object} prefs
@ -5292,8 +5322,26 @@
return output;
}
}
class Query {
}
Query.equal = (attribute, value) => Query.addQuery(attribute, "equal", value);
Query.notEqual = (attribute, value) => Query.addQuery(attribute, "notEqual", value);
Query.lesser = (attribute, value) => Query.addQuery(attribute, "lesser", value);
Query.lesserEqual = (attribute, value) => Query.addQuery(attribute, "lesserEqual", value);
Query.greater = (attribute, value) => Query.addQuery(attribute, "greater", value);
Query.greaterEqual = (attribute, value) => Query.addQuery(attribute, "greaterEqual", value);
Query.search = (attribute, value) => Query.addQuery(attribute, "search", value);
Query.addQuery = (attribute, oper, value) => value instanceof Array
? `${attribute}.${oper}(${value
.map((v) => Query.parseValues(v))
.join(",")})`
: `${attribute}.${oper}(${Query.parseValues(value)})`;
Query.parseValues = (value) => typeof value === "string" || value instanceof String
? `"${value}"`
: `${value}`;
exports.Appwrite = Appwrite;
exports.Query = Query;
Object.defineProperty(exports, '__esModule', { value: true });

View file

@ -45,7 +45,7 @@ class Build extends Model
'default' => '',
'example' => '',
])
->addRule('buildTime', [
->addRule('time', [
'type' => self::TYPE_INTEGER,
'description' => 'The build time in seconds.',
'default' => 0,

View file

@ -69,7 +69,7 @@ class Tag extends Model
'default' => '',
'example' => '',
])
->addRule('automaticDeploy', [
->addRule('deploy', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether the tag should be automatically deployed.',
'default' => false,

View file

@ -331,6 +331,38 @@ class FunctionsCustomServerTest extends Scope
return $data;
}
/**
* @depends testCreateTag
*/
public function testListBuild(array $data):array
{
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_GET, '/builds', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(1, $response['body']['sum']);
$this->assertEquals($response['body']['builds'][0]['status'], 'ready');
/**
* Check Queries work
*/
$responseQuery = $this->client->call(Client::METHOD_GET, '/builds?status=status.equal(\"ready\")', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $responseQuery['headers']['status-code']);
$this->assertEquals(1, $responseQuery['body']['sum']);
$this->assertEquals($responseQuery['body']['builds'][0]['status'], 'ready');
return $data;
}
/**
* @depends testCreateTag
*/

View file

@ -84,7 +84,6 @@ services:
- _APP_STORAGE_ANTIVIRUS=disabled
- _APP_STORAGE_LIMIT
- _APP_FUNCTIONS_TIMEOUT
- _APP_FUNCTIONS_BUILD_TIMEOUT
- _APP_FUNCTIONS_CONTAINERS
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
@ -244,7 +243,6 @@ services:
- _APP_DB_USER
- _APP_DB_PASS
- _APP_FUNCTIONS_TIMEOUT
- _APP_FUNCTIONS_BUILD_TIMEOUT
- _APP_FUNCTIONS_CONTAINERS
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY