mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
36
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26bb82411a | ||
|
|
afc0ae98c6 | ||
|
|
10ab558b65 | ||
|
|
1311b8d02b | ||
|
|
8a749c7b08 | ||
|
|
31df0eda7c | ||
|
|
cde761fb0a | ||
|
|
fb3c3f59b5 | ||
|
|
5711558617 | ||
|
|
95e1daf2a9 | ||
|
|
75828cbc21 | ||
|
|
8d5a53a0b1 | ||
|
|
62b24de063 | ||
|
|
b7e2c6c44e | ||
|
|
4c18be49a6 | ||
|
|
feedc16272 | ||
|
|
430d27a517 | ||
|
|
71ea737ee7 | ||
|
|
f7dd81e279 | ||
|
|
8a6a556462 | ||
|
|
f33478ea14 | ||
|
|
362d3a140a | ||
|
|
e910cfac7c | ||
|
|
1a32f16962 | ||
|
|
1efdd29bb6 | ||
|
|
3e5e20fa90 | ||
|
|
b86adcb28f | ||
|
|
3146d96251 | ||
|
|
5f08c832e1 | ||
|
|
14062bcee9 | ||
|
|
52fa172554 | ||
|
|
32b6823a29 | ||
|
|
b4670bfb2b | ||
|
|
60469946d9 | ||
|
|
cb94d28fb9 | ||
|
|
b1e5dfec7a |
@@ -86,4 +86,7 @@ _APP_GRAPHQL_MAX_COMPLEXITY=250
|
||||
_APP_GRAPHQL_MAX_DEPTH=3
|
||||
DOCKERHUB_PULL_USERNAME=
|
||||
DOCKERHUB_PULL_PASSWORD=
|
||||
DOCKERHUB_PULL_EMAIL=
|
||||
DOCKERHUB_PULL_EMAIL=
|
||||
VCS_GITHUB_APP_NAME=
|
||||
VCS_GITHUB_PRIVATE_KEY=
|
||||
VCS_GITHUB_APP_ID=
|
||||
@@ -154,6 +154,8 @@ RUN mkdir -p /storage/uploads && \
|
||||
chown -Rf www-data.www-data /storage/functions && chmod -Rf 0755 /storage/functions && \
|
||||
chown -Rf www-data.www-data /storage/debug && chmod -Rf 0755 /storage/debug
|
||||
|
||||
RUN apk add git
|
||||
|
||||
# Executables
|
||||
RUN chmod +x /usr/local/bin/doctor && \
|
||||
chmod +x /usr/local/bin/maintenance && \
|
||||
|
||||
@@ -495,6 +495,180 @@ $collections = [
|
||||
],
|
||||
],
|
||||
|
||||
'vcs_installations' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('vcs_installations'),
|
||||
'name' => 'vcs_installations',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => ID::custom('projectId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('projectInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('installationId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('organization'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('provider'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('accessToken'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['encrypt']
|
||||
]
|
||||
],
|
||||
'indexes' => [
|
||||
],
|
||||
],
|
||||
|
||||
'vcs_repos' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('vcs_repos'),
|
||||
'name' => 'vcs_repos',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => ID::custom('vcsInstallationId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => []
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('vcsInstallationInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('projectId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => []
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('projectInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('repositoryId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 128,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => []
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('repositoryOwner'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 128,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => []
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('resourceId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('resourceType'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 128,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => []
|
||||
]
|
||||
],
|
||||
'indexes' => [
|
||||
],
|
||||
],
|
||||
|
||||
'projects' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('projects'),
|
||||
@@ -2132,6 +2306,48 @@ $collections = [
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('vcsInstallationId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => 2048,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('vcsInstallationInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('vcsRepoId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => 2048,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('vcsRepoInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('runtime'),
|
||||
'type' => Database::VAR_STRING,
|
||||
@@ -2265,6 +2481,20 @@ $collections = [
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_vcsInstallationId'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['vcsInstallationId'],
|
||||
'lengths' => [768],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_vcsRepoId'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['vcsRepoId'],
|
||||
'lengths' => [768],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_runtime'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
@@ -2370,6 +2600,77 @@ $collections = [
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('type'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('vcsInstallationId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('vcsInstallationInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('vcsRepoId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('vcsRepoInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('branch'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('vcsCommentId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'signed' => true,
|
||||
'size' => 2048,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('size'),
|
||||
'type' => Database::VAR_INTEGER,
|
||||
|
||||
@@ -326,6 +326,13 @@ return [
|
||||
'code' => 404,
|
||||
],
|
||||
|
||||
/** VCS */
|
||||
Exception::INSTALLATION_NOT_FOUND => [
|
||||
'name' => Exception::INSTALLATION_NOT_FOUND,
|
||||
'description' => 'Installation with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
|
||||
/** Builds */
|
||||
Exception::BUILD_NOT_FOUND => [
|
||||
'name' => Exception::BUILD_NOT_FOUND,
|
||||
|
||||
@@ -186,6 +186,19 @@ return [
|
||||
'optional' => true,
|
||||
'icon' => '/images/services/graphql.png',
|
||||
],
|
||||
'vcs' => [
|
||||
'key' => 'vcs',
|
||||
'name' => 'VCS',
|
||||
'subtitle' => 'The VCS service allows you to interact with providers like GitHub, GitLab etc.',
|
||||
'description' => '',
|
||||
'controller' => 'api/vcs.php',
|
||||
'sdk' => false,
|
||||
'docs' => false,
|
||||
'docsUrl' => '',
|
||||
'tests' => false,
|
||||
'optional' => true,
|
||||
'icon' => '',
|
||||
],
|
||||
'console' => [
|
||||
'key' => 'console',
|
||||
'name' => 'Console',
|
||||
|
||||
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
+1
-1
Submodule app/console updated: ac4181aea4...5e2a40c1e3
@@ -74,6 +74,8 @@ App::post('/v1/functions')
|
||||
->inject('events')
|
||||
->action(function (string $functionId, string $name, array $execute, string $runtime, array $events, string $schedule, int $timeout, bool $enabled, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance) {
|
||||
|
||||
// TODO: Add support to link to GitHub repos from createFunction as well?
|
||||
|
||||
$cron = !empty($schedule) ? new CronExpression($schedule) : null;
|
||||
$next = !empty($schedule) ? DateTime::format($cron->getNextRunDate()) : null;
|
||||
|
||||
@@ -443,12 +445,16 @@ App::put('/v1/functions/:functionId')
|
||||
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
|
||||
->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Maximum execution time in seconds.', true)
|
||||
->param('enabled', true, new Boolean(), 'Is function enabled?', true)
|
||||
->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for vcs deployment.', true)
|
||||
->param('repositoryId', '', new Text(128, 0), 'Repository ID of the repo linked to the function', true)
|
||||
->param('repositoryOwner', '', new Text(128, 0), 'Repository Owner of the repo linked to the function', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForConsole')
|
||||
->inject('project')
|
||||
->inject('user')
|
||||
->inject('events')
|
||||
->action(function (string $functionId, string $name, array $execute, array $events, string $schedule, int $timeout, bool $enabled, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance) {
|
||||
->action(function (string $functionId, string $name, array $execute, array $events, string $schedule, int $timeout, bool $enabled, string $vcsInstallationId, string $repositoryId, string $repositoryOwner, Response $response, Database $dbForProject, Database $dbForConsole, Document $project, Document $user, Event $eventsInstance) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
@@ -456,11 +462,95 @@ App::put('/v1/functions/:functionId')
|
||||
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$installation = $dbForConsole->getDocument('vcs_installations', $vcsInstallationId, [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if (!empty($vcsInstallationId) && $installation->isEmpty()) {
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if(!empty($vcsInstallationId) && empty($repositoryId)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID); // TODO: More specific error
|
||||
}
|
||||
|
||||
if(!empty($repositoryId) && empty($vcsInstallationId)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID); // TODO: More specific error
|
||||
}
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$cron = !empty($schedule) ? new CronExpression($schedule) : null;
|
||||
$next = !empty($schedule) ? DateTime::format($cron->getNextRunDate()) : null;
|
||||
|
||||
$enabled ??= $function->getAttribute('enabled', true);
|
||||
|
||||
$vcsRepoId = null;
|
||||
$needToDeploy = false;
|
||||
//if repo id was previously empty and non empty now, we need to create a new deployment for this function
|
||||
$prevVcsRepoId = $function->getAttribute('vcsRepoId', '');
|
||||
if (empty($prevVcsRepoId) && !empty($repositoryId)) {
|
||||
$needToDeploy = true;
|
||||
}
|
||||
|
||||
// activate the deployment for first run of a VCS repo
|
||||
if ($needToDeploy) {
|
||||
$deploymentId = ID::unique();
|
||||
$entrypoint = 'index.js'; //TODO: Read from function settings
|
||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
|
||||
//Add document in VCS repos collection
|
||||
$vcs_repos = new Document([
|
||||
'$id' => ID::unique(),
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'vcsInstallationId' => $installation->getId(),
|
||||
'vcsInstallationInternalId' => $installation->getInternalId(),
|
||||
'projectId' => $project->getId(),
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'repositoryId' => $repositoryId,
|
||||
'repositoryOwner' => $repositoryOwner,
|
||||
'resourceId' => $functionId,
|
||||
'resourceType' => "function"
|
||||
]);
|
||||
|
||||
$vcs_repos = $dbForConsole->createDocument('vcs_repos', $vcs_repos);
|
||||
$vcsRepoId = $vcs_repos->getId();
|
||||
$vcsRepoInternalId = $vcs_repos->getInternalId();
|
||||
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
'$id' => $deploymentId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'resourceId' => $function->getId(),
|
||||
'resourceType' => 'functions',
|
||||
'entrypoint' => $entrypoint,
|
||||
'type' => "vcs",
|
||||
'vcsInstallationId' => $installation->getId(),
|
||||
'vcsInstallationInternalId' => $installation->getInternalId(),
|
||||
'vcsRepoId' => $vcsRepoId,
|
||||
'vcsRepoInternalId' => $vcsRepoInternalId,
|
||||
'branch' => "main",
|
||||
'search' => implode(' ', [$deploymentId, $entrypoint]),
|
||||
'activate' => true,
|
||||
]));
|
||||
}
|
||||
|
||||
// Disconnect repo
|
||||
if(!empty($prevVcsRepoId) && empty($repositoryId)) {
|
||||
$dbForConsole->deleteDocument('vcs_repos', $prevVcsRepoId);
|
||||
$vcsRepoId = '';
|
||||
$vcsRepoInternalId = '';
|
||||
}
|
||||
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
|
||||
'execute' => $execute,
|
||||
'name' => $name,
|
||||
@@ -470,6 +560,10 @@ App::put('/v1/functions/:functionId')
|
||||
'scheduleNext' => $next,
|
||||
'timeout' => $timeout,
|
||||
'enabled' => $enabled,
|
||||
'vcsInstallationId' => $installation->getId(),
|
||||
'vcsInstallationInternalId' => $installation->getInternalId(),
|
||||
'vcsRepoId' => $vcsRepoId,
|
||||
'vcsRepoInternalId' => $vcsRepoInternalId,
|
||||
'search' => implode(' ', [$functionId, $name, $function->getAttribute('runtime')]),
|
||||
])));
|
||||
|
||||
@@ -486,6 +580,18 @@ App::put('/v1/functions/:functionId')
|
||||
|
||||
$eventsInstance->setParam('functionId', $function->getId());
|
||||
|
||||
if ($needToDeploy) {
|
||||
$buildEvent = new Build();
|
||||
$buildEvent
|
||||
->setType(BUILD_TYPE_DEPLOYMENT)
|
||||
->setResource($function)
|
||||
->setDeployment($deployment)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
|
||||
//TODO: Add event?
|
||||
}
|
||||
|
||||
$response->dynamic($function, Response::MODEL_FUNCTION);
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,481 @@
|
||||
<?php
|
||||
|
||||
use Utopia\App;
|
||||
use Appwrite\Event\Build;
|
||||
use Appwrite\Event\Delete;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Installations;
|
||||
use Utopia\Cache\Adapter\Redis;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Adapter\MariaDB;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
|
||||
App::get('/v1/vcs/github/installations')
|
||||
->desc('Install GitHub App')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'public')
|
||||
->label('origin', '*')
|
||||
->label('sdk.auth', [])
|
||||
->label('sdk.namespace', 'vcs')
|
||||
->label('sdk.method', 'createGitHubInstallation')
|
||||
->label('sdk.description', '')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_MOVED_PERMANENTLY)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_HTML)
|
||||
->label('sdk.methodType', 'webAuth')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->action(function (Response $response, Document $project) {
|
||||
$projectId = $project->getId();
|
||||
|
||||
$appName = App::getEnv('VCS_GITHUB_APP_NAME');
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->redirect("https://github.com/apps/$appName/installations/new?state=$projectId");
|
||||
});
|
||||
|
||||
App::get('/v1/vcs/github/incominginstallation')
|
||||
->desc('Capture installation id and state after GitHub App Installation')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'public')
|
||||
->param('installation_id', '', new Text(256), 'installation_id')
|
||||
->param('setup_action', '', new Text(256), 'setup_action')
|
||||
->param('state', '', new Text(256), 'state')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $installationId, string $setupAction, string $state, Request $request, Response $response, Database $dbForConsole) {
|
||||
|
||||
$project = $dbForConsole->getDocument('projects', $state);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
$url = $request->getProtocol() . '://' . $request->getHostname() . "/";
|
||||
$response->redirect($url);
|
||||
}
|
||||
|
||||
$projectInternalId = $project->getInternalId();
|
||||
|
||||
$vcsInstallation = $dbForConsole->findOne('vcs_installations', [
|
||||
Query::equal('installationId', [$installationId]),
|
||||
Query::equal('projectInternalId', [$projectInternalId])
|
||||
]);
|
||||
|
||||
if (!$vcsInstallation) {
|
||||
$vcsInstallation = new Document([
|
||||
'$id' => ID::unique(),
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'installationId' => $installationId,
|
||||
'projectId' => $state,
|
||||
'projectInternalId' => $projectInternalId,
|
||||
'provider' => 'GitHub',
|
||||
'organization' => '(todo) My Awesome Organization',
|
||||
'accessToken' => null
|
||||
]);
|
||||
|
||||
$vcsInstallation = $dbForConsole->createDocument('vcs_installations', $vcsInstallation);
|
||||
} else {
|
||||
$vcsInstallation = $vcsInstallation->setAttribute('organization', '(todo) My Awesome Organization');
|
||||
$vcsInstallation = $dbForConsole->updateDocument('vcs_installations', $vcsInstallation->getId(), $vcsInstallation);
|
||||
}
|
||||
|
||||
$url = $request->getProtocol() . '://' . $request->getHostname() . ":3000/console/project-$state/settings/git-installations";
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->redirect($url);
|
||||
});
|
||||
|
||||
App::get('v1/vcs/github/installations/:installationId/repositories')
|
||||
->desc('List repositories')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'public')
|
||||
->label('sdk.namespace', 'vcs')
|
||||
->label('sdk.method', 'listRepositories')
|
||||
->label('sdk.description', '')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_REPOSITORY_LIST)
|
||||
->param('installationId', '', new Text(256), 'Installation Id')
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $vcsInstallationId, string $search, Response $response, Document $project, Database $dbForConsole) {
|
||||
if (empty($search)) {
|
||||
$search = "";
|
||||
}
|
||||
|
||||
$installation = $dbForConsole->getDocument('vcs_installations', $vcsInstallationId, [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if ($installation->isEmpty()) {
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$installationId = $installation->getAttribute('installationId');
|
||||
$privateKey = App::getEnv('VCS_GITHUB_PRIVATE_KEY');
|
||||
$githubAppId = App::getEnv('VCS_GITHUB_APP_ID');
|
||||
$github = new GitHub();
|
||||
$github->initialiseVariables($installationId, $privateKey, $githubAppId);
|
||||
|
||||
$page = 1;
|
||||
$per_page = 100; // max limit of GitHub API
|
||||
$repos = []; // Array to store all repositories
|
||||
|
||||
// loop to store all repos in repos array
|
||||
|
||||
do {
|
||||
$repositories = $github->listRepositoriesForGitHubApp($page, $per_page);
|
||||
$repos = array_merge($repos, $repositories);
|
||||
$page++;
|
||||
} while ($repositories == $per_page);
|
||||
|
||||
// Filter repositories based on search parameter
|
||||
if (!empty($search)) {
|
||||
$repos = array_filter($repos, function ($repo) use ($search) {
|
||||
$repoName = strtolower($repo['name']);
|
||||
$searchTerm = strtolower($search);
|
||||
return strpos($repoName, $searchTerm) !== false;
|
||||
});
|
||||
}
|
||||
// Sort repositories by last modified date in descending order
|
||||
usort($repos, function ($repo1, $repo2) {
|
||||
return strtotime($repo2['pushed_at']) - strtotime($repo1['pushed_at']);
|
||||
});
|
||||
|
||||
// Limit the maximum results to 5
|
||||
$repos = array_slice($repos, 0, 5);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'repositories' => $repos,
|
||||
'total' => \count($repos),
|
||||
]), Response::MODEL_REPOSITORY_LIST);
|
||||
});
|
||||
|
||||
App::post('/v1/vcs/github/incomingwebhook')
|
||||
->desc('Captures GitHub Webhook Events')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'public')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->inject('cache')
|
||||
->inject('db')
|
||||
->action(
|
||||
function (Request $request, Response $response, Database $dbForConsole, mixed $cache, mixed $db) {
|
||||
$cache = new Cache(new Redis($cache));
|
||||
$event = $request->getHeader('x-github-event', '');
|
||||
$payload = $request->getRawPayload();
|
||||
$github = new GitHub();
|
||||
$privateKey = App::getEnv('VCS_GITHUB_PRIVATE_KEY');
|
||||
$githubAppId = App::getEnv('VCS_GITHUB_APP_ID');
|
||||
$parsedPayload = $github->parseWebhookEventPayload($event, $payload);
|
||||
|
||||
if ($event == $github::EVENT_PUSH) {
|
||||
$branchName = $parsedPayload["branch"];
|
||||
$repositoryId = $parsedPayload["repositoryId"];
|
||||
$installationId = $parsedPayload["installationId"];
|
||||
$SHA = $parsedPayload["SHA"];
|
||||
$owner = $parsedPayload["owner"];
|
||||
|
||||
//find functionId from functions table
|
||||
$resources = $dbForConsole->find('vcs_repos', [
|
||||
Query::equal('repositoryId', [$repositoryId]),
|
||||
Query::limit(100),
|
||||
]);
|
||||
|
||||
foreach ($resources as $resource) {
|
||||
$resourceType = $resource->getAttribute('resourceType');
|
||||
|
||||
if ($resourceType == "function") {
|
||||
// TODO: For cloud, we might have different $db
|
||||
$dbForProject = new Database(new MariaDB($db), $cache);
|
||||
$dbForProject->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
$dbForProject->setNamespace("_{$resource->getAttribute('projectInternalId')}");
|
||||
|
||||
$functionId = $resource->getAttribute('resourceId');
|
||||
//TODO: Why is Authorization::skip needed?
|
||||
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||
$projectId = $resource->getAttribute('projectId');
|
||||
//TODO: Why is Authorization::skip needed?
|
||||
$project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId));
|
||||
$deploymentId = ID::unique();
|
||||
$entrypoint = 'index.js'; //TODO: Read from function settings
|
||||
$vcsRepoId = $resource->getId();
|
||||
$vcsRepoInternalId = $resource->getInternalId();
|
||||
$vcsInstallationId = $resource->getAttribute('vcsInstallationId');
|
||||
$vcsInstallationInternalId = $resource->getAttribute('vcsInstallationInternalId');
|
||||
$activate = false;
|
||||
|
||||
if ($branchName == "main") {
|
||||
$activate = true;
|
||||
}
|
||||
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
'$id' => $deploymentId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'resourceId' => $functionId,
|
||||
'resourceType' => 'functions',
|
||||
'entrypoint' => $entrypoint,
|
||||
'type' => "vcs",
|
||||
'vcsInstallationId' => $vcsInstallationId,
|
||||
'vcsInstallationInternalId' => $vcsInstallationInternalId,
|
||||
'vcsRepoId' => $vcsRepoId,
|
||||
'vcsRepoInternalId' => $vcsRepoInternalId,
|
||||
'branch' => $branchName,
|
||||
'search' => implode(' ', [$deploymentId, $entrypoint]),
|
||||
'activate' => $activate,
|
||||
]));
|
||||
|
||||
$targetUrl = $request->getProtocol() . '://' . $request->getHostname() . ":3000/console/project-$projectId/functions/function-$functionId";
|
||||
|
||||
$buildEvent = new Build();
|
||||
$buildEvent
|
||||
->setType(BUILD_TYPE_DEPLOYMENT)
|
||||
->setResource($function)
|
||||
->setDeployment($deployment)
|
||||
->setProject($project)
|
||||
->setSHA($SHA)
|
||||
->setOwner($owner)
|
||||
->setTargetUrl($targetUrl)
|
||||
->trigger();
|
||||
|
||||
//TODO: Add event?
|
||||
}
|
||||
}
|
||||
} else if ($event == $github::EVENT_INSTALLATION) {
|
||||
if ($parsedPayload["action"] == "deleted") {
|
||||
// TODO: Use worker for this job instead
|
||||
$installationId = $parsedPayload["installationId"];
|
||||
|
||||
$vcsInstallations = $dbForConsole->find('vcs_installations', [
|
||||
Query::equal('installationId', [$installationId]),
|
||||
Query::limit(1000)
|
||||
]);
|
||||
|
||||
foreach ($vcsInstallations as $installation) {
|
||||
$vcsRepos = $dbForConsole->find('vcs_repos', [
|
||||
Query::equal('vcsInstallationId', [$installation->getId()]),
|
||||
Query::limit(1000)
|
||||
]);
|
||||
|
||||
foreach ($vcsRepos as $repo) {
|
||||
$dbForConsole->deleteDocument('vcs_repos', $repo->getId());
|
||||
}
|
||||
|
||||
$dbForConsole->deleteDocument('vcs_installations', $installation->getId());
|
||||
}
|
||||
}
|
||||
} else if ($event == $github::EVENT_PULL_REQUEST) {
|
||||
if ($parsedPayload["action"] == "opened" or $parsedPayload["action"] == "reopened") {
|
||||
$startNewDeployment = false;
|
||||
$branchName = $parsedPayload["branch"];
|
||||
$repositoryId = $parsedPayload["repositoryId"];
|
||||
$installationId = $parsedPayload["installationId"];
|
||||
$pullRequestNumber = $parsedPayload["pullRequestNumber"];
|
||||
$repositoryName = $parsedPayload["repositoryName"];
|
||||
$owner = $parsedPayload["owner"];
|
||||
$github->initialiseVariables($installationId, $privateKey, $githubAppId);
|
||||
|
||||
$vcsRepos = $dbForConsole->find('vcs_repos', [
|
||||
Query::equal('repositoryId', [$repositoryId]),
|
||||
Query::orderDesc('$createdAt')
|
||||
]);
|
||||
|
||||
$dbForProject = new Database(new MariaDB($db), $cache);
|
||||
$dbForProject->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
|
||||
|
||||
if ($vcsRepos) {
|
||||
$dbForProject->setNamespace("_{$vcsRepos[0]->getAttribute('projectInternalId')}");
|
||||
$vcsRepoId = $vcsRepos[0]->getId();
|
||||
$deployment = Authorization::skip(fn () => $dbForProject->find('deployments', [
|
||||
Query::equal('vcsRepoId', [$vcsRepoId]),
|
||||
Query::equal('branch', [$branchName]),
|
||||
Query::orderDesc('$createdAt')
|
||||
]));
|
||||
|
||||
if ($deployment) {
|
||||
$buildId = $deployment[0]->getAttribute('buildId');
|
||||
$build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $buildId));
|
||||
$buildStatus = $build->getAttribute('status');
|
||||
$comment = "| Build Status |\r\n | --------------- |\r\n | $buildStatus |";
|
||||
$commentId = $github->addComment($owner, $repositoryName, $pullRequestNumber, $comment);
|
||||
} else {
|
||||
$startNewDeployment = true;
|
||||
}
|
||||
} else {
|
||||
$startNewDeployment = true;
|
||||
}
|
||||
if ($startNewDeployment) {
|
||||
$commentId = strval($github->addComment($owner, $repositoryName, $pullRequestNumber, "Build is not deployed yet 🚀"));
|
||||
|
||||
foreach ($vcsRepos as $resource) {
|
||||
$resourceType = $resource->getAttribute('resourceType');
|
||||
|
||||
if ($resourceType == "function") {
|
||||
// TODO: For cloud, we might have different $db
|
||||
$dbForProject->setNamespace("_{$resource->getAttribute('projectInternalId')}");
|
||||
|
||||
$functionId = $resource->getAttribute('resourceId');
|
||||
//TODO: Why is Authorization::skip needed?
|
||||
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||
$projectId = $resource->getAttribute('projectId');
|
||||
//TODO: Why is Authorization::skip needed?
|
||||
$project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId));
|
||||
$deploymentId = ID::unique();
|
||||
$entrypoint = 'index.js'; //TODO: Read from function settings
|
||||
$vcsRepoId = $resource->getId();
|
||||
$vcsRepoInternalId = $resource->getInternalId();
|
||||
$vcsInstallationId = $resource->getAttribute('vcsInstallationId');
|
||||
$vcsInstallationInternalId = $resource->getAttribute('vcsInstallationInternalId');
|
||||
$activate = false;
|
||||
|
||||
if ($branchName == "main") {
|
||||
$activate = true;
|
||||
}
|
||||
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
'$id' => $deploymentId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'resourceId' => $functionId,
|
||||
'resourceType' => 'functions',
|
||||
'entrypoint' => $entrypoint,
|
||||
'type' => "vcs",
|
||||
'vcsInstallationId' => $vcsInstallationId,
|
||||
'vcsInstallationInternalId' => $vcsInstallationInternalId,
|
||||
'vcsRepoId' => $vcsRepoId,
|
||||
'vcsRepoInternalId' => $vcsRepoInternalId,
|
||||
'branch' => $branchName,
|
||||
'vcsCommentId' => $commentId,
|
||||
'search' => implode(' ', [$deploymentId, $entrypoint]),
|
||||
'activate' => $activate,
|
||||
]));
|
||||
|
||||
$buildEvent = new Build();
|
||||
$buildEvent
|
||||
->setType(BUILD_TYPE_DEPLOYMENT)
|
||||
->setResource($function)
|
||||
->setDeployment($deployment)
|
||||
->setProject($project)
|
||||
->setOwner($owner)
|
||||
->trigger();
|
||||
|
||||
//TODO: Add event?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response->json($parsedPayload);
|
||||
}
|
||||
);
|
||||
|
||||
App::get('/v1/vcs/installations')
|
||||
->groups(['api', 'vcs'])
|
||||
->desc('List installations')
|
||||
->label('scope', 'public')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'vcs')
|
||||
->label('sdk.method', 'listInstallations')
|
||||
->label('sdk.description', '/docs/references/vcs/list-installations.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_INSTALLATION_LIST)
|
||||
->param('queries', [], new Installations(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Installations::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForConsole')
|
||||
->action(function (array $queries, string $search, Response $response, Document $project, Database $dbForConsole) {
|
||||
|
||||
$queries = Query::parseQueries($queries);
|
||||
|
||||
$queries[] = Query::equal('projectInternalId', [$project->getInternalId()]);
|
||||
|
||||
if (!empty($search)) {
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
// Get cursor document if there was a cursor query
|
||||
$cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE);
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$vcsInstallationId = $cursor->getValue();
|
||||
$cursorDocument = $dbForConsole->getDocument('vcs_installations', $vcsInstallationId);
|
||||
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Installation '{$vcsInstallationId}' for the 'cursor' value not found.");
|
||||
}
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'installations' => $dbForConsole->find('vcs_installations', $queries),
|
||||
'total' => $dbForConsole->count('vcs_installations', $filterQueries, APP_LIMIT_COUNT),
|
||||
]), Response::MODEL_INSTALLATION_LIST);
|
||||
});
|
||||
|
||||
App::delete('/v1/vcs/installations/:installationId')
|
||||
->groups(['api', 'vcs'])
|
||||
->desc('Delete Installation')
|
||||
->label('scope', 'public')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
->label('sdk.namespace', 'vcs')
|
||||
->label('sdk.method', 'deleteInstallation')
|
||||
->label('sdk.description', '/docs/references/vcs/delete-installation.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('installationId', '', new Text(256), 'Installation Id')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForConsole')
|
||||
->inject('deletes')
|
||||
->action(function (string $vcsInstallationId, Response $response, Document $project, Database $dbForConsole, Delete $deletes) {
|
||||
|
||||
$installation = $dbForConsole->getDocument('vcs_installations', $vcsInstallationId, [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()])
|
||||
]);
|
||||
|
||||
if ($installation->isEmpty()) {
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!$dbForConsole->deleteDocument('vcs_installations', $installation->getId())) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove installation from DB');
|
||||
}
|
||||
|
||||
$deletes
|
||||
->setType(DELETE_TYPE_DOCUMENT)
|
||||
->setDocument($installation);
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
@@ -155,6 +155,7 @@ const DELETE_TYPE_CERTIFICATES = 'certificates';
|
||||
const DELETE_TYPE_USAGE = 'usage';
|
||||
const DELETE_TYPE_REALTIME = 'realtime';
|
||||
const DELETE_TYPE_BUCKETS = 'buckets';
|
||||
const DELETE_TYPE_INSTALLATIONS = 'vcs_installations';
|
||||
const DELETE_TYPE_SESSIONS = 'sessions';
|
||||
const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp';
|
||||
const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource';
|
||||
|
||||
+125
-17
@@ -7,6 +7,7 @@ use Appwrite\Utopia\Response\Model\Deployment;
|
||||
use Cron\CronExpression;
|
||||
use Executor\Executor;
|
||||
use Appwrite\Usage\Stats;
|
||||
use phpDocumentor\Reflection\DocBlock\Tags\Var_;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\App;
|
||||
@@ -16,6 +17,8 @@ use Utopia\Storage\Storage;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
||||
require_once __DIR__ . '/../init.php';
|
||||
|
||||
@@ -43,12 +46,15 @@ class BuildsV1 extends Worker
|
||||
$project = new Document($this->args['project'] ?? []);
|
||||
$resource = new Document($this->args['resource'] ?? []);
|
||||
$deployment = new Document($this->args['deployment'] ?? []);
|
||||
$SHA = $this->args['SHA'] ?? '';
|
||||
$owner = $this->args['owner'] ?? '';
|
||||
$targetUrl = $this->args['targetUrl'] ?? '';
|
||||
|
||||
switch ($type) {
|
||||
case BUILD_TYPE_DEPLOYMENT:
|
||||
case BUILD_TYPE_RETRY:
|
||||
Console::info('Creating build for deployment: ' . $deployment->getId());
|
||||
$this->buildDeployment($project, $resource, $deployment);
|
||||
$this->buildDeployment($project, $resource, $deployment, $SHA, $owner, $targetUrl);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -57,9 +63,10 @@ class BuildsV1 extends Worker
|
||||
}
|
||||
}
|
||||
|
||||
protected function buildDeployment(Document $project, Document $function, Document $deployment)
|
||||
protected function buildDeployment(Document $project, Document $function, Document $deployment, string $SHA = '', string $owner = '', string $targetUrl = '')
|
||||
{
|
||||
$dbForProject = $this->getProjectDB($project->getId());
|
||||
$dbForConsole = $this->getConsoleDB();
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $function->getId());
|
||||
if ($function->isEmpty()) {
|
||||
@@ -82,21 +89,93 @@ class BuildsV1 extends Worker
|
||||
$startTime = DateTime::now();
|
||||
if (empty($buildId)) {
|
||||
$buildId = ID::unique();
|
||||
$build = $dbForProject->createDocument('builds', new Document([
|
||||
'$id' => $buildId,
|
||||
'$permissions' => [],
|
||||
'startTime' => $startTime,
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'status' => 'processing',
|
||||
'outputPath' => '',
|
||||
'runtime' => $function->getAttribute('runtime'),
|
||||
'source' => $deployment->getAttribute('path'),
|
||||
'sourceType' => strtolower(App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)),
|
||||
'stdout' => '',
|
||||
'stderr' => '',
|
||||
'endTime' => null,
|
||||
'duration' => 0
|
||||
]));
|
||||
|
||||
$vcsInstallationId = $deployment->getAttribute('vcsInstallationId');
|
||||
$vcsRepoId = $deployment->getAttribute('vcsRepoId');
|
||||
$isVcsEnabled = $vcsRepoId !== null ? true : false;
|
||||
$addComment = false;
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$vcsRepos = Authorization::skip(fn () => $dbForConsole
|
||||
->getDocument('vcs_repos', $vcsRepoId));
|
||||
$repositoryId = $vcsRepos->getAttribute('repositoryId');
|
||||
$owner = $vcsRepos->getAttribute('repositoryOwner');
|
||||
$vcsInstallations = Authorization::skip(fn () => $dbForConsole
|
||||
->getDocument('vcs_installations', $vcsInstallationId));
|
||||
$installationId = $vcsInstallations->getAttribute('installationId');
|
||||
|
||||
$privateKey = App::getEnv('VCS_GITHUB_PRIVATE_KEY');
|
||||
$githubAppId = App::getEnv('VCS_GITHUB_APP_ID');
|
||||
|
||||
$github = new GitHub();
|
||||
$github->initialiseVariables($installationId, $privateKey, $githubAppId);
|
||||
$repositoryName = $github->getRepositoryName($repositoryId);
|
||||
$branchName = $deployment->getAttribute('branch');
|
||||
$gitCloneCommand = $github->generateGitCloneCommand($owner, $repositoryId, $branchName);
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
Console::execute('mkdir /tmp/builds/' . $buildId, '', $stdout, $stderr);
|
||||
Console::execute($gitCloneCommand . ' /tmp/builds/' . $buildId . '/code', '', $stdout, $stderr);
|
||||
Console::execute('tar --exclude code.tar.gz -czf /tmp/builds/' . $buildId . '/code.tar.gz -C /tmp/builds/' . $buildId . '/code .', '', $stdout, $stderr);
|
||||
|
||||
$deviceFunctions = $this->getFunctionsDevice($project->getId());
|
||||
|
||||
$fileName = 'code.tar.gz';
|
||||
$fileTmpName = '/tmp/builds/' . $buildId . '/code.tar.gz';
|
||||
|
||||
$deploymentId = $deployment->getId();
|
||||
$path = $deviceFunctions->getPath($deploymentId . '.' . \pathinfo($fileName, PATHINFO_EXTENSION));
|
||||
|
||||
$result = $deviceFunctions->move($fileTmpName, $path);
|
||||
|
||||
if (!$result) {
|
||||
throw new \Exception("Unable to move file");
|
||||
}
|
||||
|
||||
Console::execute('rm -rf /tmp/builds/' . $buildId, '', $stdout, $stderr);
|
||||
|
||||
$build = $dbForProject->createDocument('builds', new Document([
|
||||
'$id' => $buildId,
|
||||
'$permissions' => [],
|
||||
'startTime' => $startTime,
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'status' => 'processing',
|
||||
'outputPath' => '',
|
||||
'runtime' => $function->getAttribute('runtime'),
|
||||
'source' => $path,
|
||||
'sourceType' => strtolower(App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)),
|
||||
'stdout' => '',
|
||||
'stderr' => '',
|
||||
'endTime' => null,
|
||||
'duration' => 0
|
||||
]));
|
||||
if ($SHA !== "" && $owner !== ""){
|
||||
$github->updateCommitStatus($repositoryName, $SHA, $owner, "pending", "Deployment is being processed..", $targetUrl, "Appwrite Deployment");
|
||||
}
|
||||
$commentId = $deployment->getAttribute('vcsCommentId');
|
||||
if ($commentId) {
|
||||
$comment = "| Build Status |\r\n | --------------- |\r\n | Processing |";
|
||||
|
||||
$github->updateComment($owner, $repositoryName, $commentId, $comment);
|
||||
$addComment = true;
|
||||
}
|
||||
} else {
|
||||
$build = $dbForProject->createDocument('builds', new Document([
|
||||
'$id' => $buildId,
|
||||
'$permissions' => [],
|
||||
'startTime' => $startTime,
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'status' => 'processing',
|
||||
'outputPath' => '',
|
||||
'runtime' => $function->getAttribute('runtime'),
|
||||
'source' => $deployment->getAttribute('path'),
|
||||
'sourceType' => strtolower(App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)),
|
||||
'stdout' => '',
|
||||
'stderr' => '',
|
||||
'endTime' => null,
|
||||
'duration' => 0
|
||||
]));
|
||||
}
|
||||
$deployment->setAttribute('buildId', $buildId);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
} else {
|
||||
@@ -105,6 +184,13 @@ class BuildsV1 extends Worker
|
||||
|
||||
/** Request the executor to build the code... */
|
||||
$build->setAttribute('status', 'building');
|
||||
if ($isVcsEnabled) {
|
||||
$commentId = $deployment->getAttribute('vcsCommentId');
|
||||
if ($commentId) {
|
||||
$comment = "| Build Status |\r\n | --------------- |\r\n | Building |";
|
||||
$github->updateComment($owner, $repositoryName, $commentId, $comment);
|
||||
}
|
||||
}
|
||||
$build = $dbForProject->updateDocument('builds', $buildId, $build);
|
||||
|
||||
/** Trigger Webhook */
|
||||
@@ -147,6 +233,10 @@ class BuildsV1 extends Worker
|
||||
|
||||
$source = $deployment->getAttribute('path');
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$source = $path;
|
||||
}
|
||||
|
||||
$vars = array_reduce($function['vars'] ?? [], function (array $carry, Document $var) {
|
||||
$carry[$var->getAttribute('key')] = $var->getAttribute('value');
|
||||
return $carry;
|
||||
@@ -181,6 +271,24 @@ class BuildsV1 extends Worker
|
||||
$build->setAttribute('stderr', $response['stderr']);
|
||||
$build->setAttribute('stdout', $response['response']);
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
|
||||
$status = $response["status"];
|
||||
|
||||
if ($status === "ready" && $SHA !== "" && $owner !== "") {
|
||||
$github->updateCommitStatus($repositoryName, $SHA, $owner, "success", "Deployment is successful!", $targetUrl, "Appwrite Deployment");
|
||||
}
|
||||
else if ($status === "failed" && $SHA !== "" && $owner !== "") {
|
||||
$github->updateCommitStatus($repositoryName, $SHA, $owner, "failure", "Deployment failed.", $targetUrl, "Appwrite Deployment");
|
||||
}
|
||||
|
||||
$commentId = $deployment->getAttribute('vcsCommentId');
|
||||
if ($commentId) {
|
||||
$comment = "| Build Status |\r\n | --------------- |\r\n | $status |";
|
||||
$github->updateComment($owner, $repositoryName, $commentId, $comment);
|
||||
}
|
||||
}
|
||||
|
||||
/* Also update the deployment buildTime */
|
||||
$deployment->setAttribute('buildTime', $response['duration']);
|
||||
|
||||
|
||||
@@ -65,6 +65,9 @@ class DeletesV1 extends Worker
|
||||
case DELETE_TYPE_BUCKETS:
|
||||
$this->deleteBucket($document, $project->getId());
|
||||
break;
|
||||
case DELETE_TYPE_INSTALLATIONS:
|
||||
$this->deleteInstallation($document, $project->getId());
|
||||
break;
|
||||
default:
|
||||
if (\str_starts_with($document->getCollection(), 'database_')) {
|
||||
$this->deleteCollection($document, $project->getId());
|
||||
@@ -704,6 +707,43 @@ class DeletesV1 extends Worker
|
||||
Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $collection collectionID
|
||||
* @param Query[] $queries
|
||||
* @param Database $database
|
||||
* @param callable $callback
|
||||
*/
|
||||
protected function listByGroup(string $collection, array $queries, Database $database, callable $callback = null): void
|
||||
{
|
||||
$count = 0;
|
||||
$chunk = 0;
|
||||
$limit = 50;
|
||||
$results = [];
|
||||
$sum = $limit;
|
||||
|
||||
$executionStart = \microtime(true);
|
||||
|
||||
while ($sum === $limit) {
|
||||
$chunk++;
|
||||
|
||||
$results = $database->find($collection, \array_merge([Query::limit($limit)], $queries));
|
||||
|
||||
$sum = count($results);
|
||||
|
||||
foreach ($results as $document) {
|
||||
if (is_callable($callback)) {
|
||||
$callback($document);
|
||||
}
|
||||
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
$executionEnd = \microtime(true);
|
||||
|
||||
Console::info("Listed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Document $document certificates document
|
||||
* @throws \Utopia\Database\Exception\Authorization
|
||||
@@ -761,4 +801,23 @@ class DeletesV1 extends Worker
|
||||
|
||||
$device->deletePath($document->getId());
|
||||
}
|
||||
|
||||
protected function deleteInstallation(Document $document, string $projectId)
|
||||
{
|
||||
$dbForProject = $this->getProjectDB($projectId);
|
||||
$dbForConsole = $this->getConsoleDB();
|
||||
|
||||
$this->listByGroup('functions', [
|
||||
Query::equal('vcsInstallationInternalId', [$document->getInternalId()])
|
||||
], $dbForProject, function($function) use ($dbForProject, $dbForConsole) {
|
||||
$dbForConsole->deleteDocument('vcs_repos', $function->getAttribute('vcsRepoId'));
|
||||
|
||||
$function = $function
|
||||
->setAttribute('vcsInstallationId', '')
|
||||
->setAttribute('vcsInstallationInternalId', '')
|
||||
->setAttribute('vcsRepoId', '')
|
||||
->setAttribute('vcsRepoInternalId', '');
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/storage": "0.14.*",
|
||||
"utopia-php/swoole": "0.5.*",
|
||||
"utopia-php/vcs": "dev-feat-git-adapter as 0.1.99",
|
||||
"utopia-php/websocket": "0.1.*",
|
||||
"resque/php-resque": "1.3.6",
|
||||
"matomo/device-detector": "6.0.*",
|
||||
@@ -76,6 +77,10 @@
|
||||
{
|
||||
"url": "https://github.com/appwrite/runtimes.git",
|
||||
"type": "git"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/utopia-php/vcs.git",
|
||||
"type": "git"
|
||||
}
|
||||
],
|
||||
"require-dev": {
|
||||
|
||||
Generated
+511
-240
File diff suppressed because it is too large
Load Diff
@@ -177,6 +177,9 @@ services:
|
||||
- _APP_GRAPHQL_MAX_BATCH_SIZE
|
||||
- _APP_GRAPHQL_MAX_COMPLEXITY
|
||||
- _APP_GRAPHQL_MAX_DEPTH
|
||||
- VCS_GITHUB_PRIVATE_KEY
|
||||
- VCS_GITHUB_APP_ID
|
||||
- VCS_GITHUB_APP_NAME
|
||||
|
||||
appwrite-realtime:
|
||||
entrypoint: realtime
|
||||
@@ -372,6 +375,8 @@ services:
|
||||
volumes:
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
- appwrite-functions:/storage/functions:rw
|
||||
- appwrite-builds:/storage/builds:rw
|
||||
depends_on:
|
||||
- redis
|
||||
- mariadb
|
||||
@@ -391,6 +396,11 @@ services:
|
||||
- _APP_DB_PASS
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_GITHUB_PRIVATE_KEY
|
||||
- _APP_GITHUB_APP_ID
|
||||
- VCS_GITHUB_PRIVATE_KEY
|
||||
- VCS_GITHUB_APP_ID
|
||||
- VCS_GITHUB_APP_NAME
|
||||
|
||||
appwrite-worker-certificates:
|
||||
entrypoint: worker-certificates
|
||||
|
||||
@@ -10,6 +10,9 @@ class Build extends Event
|
||||
protected string $type = '';
|
||||
protected ?Document $resource = null;
|
||||
protected ?Document $deployment = null;
|
||||
protected string $SHA = '';
|
||||
protected string $owner = '';
|
||||
protected string $targetUrl = '';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -85,6 +88,45 @@ class Build extends Event
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets commit SHA for the build event.
|
||||
*
|
||||
* @param string $SHA is the commit hash of the incoming commit
|
||||
* @return self
|
||||
*/
|
||||
public function setSHA(string $SHA): self
|
||||
{
|
||||
$this->SHA = $SHA;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets repository owner name for the build event.
|
||||
*
|
||||
* @param string $owner is the name of the repository owner
|
||||
* @return self
|
||||
*/
|
||||
public function setOwner(string $owner): self
|
||||
{
|
||||
$this->owner = $owner;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets redirect target url for the deployment
|
||||
*
|
||||
* @param string $targetUrl is the url that is to be set
|
||||
* @return self
|
||||
*/
|
||||
public function setTargetUrl(string $targetUrl): self
|
||||
{
|
||||
$this->targetUrl = $targetUrl;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the function event and sends it to the functions worker.
|
||||
*
|
||||
@@ -97,7 +139,10 @@ class Build extends Event
|
||||
'project' => $this->project,
|
||||
'resource' => $this->resource,
|
||||
'deployment' => $this->deployment,
|
||||
'type' => $this->type
|
||||
'type' => $this->type,
|
||||
'SHA' => $this->SHA,
|
||||
'owner' => $this->owner,
|
||||
'targetUrl' => $this->targetUrl
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +110,9 @@ class Exception extends \Exception
|
||||
public const FUNCTION_NOT_FOUND = 'function_not_found';
|
||||
public const FUNCTION_RUNTIME_UNSUPPORTED = 'function_runtime_unsupported';
|
||||
|
||||
/** VCS */
|
||||
public const INSTALLATION_NOT_FOUND = 'installation_not_found';
|
||||
|
||||
/** Deployments */
|
||||
public const DEPLOYMENT_NOT_FOUND = 'deployment_not_found';
|
||||
|
||||
|
||||
@@ -254,6 +254,7 @@ class Mapper
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Collections':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Databases':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Deployments':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Installations':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Documents':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Executions':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Files':
|
||||
|
||||
@@ -345,6 +345,7 @@ class OpenAPI3 extends Format
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Collections':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Databases':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Deployments':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Installations':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Documents':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Executions':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Files':
|
||||
|
||||
@@ -344,6 +344,7 @@ class Swagger2 extends Format
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Collections':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Databases':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Deployments':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Installations':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Documents':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Executions':
|
||||
case 'Appwrite\Utopia\Database\Validator\Queries\Files':
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator\Queries;
|
||||
|
||||
class Installations extends Base
|
||||
{
|
||||
public const ALLOWED_ATTRIBUTES = [
|
||||
'provider',
|
||||
'organization'
|
||||
];
|
||||
|
||||
/**
|
||||
* Expression constructor
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('vcs_installations', self::ALLOWED_ATTRIBUTES);
|
||||
}
|
||||
}
|
||||
@@ -72,8 +72,10 @@ use Appwrite\Utopia\Response\Model\HealthQueue;
|
||||
use Appwrite\Utopia\Response\Model\HealthStatus;
|
||||
use Appwrite\Utopia\Response\Model\HealthTime;
|
||||
use Appwrite\Utopia\Response\Model\HealthVersion;
|
||||
use Appwrite\Utopia\Response\Model\Installation;
|
||||
use Appwrite\Utopia\Response\Model\Mock; // Keep last
|
||||
use Appwrite\Utopia\Response\Model\Provider;
|
||||
use Appwrite\Utopia\Response\Model\Repository;
|
||||
use Appwrite\Utopia\Response\Model\Runtime;
|
||||
use Appwrite\Utopia\Response\Model\UsageBuckets;
|
||||
use Appwrite\Utopia\Response\Model\UsageCollection;
|
||||
@@ -193,6 +195,12 @@ class Response extends SwooleResponse
|
||||
public const MODEL_BUILD_LIST = 'buildList'; // Not used anywhere yet
|
||||
public const MODEL_FUNC_PERMISSIONS = 'funcPermissions';
|
||||
|
||||
// VCS
|
||||
public const MODEL_INSTALLATION = 'installation';
|
||||
public const MODEL_INSTALLATION_LIST = 'installationList';
|
||||
public const MODEL_REPOSITORY = 'repository';
|
||||
public const MODEL_REPOSITORY_LIST = 'repositoryList';
|
||||
|
||||
// Project
|
||||
public const MODEL_PROJECT = 'project';
|
||||
public const MODEL_PROJECT_LIST = 'projectList';
|
||||
@@ -208,6 +216,7 @@ class Response extends SwooleResponse
|
||||
public const MODEL_DOMAIN_LIST = 'domainList';
|
||||
public const MODEL_VARIABLE = 'variable';
|
||||
public const MODEL_VARIABLE_LIST = 'variableList';
|
||||
public const MODEL_VCS = 'vcs';
|
||||
|
||||
// Health
|
||||
public const MODEL_HEALTH_STATUS = 'healthStatus';
|
||||
@@ -263,6 +272,8 @@ class Response extends SwooleResponse
|
||||
->setModel(new BaseList('Teams List', self::MODEL_TEAM_LIST, 'teams', self::MODEL_TEAM))
|
||||
->setModel(new BaseList('Memberships List', self::MODEL_MEMBERSHIP_LIST, 'memberships', self::MODEL_MEMBERSHIP))
|
||||
->setModel(new BaseList('Functions List', self::MODEL_FUNCTION_LIST, 'functions', self::MODEL_FUNCTION))
|
||||
->setModel(new BaseList('Installations List', self::MODEL_INSTALLATION_LIST, 'installations', self::MODEL_INSTALLATION))
|
||||
->setModel(new BaseList('Repositories List', self::MODEL_REPOSITORY_LIST, 'repositories', self::MODEL_REPOSITORY))
|
||||
->setModel(new BaseList('Runtimes List', self::MODEL_RUNTIME_LIST, 'runtimes', self::MODEL_RUNTIME))
|
||||
->setModel(new BaseList('Deployments List', self::MODEL_DEPLOYMENT_LIST, 'deployments', self::MODEL_DEPLOYMENT))
|
||||
->setModel(new BaseList('Executions List', self::MODEL_EXECUTION_LIST, 'executions', self::MODEL_EXECUTION))
|
||||
@@ -317,6 +328,8 @@ class Response extends SwooleResponse
|
||||
->setModel(new Team())
|
||||
->setModel(new Membership())
|
||||
->setModel(new Func())
|
||||
->setModel(new Installation())
|
||||
->setModel(new Repository())
|
||||
->setModel(new Runtime())
|
||||
->setModel(new Deployment())
|
||||
->setModel(new Execution())
|
||||
|
||||
@@ -49,6 +49,12 @@ class Func extends Model
|
||||
'default' => true,
|
||||
'example' => false,
|
||||
])
|
||||
->addRule('repositoryId', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'VCS Repository ID',
|
||||
'default' => false,
|
||||
'example' => '35493993',
|
||||
])
|
||||
->addRule('runtime', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Function execution runtime.',
|
||||
@@ -99,6 +105,12 @@ class Func extends Model
|
||||
'default' => 15,
|
||||
'example' => 15,
|
||||
])
|
||||
->addRule('vcsInstallationId', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Function vcs installation id.',
|
||||
'default' => '',
|
||||
'example' => '644051bd6572792165cc',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class Installation extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->addRule('$id', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Function ID.',
|
||||
'default' => '',
|
||||
'example' => '5e5ea5c16897e',
|
||||
])
|
||||
->addRule('$createdAt', [
|
||||
'type' => self::TYPE_DATETIME,
|
||||
'description' => 'Function creation date in ISO 8601 format.',
|
||||
'default' => '',
|
||||
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||
])
|
||||
->addRule('$updatedAt', [
|
||||
'type' => self::TYPE_DATETIME,
|
||||
'description' => 'Function update date in ISO 8601 format.',
|
||||
'default' => '',
|
||||
'example' => self::TYPE_DATETIME_EXAMPLE,
|
||||
])
|
||||
->addRule('provider', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Installation provider.',
|
||||
'default' => [],
|
||||
'example' => 'github',
|
||||
'array' => false,
|
||||
])
|
||||
->addRule('organization', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Installation organization.',
|
||||
'default' => [],
|
||||
'example' => 'appwrite',
|
||||
'array' => false,
|
||||
])
|
||||
->addRule('installationId', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Provider installation ID.',
|
||||
'default' => '',
|
||||
'example' => '5322',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Installation';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_INSTALLATION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
|
||||
class Repository extends Model
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->addRule('id', [
|
||||
'type' => self::TYPE_INTEGER,
|
||||
'description' => 'Repository ID.',
|
||||
'default' => '',
|
||||
'example' => '5e5ea5c16897e',
|
||||
])
|
||||
->addRule('name', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Repository Name.',
|
||||
'default' => '',
|
||||
'example' => 'appwrite',
|
||||
])
|
||||
->addRule('owner', [
|
||||
'type' => self::TYPE_JSON,
|
||||
'description' => 'Repository Owner.',
|
||||
'default' => '',
|
||||
'example' => '{"login": "Example Owner"}',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Repository';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_REPOSITORY;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user