Copy Khushboo's integration from feat-peach-q1-kh
This commit is contained in:
parent
6766508c95
commit
6e515e3cc4
5
.env
5
.env
|
@ -80,4 +80,7 @@ _APP_REGION=default
|
|||
_APP_DOCKER_HUB_USERNAME=
|
||||
_APP_DOCKER_HUB_PASSWORD=
|
||||
_APP_CONSOLE_GITHUB_SECRET=
|
||||
_APP_CONSOLE_GITHUB_APP_ID=
|
||||
_APP_CONSOLE_GITHUB_APP_ID=
|
||||
VCS_GITHUB_APP_NAME=
|
||||
VCS_GITHUB_PRIVATE_KEY=
|
||||
VCS_GITHUB_APP_ID=
|
|
@ -1007,7 +1007,7 @@ $collections = [
|
|||
[
|
||||
'$id' => ID::custom('_key_region_resourceType_resourceUpdatedAt'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['region', 'resourceType','resourceUpdatedAt'],
|
||||
'attributes' => ['region', 'resourceType', 'resourceUpdatedAt'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
|
@ -2221,6 +2221,178 @@ $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' => [],
|
||||
],
|
||||
|
||||
'functions' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('functions'),
|
||||
|
@ -2258,6 +2430,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('logging'),
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
|
@ -2445,6 +2659,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,
|
||||
|
@ -2601,6 +2829,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,
|
||||
|
@ -3549,7 +3848,7 @@ $collections = [
|
|||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => '_key_accessedAt',
|
||||
|
@ -3878,7 +4177,7 @@ $collections = [
|
|||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [ 'encrypt' ]
|
||||
'filters' => ['encrypt']
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('search'),
|
||||
|
|
|
@ -324,6 +324,13 @@ return [
|
|||
'code' => 416,
|
||||
],
|
||||
|
||||
/** VCS */
|
||||
Exception::INSTALLATION_NOT_FOUND => [
|
||||
'name' => Exception::INSTALLATION_NOT_FOUND,
|
||||
'description' => 'Installation with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
|
||||
/** Functions */
|
||||
Exception::FUNCTION_NOT_FOUND => [
|
||||
'name' => Exception::FUNCTION_NOT_FOUND,
|
||||
|
|
|
@ -160,6 +160,19 @@ return [
|
|||
'optional' => true,
|
||||
'icon' => '/images/services/users.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' => '',
|
||||
],
|
||||
'functions' => [
|
||||
'key' => 'functions',
|
||||
'name' => 'Functions',
|
||||
|
|
|
@ -80,6 +80,7 @@ App::post('/v1/functions')
|
|||
->inject('events')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $functionId, string $name, array $execute, string $runtime, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $buildCommand, string $installCommand, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance, Database $dbForConsole) {
|
||||
// TODO: Add support to link to GitHub repos from createFunction as well
|
||||
|
||||
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
|
||||
$function = $dbForProject->createDocument('functions', new Document([
|
||||
|
@ -522,13 +523,17 @@ App::put('/v1/functions/:functionId')
|
|||
->param('entrypoint', '', new Text('1028'), 'Entrypoint File.', true)
|
||||
->param('buildCommand', '', new Text('1028'), 'Build Command.', true)
|
||||
->param('installCommand', '', new Text('1028'), 'Install Command.', 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')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $functionId, string $name, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $buildCommand, string $installCommand, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance, Database $dbForConsole) {
|
||||
->action(function (string $functionId, string $name, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $buildCommand, string $installCommand, string $vcsInstallationId, string $repositoryId, string $repositoryOwner, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance, Database $dbForConsole) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
|
@ -536,8 +541,92 @@ 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);
|
||||
}
|
||||
|
||||
$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,
|
||||
|
@ -549,6 +638,10 @@ App::put('/v1/functions/:functionId')
|
|||
'entrypoint' => $entrypoint,
|
||||
'buildCommand' => $buildCommand,
|
||||
'installCommand' => $installCommand,
|
||||
'vcsInstallationId' => $installation->getId(),
|
||||
'vcsInstallationInternalId' => $installation->getInternalId(),
|
||||
'vcsRepoId' => $vcsRepoId,
|
||||
'vcsRepoInternalId' => $vcsRepoInternalId,
|
||||
'search' => implode(' ', [$functionId, $name, $function->getAttribute('runtime')]),
|
||||
])));
|
||||
|
||||
|
@ -563,6 +656,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);
|
||||
});
|
||||
|
||||
|
|
481
app/controllers/api/vcs.php
Normal file
481
app/controllers/api/vcs.php
Normal file
|
@ -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?
|
||||
}
|
||||
}
|
||||
} elseif ($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());
|
||||
}
|
||||
}
|
||||
} elseif ($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();
|
||||
});
|
|
@ -152,6 +152,7 @@ const DELETE_TYPE_ABUSE = 'abuse';
|
|||
const DELETE_TYPE_USAGE = 'usage';
|
||||
const DELETE_TYPE_REALTIME = 'realtime';
|
||||
const DELETE_TYPE_BUCKETS = 'buckets';
|
||||
const DELETE_TYPE_INSTALLATIONS = 'vcs_installations';
|
||||
const DELETE_TYPE_RULES = 'rules';
|
||||
const DELETE_TYPE_SESSIONS = 'sessions';
|
||||
const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp';
|
||||
|
|
|
@ -16,6 +16,8 @@ use Utopia\Database\Document;
|
|||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
||||
require_once __DIR__ . '/../init.php';
|
||||
|
||||
|
@ -43,12 +45,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,11 +62,12 @@ 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 = '')
|
||||
{
|
||||
global $register;
|
||||
|
||||
$dbForProject = $this->getProjectDB($project);
|
||||
$dbForConsole = $this->getConsoleDB();
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $function->getId());
|
||||
if ($function->isEmpty()) {
|
||||
|
@ -80,7 +86,8 @@ class BuildsV1 extends Worker
|
|||
throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported');
|
||||
}
|
||||
|
||||
$connection = App::getEnv('_APP_CONNECTIONS_STORAGE', ''); /** @TODO : move this to the registry or someplace else */
|
||||
$connection = App::getEnv('_APP_CONNECTIONS_STORAGE', '');
|
||||
/** @TODO : move this to the registry or someplace else */
|
||||
$device = Storage::DEVICE_LOCAL;
|
||||
try {
|
||||
$dsn = new DSN($connection);
|
||||
|
@ -94,6 +101,94 @@ class BuildsV1 extends Worker
|
|||
$durationStart = \microtime(true);
|
||||
if (empty($buildId)) {
|
||||
$buildId = ID::unique();
|
||||
|
||||
$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
|
||||
]));
|
||||
}
|
||||
|
||||
$build = $dbForProject->createDocument('builds', new Document([
|
||||
'$id' => $buildId,
|
||||
'$permissions' => [],
|
||||
|
@ -122,6 +217,14 @@ class BuildsV1 extends Worker
|
|||
$build->setAttribute('status', 'building');
|
||||
$build = $dbForProject->updateDocument('builds', $buildId, $build);
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$commentId = $deployment->getAttribute('vcsCommentId');
|
||||
if ($commentId) {
|
||||
$comment = "| Build Status |\r\n | --------------- |\r\n | Building |";
|
||||
$github->updateComment($owner, $repositoryName, $commentId, $comment);
|
||||
}
|
||||
}
|
||||
|
||||
/** Trigger Webhook */
|
||||
$deploymentModel = new Deployment();
|
||||
|
||||
|
@ -167,6 +270,10 @@ class BuildsV1 extends Worker
|
|||
|
||||
$source = $deployment->getAttribute('path');
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$source = $path;
|
||||
}
|
||||
|
||||
$vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) {
|
||||
$carry[$var->getAttribute('key')] = $var->getAttribute('value');
|
||||
return $carry;
|
||||
|
@ -214,6 +321,22 @@ class BuildsV1 extends Worker
|
|||
$build->setAttribute('stderr', $response['stderr']);
|
||||
$build->setAttribute('stdout', $response['stdout']);
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$status = $response["status"];
|
||||
|
||||
if ($status === "ready" && $SHA !== "" && $owner !== "") {
|
||||
$github->updateCommitStatus($repositoryName, $SHA, $owner, "success", "Deployment is successful!", $targetUrl, "Appwrite Deployment");
|
||||
} elseif ($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']);
|
||||
|
||||
|
|
|
@ -64,6 +64,10 @@ class DeletesV1 extends Worker
|
|||
break;
|
||||
case DELETE_TYPE_BUCKETS:
|
||||
$this->deleteBucket($document, $project);
|
||||
break;
|
||||
case DELETE_TYPE_INSTALLATIONS:
|
||||
$this->deleteInstallation($document, $project);
|
||||
break;
|
||||
case DELETE_TYPE_RULES:
|
||||
$this->deleteRule($document, $project);
|
||||
break;
|
||||
|
@ -329,7 +333,7 @@ class DeletesV1 extends Worker
|
|||
'teams',
|
||||
$teamId,
|
||||
// Ensure that total >= 0
|
||||
$team->setAttribute('total', \max($team->getAttribute('total', 0) - 1, 0))
|
||||
$team->setAttribute('total', \max($team->getAttribute('total', 0) - 1, 0))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -626,6 +630,43 @@ class DeletesV1 extends Worker
|
|||
Console::info("Found {$count} projects " . ($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 string $collection collectionID
|
||||
* @param Query[] $queries
|
||||
|
@ -737,6 +778,25 @@ class DeletesV1 extends Worker
|
|||
$device->deletePath($document->getId());
|
||||
}
|
||||
|
||||
protected function deleteInstallation(Document $document, Document $project)
|
||||
{
|
||||
$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);
|
||||
});
|
||||
}
|
||||
|
||||
protected function deleteRuntimes(?Document $function, Document $project)
|
||||
{
|
||||
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/storage": "0.13.*",
|
||||
"utopia-php/swoole": "0.5.*",
|
||||
"utopia-php/vcs": "dev-feat-git-adapter as 0.1.99",
|
||||
"utopia-php/websocket": "0.1.0",
|
||||
"resque/php-resque": "1.3.6",
|
||||
"matomo/device-detector": "6.0.0",
|
||||
|
@ -79,6 +80,10 @@
|
|||
{
|
||||
"url": "https://github.com/appwrite/runtimes.git",
|
||||
"type": "git"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/utopia-php/vcs.git",
|
||||
"type": "git"
|
||||
}
|
||||
],
|
||||
"require-dev": {
|
||||
|
|
|
@ -177,6 +177,9 @@ services:
|
|||
- _APP_REGION
|
||||
- _APP_CONSOLE_GITHUB_APP_ID
|
||||
- _APP_CONSOLE_GITHUB_SECRET
|
||||
- VCS_GITHUB_APP_NAME
|
||||
- VCS_GITHUB_PRIVATE_KEY
|
||||
- VCS_GITHUB_APP_ID
|
||||
|
||||
appwrite-realtime:
|
||||
entrypoint: realtime
|
||||
|
@ -382,7 +385,9 @@ services:
|
|||
image: appwrite-dev
|
||||
networks:
|
||||
- appwrite
|
||||
volumes:
|
||||
volumes:
|
||||
- appwrite-functions:/storage/functions:rw
|
||||
- appwrite-builds:/storage/builds:rw
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
depends_on:
|
||||
|
@ -412,6 +417,9 @@ services:
|
|||
- _APP_CONNECTIONS_STORAGE
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
- VCS_GITHUB_APP_NAME
|
||||
- VCS_GITHUB_PRIVATE_KEY
|
||||
- VCS_GITHUB_APP_ID
|
||||
|
||||
appwrite-worker-certificates:
|
||||
entrypoint: worker-certificates
|
||||
|
|
|
@ -10,12 +10,54 @@ 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()
|
||||
{
|
||||
parent::__construct(Event::BUILDS_QUEUE_NAME, Event::BUILDS_CLASS_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets resource document for the build event.
|
||||
*
|
||||
|
@ -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
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,6 +106,9 @@ class Exception extends \Exception
|
|||
public const STORAGE_INVALID_CONTENT_RANGE = 'storage_invalid_content_range';
|
||||
public const STORAGE_INVALID_RANGE = 'storage_invalid_range';
|
||||
|
||||
/** VCS */
|
||||
public const INSTALLATION_NOT_FOUND = 'installation_not_found';
|
||||
|
||||
/** Functions */
|
||||
public const FUNCTION_NOT_FOUND = 'function_not_found';
|
||||
public const FUNCTION_RUNTIME_UNSUPPORTED = 'function_runtime_unsupported';
|
||||
|
|
|
@ -334,6 +334,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':
|
||||
|
|
|
@ -328,6 +328,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);
|
||||
}
|
||||
}
|
|
@ -70,6 +70,8 @@ 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\Repository;
|
||||
use Appwrite\Utopia\Response\Model\Mock; // Keep last
|
||||
use Appwrite\Utopia\Response\Model\Provider;
|
||||
use Appwrite\Utopia\Response\Model\Runtime;
|
||||
|
@ -176,6 +178,12 @@ class Response extends SwooleResponse
|
|||
public const MODEL_MEMBERSHIP = 'membership';
|
||||
public const MODEL_MEMBERSHIP_LIST = 'membershipList';
|
||||
|
||||
// VCS
|
||||
public const MODEL_INSTALLATION = 'installation';
|
||||
public const MODEL_INSTALLATION_LIST = 'installationList';
|
||||
public const MODEL_REPOSITORY = 'repository';
|
||||
public const MODEL_REPOSITORY_LIST = 'repositoryList';
|
||||
|
||||
// Functions
|
||||
public const MODEL_FUNCTION = 'function';
|
||||
public const MODEL_FUNCTION_LIST = 'functionList';
|
||||
|
@ -207,6 +215,7 @@ class Response extends SwooleResponse
|
|||
public const MODEL_PLATFORM_LIST = 'platformList';
|
||||
public const MODEL_VARIABLE = 'variable';
|
||||
public const MODEL_VARIABLE_LIST = 'variableList';
|
||||
public const MODEL_VCS = 'vcs';
|
||||
|
||||
// Health
|
||||
public const MODEL_HEALTH_STATUS = 'healthStatus';
|
||||
|
@ -260,6 +269,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))
|
||||
|
@ -314,6 +325,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())
|
||||
|
|
|
@ -111,6 +111,18 @@ class Func extends Model
|
|||
'default' => '',
|
||||
'example' => 'npm install',
|
||||
])
|
||||
->addRule('repositoryId', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'VCS Repository ID',
|
||||
'default' => false,
|
||||
'example' => '35493993',
|
||||
])
|
||||
->addRule('vcsInstallationId', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Function vcs installation id.',
|
||||
'default' => '',
|
||||
'example' => '644051bd6572792165cc',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
|
|
72
src/Appwrite/Utopia/Response/Model/Installation.php
Normal file
72
src/Appwrite/Utopia/Response/Model/Installation.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
52
src/Appwrite/Utopia/Response/Model/Repository.php
Normal file
52
src/Appwrite/Utopia/Response/Model/Repository.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue