Implement branches endpoint, rename some attributes, update response models

This commit is contained in:
Matej Bačo
2023-05-26 00:29:08 +02:00
parent 4665c2a2a4
commit f53d75205b
16 changed files with 187 additions and 64 deletions
+20 -10
View File
@@ -2423,7 +2423,7 @@ $collections = [
'$id' => ID::custom('vcsInstallationId'),
'type' => Database::VAR_STRING,
'signed' => true,
'size' => 2048,
'size' => Database::LENGTH_KEY,
'format' => '',
'filters' => [],
'required' => false,
@@ -2441,17 +2441,27 @@ $collections = [
'filters' => [],
],
[
'$id' => ID::custom('vcsRepoId'),
'$id' => ID::custom('repositoryId'),
'type' => Database::VAR_STRING,
'signed' => true,
'size' => 2048,
'size' => Database::LENGTH_KEY,
'format' => '',
'filters' => [],
'required' => false,
'array' => false,
],
[
'$id' => ID::custom('vcsRepoInternalId'),
'$id' => ID::custom('vcsRepositoryId'),
'type' => Database::VAR_STRING,
'signed' => true,
'size' => Database::LENGTH_KEY,
'format' => '',
'filters' => [],
'required' => false,
'array' => false,
],
[
'$id' => ID::custom('vcsRepositoryInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
@@ -2662,14 +2672,14 @@ $collections = [
'$id' => ID::custom('_key_vcsInstallationId'),
'type' => Database::INDEX_KEY,
'attributes' => ['vcsInstallationId'],
'lengths' => [768],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_vcsRepoId'),
'$id' => ID::custom('_key_vcsRepositoryId'),
'type' => Database::INDEX_KEY,
'attributes' => ['vcsRepoId'],
'lengths' => [768],
'attributes' => ['vcsRepositoryId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
@@ -2860,7 +2870,7 @@ $collections = [
'array' => false,
],
[
'$id' => ID::custom('vcsRepoId'),
'$id' => ID::custom('vcsRepositoryId'),
'type' => Database::VAR_STRING,
'signed' => true,
'size' => Database::LENGTH_KEY,
@@ -2870,7 +2880,7 @@ $collections = [
'array' => false,
],
[
'$id' => ID::custom('vcsRepoInternalId'),
'$id' => ID::custom('vcsRepositoryInternalId'),
'type' => Database::VAR_STRING,
'signed' => true,
'size' => Database::LENGTH_KEY,
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
+8 -6
View File
@@ -533,6 +533,7 @@ App::put('/v1/functions/:functionId')
->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, string $vcsInstallationId, string $repositoryId, string $branch, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance, Database $dbForConsole) {
// TODO: If only branch changes, re-deploy
$function = $dbForProject->getDocument('functions', $functionId);
@@ -565,7 +566,7 @@ App::put('/v1/functions/:functionId')
$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', '');
$prevVcsRepoId = $function->getAttribute('vcsRepositoryId', '');
if (empty($prevVcsRepoId) && !empty($repositoryId)) {
$needToDeploy = true;
}
@@ -610,8 +611,8 @@ App::put('/v1/functions/:functionId')
'type' => 'vcs',
'vcsInstallationId' => $installation->getId(),
'vcsInstallationInternalId' => $installation->getInternalId(),
'vcsRepoId' => $vcsRepoId,
'vcsRepoInternalId' => $vcsRepoInternalId,
'vcsRepositoryId' => $vcsRepoId,
'vcsRepositoryInternalId' => $vcsRepoInternalId,
'branch' => $branch,
'search' => implode(' ', [$deploymentId, $entrypoint]),
'activate' => true,
@@ -638,9 +639,10 @@ App::put('/v1/functions/:functionId')
'installCommand' => $installCommand,
'vcsInstallationId' => $installation->getId(),
'vcsInstallationInternalId' => $installation->getInternalId(),
'vcsRepoId' => $vcsRepoId ?? $function->getAttribute('vcsRepoId', ''),
'vcsRepoInternalId' => $vcsRepoInternalId ?? $function->getAttribute('vcsRepoInternalId', ''),
'branch'=> $branch,
'repositoryId' => $repositoryId,
'vcsRepositoryId' => $vcsRepoId ?? $function->getAttribute('vcsRepositoryId', ''),
'vcsRepositoryInternalId' => $vcsRepoInternalId ?? $function->getAttribute('vcsRepositoryInternalId', ''),
'branch' => $branch,
'search' => implode(' ', [$functionId, $name, $function->getAttribute('runtime')]),
])));
+86 -22
View File
@@ -10,6 +10,7 @@ use Appwrite\Utopia\Response;
use Utopia\Validator\Text;
use Utopia\VCS\Adapter\Git\GitHub;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Host;
use Appwrite\Utopia\Database\Validator\Queries\Installations;
use Utopia\Database\Query;
use Utopia\Database\ID;
@@ -29,37 +30,59 @@ App::get('/v1/vcs/github/installations')
->label('sdk.response.code', Response::STATUS_CODE_MOVED_PERMANENTLY)
->label('sdk.response.type', Response::CONTENT_TYPE_HTML)
->label('sdk.methodType', 'webAuth')
->param('redirect', '', fn($clients) => new Host($clients), 'URL to redirect back to your Git authorization. Only console hostnames are allowed.', true, ['clients'])
->inject('response')
->inject('project')
->action(function (Response $response, Document $project) {
->action(function (string $redirect, Response $response, Document $project) {
$projectId = $project->getId();
$state = \json_encode([
'projectId' => $projectId,
'redirect' => $redirect
]);
$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");
->redirect("https://github.com/apps/$appName/installations/new?" . \http_build_query([
'state' => $state
]));
});
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')
->param('installation_id', '', new Text(256), 'GitHub installation ID')
->param('setup_action', '', new Text(256), 'GitHub setup actuon type')
->param('state', '', new Text(2048), 'GitHub state. Contains info sent when starting authorization flow.')
->inject('request')
->inject('response')
->inject('dbForConsole')
->action(function (string $installationId, string $setupAction, string $state, Request $request, Response $response, Database $dbForConsole) {
$state = \json_decode($state, true);
$project = $dbForConsole->getDocument('projects', $state);
$projectId = $state['projectId'] ?? '';
$redirect = $state['redirect'] ?? '';
if (empty($redirect)) {
$redirect = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/settings/git-installations";
}
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
$url = $request->getProtocol() . '://' . $request->getHostname() . "/";
$response->redirect($url);
$response->redirect($redirect);
}
$installationId = $installationId;
$privateKey = App::getEnv('VCS_GITHUB_PRIVATE_KEY');
$githubAppId = App::getEnv('VCS_GITHUB_APP_ID');
$github = new GitHub();
$github->initialiseVariables($installationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($installationId);
$projectInternalId = $project->getInternalId();
$vcsInstallation = $dbForConsole->findOne('vcs_installations', [
@@ -76,26 +99,23 @@ App::get('/v1/vcs/github/incominginstallation')
Permission::delete(Role::any()),
],
'installationId' => $installationId,
'projectId' => $state,
'projectId' => $projectId,
'projectInternalId' => $projectInternalId,
'provider' => 'GitHub',
'organization' => '(todo) My Awesome Organization',
'organization' => $owner,
'accessToken' => null
]);
$vcsInstallation = $dbForConsole->createDocument('vcs_installations', $vcsInstallation);
} else {
$vcsInstallation = $vcsInstallation->setAttribute('organization', '(todo) My Awesome Organization');
$vcsInstallation = $vcsInstallation->setAttribute('organization', $owner);
$vcsInstallation = $dbForConsole->updateDocument('vcs_installations', $vcsInstallation->getId(), $vcsInstallation);
}
// TODO: Figure out port
$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);
->redirect($redirect);
});
App::get('v1/vcs/github/installations/:installationId/repositories')
@@ -136,13 +156,15 @@ App::get('v1/vcs/github/installations/:installationId/repositories')
$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);
$repositories = \array_map(function ($repository) {
$repository['id'] = \strval($repository['id']);
return $repository;
}, $repositories);
$repos = array_merge($repos, $repositories);
$page++;
} while ($repositories == $per_page);
} while (\count($repositories) === $per_page);
// Filter repositories based on search parameter
if (!empty($search)) {
@@ -166,6 +188,48 @@ App::get('v1/vcs/github/installations/:installationId/repositories')
]), Response::MODEL_REPOSITORY_LIST);
});
App::get('v1/vcs/github/installations/:installationId/repositories/:repositoryId/branches')
->desc('List Repository Branches')
->groups(['api', 'vcs'])
->label('scope', 'public')
->label('sdk.namespace', 'vcs')
->label('sdk.method', 'listRepositoryBranches')
->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_BRANCH_LIST)
->param('installationId', '', new Text(256), 'Installation Id')
->param('repositoryId', '', new Text(256), 'Repository Id')
->inject('response')
->inject('project')
->inject('dbForConsole')
->action(function (string $vcsInstallationId, string $repositoryId, Response $response, Document $project, Database $dbForConsole) {
$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);
$owner = $github->getOwnerName($installationId);
$repoName = $github->getRepositoryName($repositoryId);
$branches = $github->listBranches($owner, $repoName);
$response->dynamic(new Document([
'branches' => \array_map(function ($branch) {
return [ 'name' => $branch ];
}, $branches),
'total' => \count($branches),
]), Response::MODEL_BRANCH_LIST);
});
$createGitDeployments = function (array $vcsRepos, string $branchName, string $SHA, Database $dbForConsole, callable $getProjectDB, Request $request) {
foreach ($vcsRepos as $resource) {
$resourceType = $resource->getAttribute('resourceType');
@@ -206,8 +270,8 @@ $createGitDeployments = function (array $vcsRepos, string $branchName, string $S
'type' => 'vcs',
'vcsInstallationId' => $vcsInstallationId,
'vcsInstallationInternalId' => $vcsInstallationInternalId,
'vcsRepoId' => $vcsRepoId,
'vcsRepoInternalId' => $vcsRepoInternalId,
'vcsRepositoryId' => $vcsRepoId,
'vcsRepositoryInternalId' => $vcsRepoInternalId,
'branch' => $branchName,
'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint')]),
'activate' => $activate,
@@ -264,7 +328,7 @@ App::post('/v1/vcs/github/incomingwebhook')
$createGitDeployments($vcsRepos, $branchName, $SHA, $dbForConsole, $getProjectDB, $request);
} elseif ($event == $github::EVENT_INSTALLATION) {
if ($parsedPayload["action"] == "deleted") {
// TODO: Use worker for this job instead
// TODO: Use worker for this job instead (update function as well)
$installationId = $parsedPayload["installationId"];
$vcsInstallations = $dbForConsole->find('vcs_installations', [
@@ -313,7 +377,7 @@ App::post('/v1/vcs/github/incomingwebhook')
$dbForProject = $getProjectDB($project);
$vcsRepoId = $vcsRepo->getId();
$deployment = Authorization::skip(fn () => $dbForProject->findOne('deployments', [
Query::equal('vcsRepoId', [$vcsRepoId]),
Query::equal('vcsRepositoryId', [$vcsRepoId]),
Query::equal('branch', [$branchName]),
Query::orderDesc('$createdAt'),
]));
+1 -1
View File
@@ -101,7 +101,7 @@ class BuildsV1 extends Worker
$buildId = ID::unique();
$vcsInstallationId = $deployment->getAttribute('vcsInstallationId', '');
$vcsRepoId = $deployment->getAttribute('vcsRepoId', '');
$vcsRepoId = $deployment->getAttribute('vcsRepositoryId', '');
$isVcsEnabled = $vcsRepoId ? true : false;
if ($isVcsEnabled) {
+5 -3
View File
@@ -786,13 +786,15 @@ class DeletesV1 extends Worker
$this->listByGroup('functions', [
Query::equal('vcsInstallationInternalId', [$document->getInternalId()])
], $dbForProject, function ($function) use ($dbForProject, $dbForConsole) {
$dbForConsole->deleteDocument('vcs_repos', $function->getAttribute('vcsRepoId'));
$dbForConsole->deleteDocument('vcs_repos', $function->getAttribute('vcsRepositoryId'));
$function = $function
->setAttribute('vcsInstallationId', '')
->setAttribute('vcsInstallationInternalId', '')
->setAttribute('vcsRepoId', '')
->setAttribute('vcsRepoInternalId', '');
->setAttribute('repositoryId', '')
->setAttribute('branch', '')
->setAttribute('vcsRepositoryId', '')
->setAttribute('vcsRepositoryInternalId', '');
$dbForProject->updateDocument('functions', $function->getId(), $function);
});
}
Generated
+2 -2
View File
@@ -3090,7 +3090,7 @@
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
"reference": "a3f5d680e79431b661bde03774b8c4e4d71e6023"
"reference": "de473962979c1ebd9c98952a0c22ce3e29242002"
},
"require": {
"adhocore/jwt": "^1.1",
@@ -3134,7 +3134,7 @@
"utopia",
"vcs"
],
"time": "2023-05-22T12:56:31+00:00"
"time": "2023-05-25T15:25:45+00:00"
},
{
"name": "utopia-php/websocket",
+5
View File
@@ -30,6 +30,7 @@ use Appwrite\Utopia\Response\Model\AttributeIP;
use Appwrite\Utopia\Response\Model\AttributeURL;
use Appwrite\Utopia\Response\Model\AttributeDatetime;
use Appwrite\Utopia\Response\Model\BaseList;
use Appwrite\Utopia\Response\Model\Branch;
use Appwrite\Utopia\Response\Model\Collection;
use Appwrite\Utopia\Response\Model\Database;
use Appwrite\Utopia\Response\Model\Continent;
@@ -183,6 +184,8 @@ class Response extends SwooleResponse
public const MODEL_INSTALLATION_LIST = 'installationList';
public const MODEL_REPOSITORY = 'repository';
public const MODEL_REPOSITORY_LIST = 'repositoryList';
public const MODEL_BRANCH = 'branch';
public const MODEL_BRANCH_LIST = 'branchList';
// Functions
public const MODEL_FUNCTION = 'function';
@@ -271,6 +274,7 @@ class Response extends SwooleResponse
->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('Branches List', self::MODEL_BRANCH_LIST, 'branches', self::MODEL_BRANCH))
->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))
@@ -327,6 +331,7 @@ class Response extends SwooleResponse
->setModel(new Func())
->setModel(new Installation())
->setModel(new Repository())
->setModel(new Branch())
->setModel(new Runtime())
->setModel(new Deployment())
->setModel(new Execution())
@@ -0,0 +1,40 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class Branch extends Model
{
public function __construct()
{
$this
->addRule('name', [
'type' => self::TYPE_STRING,
'description' => 'Repository Name.',
'default' => '',
'example' => 'appwrite',
]);
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Branch';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_BRANCH;
}
}
+12 -6
View File
@@ -111,18 +111,24 @@ class Func extends Model
'default' => '',
'example' => 'npm install',
])
->addRule('repositoryId', [
'type' => self::TYPE_STRING,
'description' => 'VCS Repository ID',
'default' => '',
'example' => '35493993',
])
->addRule('vcsInstallationId', [
'type' => self::TYPE_STRING,
'description' => 'Function vcs installation id.',
'default' => '',
'example' => '644051bd6572792165cc',
])
->addRule('repositoryId', [
'type' => self::TYPE_STRING,
'description' => 'Git Repository ID',
'default' => '',
'example' => 'appwrite',
])
->addRule('branch', [
'type' => self::TYPE_STRING,
'description' => 'Git branch name',
'default' => '',
'example' => 'main',
])
;
}
@@ -11,8 +11,8 @@ class Repository extends Model
{
$this
->addRule('id', [
'type' => self::TYPE_INTEGER,
'description' => 'Repository ID.',
'type' => self::TYPE_STRING,
'description' => 'Git Repository ID.',
'default' => '',
'example' => '5e5ea5c16897e',
])
@@ -21,12 +21,6 @@ class Repository extends Model
'description' => 'Repository Name.',
'default' => '',
'example' => 'appwrite',
])
->addRule('owner', [
'type' => self::TYPE_JSON,
'description' => 'Repository Owner.',
'default' => '',
'example' => '{"login": "Example Owner"}',
]);
}