From 37c0cc122a788e9fee15bb4696e70cc1ae287a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Fri, 28 Jul 2023 10:27:16 +0200 Subject: [PATCH] More PR review changes --- app/config/collections.php | 21 - app/config/errors.php | 15 - app/controllers/api/proxy.php | 8 +- app/controllers/api/vcs.php | 378 +++++++++--------- app/controllers/general.php | 2 +- src/Appwrite/Extend/Exception.php | 5 +- .../Database/Validator/Queries/Executions.php | 3 - 7 files changed, 194 insertions(+), 238 deletions(-) diff --git a/app/config/collections.php b/app/config/collections.php index 42712cf375..3074b8442c 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -3662,27 +3662,6 @@ $collections = [ 'lengths' => [128], 'orders' => [Database::ORDER_ASC], ], - [ - '$id' => ID::custom('_key_agent'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['agent'], - 'lengths' => [2048], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_path'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['path'], - 'lengths' => [2048], - 'orders' => [Database::ORDER_ASC], - ], - [ - '$id' => ID::custom('_key_method'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['method'], - 'lengths' => [128], - 'orders' => [Database::ORDER_ASC], - ], [ '$id' => ID::custom('_key_status'), 'type' => Database::INDEX_KEY, diff --git a/app/config/errors.php b/app/config/errors.php index e4382aafe0..9bf400fa0c 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -546,26 +546,11 @@ return [ 'description' => 'The project key has expired. Please generate a new key using the Appwrite console.', 'code' => 401, ], - Exception::ROUTER_INVALID_TYPE => [ - 'name' => Exception::ROUTER_INVALID_TYPE, - 'description' => 'Invalid domain configuration. Route type is not supported.', - 'code' => 400, - ], Exception::ROUTER_UNKNOWN_HOST => [ 'name' => Exception::ROUTER_UNKNOWN_HOST, 'description' => 'Host is not trusted. Add a custom domain to your project first.', 'code' => 400, ], - Exception::RULE_RESOURCE_ID_MISSING => [ - 'name' => Exception::RULE_RESOURCE_ID_MISSING, - 'description' => 'With resourceType you provided, the resourceId is required.', - 'code' => 400, - ], - Exception::RULE_CONFIGURATION_MISSING => [ - 'name' => Exception::RULE_CONFIGURATION_MISSING, - 'description' => '_APP_DOMAIN_TARGET must be a public domain.', - 'code' => 501, - ], Exception::RULE_RESOURCE_NOT_FOUND => [ 'name' => Exception::RULE_RESOURCE_NOT_FOUND, 'description' => 'Resource could not be found. Check resourceId and resourceType.', diff --git a/app/controllers/api/proxy.php b/app/controllers/api/proxy.php index a77580c26a..9e3d287f80 100644 --- a/app/controllers/api/proxy.php +++ b/app/controllers/api/proxy.php @@ -67,7 +67,7 @@ App::post('/v1/proxy/rules') if ($resourceType == 'function') { if (empty($resourceId)) { - throw new Exception(Exception::RULE_RESOURCE_ID_MISSING); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $function = $dbForProject->getDocument('functions', $resourceId); @@ -238,9 +238,7 @@ App::delete('/v1/proxy/rules/:ruleId') throw new Exception(Exception::RULE_NOT_FOUND); } - if (!$dbForConsole->deleteDocument('rules', $rule->getId())) { - throw new Exception(Exception::RULE_CONFIGURATION_MISSING); - } + $dbForConsole->deleteDocument('rules', $rule->getId()); $deletes ->setType(DELETE_TYPE_DOCUMENT) @@ -279,7 +277,7 @@ App::patch('/v1/proxy/rules/:ruleId/verification') $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); if (!$target->isKnown() || $target->isTest()) { - throw new Exception(Exception::RULE_CONFIGURATION_MISSING); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Domain target must be configured as environment variable.'); } if ($rule->getAttribute('verification') === true) { diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 8e28d39935..651435d2ad 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -37,6 +37,195 @@ use Utopia\Validator\Boolean; use function Swoole\Coroutine\batch; +$createGitDeployments = function (GitHub $github, string $installationId, array $vcsRepos, string $branchName, string $vcsCommitHash, string $pullRequest, bool $external, Database $dbForConsole, callable $getProjectDB, Request $request) { + foreach ($vcsRepos as $resource) { + $resourceType = $resource->getAttribute('resourceType'); + + if ($resourceType === "function") { + $projectId = $resource->getAttribute('projectId'); + $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + $dbForProject = $getProjectDB($project); + + $functionId = $resource->getAttribute('resourceId'); + $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); + + $deploymentId = ID::unique(); + $vcsRepoId = $resource->getId(); + $vcsRepoInternalId = $resource->getInternalId(); + $repositoryId = $resource->getAttribute('repositoryId'); + $vcsInstallationId = $resource->getAttribute('vcsInstallationId'); + $vcsInstallationInternalId = $resource->getAttribute('vcsInstallationInternalId'); + $productionBranch = $function->getAttribute('vcsBranch'); + $activate = false; + + if ($branchName == $productionBranch && $external === false) { + $activate = true; + } + + $latestCommentId = ''; + + if (!empty($pullRequest)) { + $latestComment = Authorization::skip(fn () => $dbForConsole->findOne('vcsComments', [ + Query::equal('vcsInstallationInternalId', [$vcsInstallationInternalId]), + Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('repositoryId', [$repositoryId]), + Query::equal('pullRequestId', [$pullRequest]), + Query::orderDesc('$createdAt'), + ])); + + if ($latestComment !== false && !$latestComment->isEmpty()) { + $latestCommentId = $latestComment->getAttribute('commentId', ''); + } + } elseif (!empty($branchName)) { + $latestComment = Authorization::skip(fn () => $dbForConsole->findOne('vcsComments', [ + Query::equal('vcsInstallationInternalId', [$vcsInstallationInternalId]), + Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('repositoryId', [$repositoryId]), + Query::equal('branch', [$branchName]), + Query::orderDesc('$createdAt'), + ])); + + if ($latestComment !== false && !$latestComment->isEmpty()) { + $latestCommentId = $latestComment->getAttribute('commentId', ''); + } + } + + $owner = $github->getOwnerName($installationId) ?? ''; + $repositoryName = $github->getRepositoryName($repositoryId) ?? ''; + + if (empty($repositoryName)) { + throw new Exception(Exception::REPOSITORY_NOT_FOUND); + } + + $isAuthorized = !$external; + + if (!$isAuthorized && !empty($pullRequest)) { + if (\in_array($pullRequest, $resource->getAttribute('pullRequests', []))) { + $isAuthorized = true; + } + } + + $commentStatus = $isAuthorized ? 'waiting' : 'failed'; + + if (empty($latestCommentId)) { + $comment = new Comment(); + $comment->addBuild($project, $function, $commentStatus, $deploymentId); + + if (!empty($pullRequest)) { + $latestCommentId = \strval($github->createComment($owner, $repositoryName, $pullRequest, $comment->generateComment())); + } elseif (!empty($branchName)) { + $gitPullRequest = $github->getBranchPullRequest($owner, $repositoryName, $branchName); + $pullRequest = \strval($gitPullRequest['number'] ?? ''); + if (!empty($pullRequest)) { + $latestCommentId = \strval($github->createComment($owner, $repositoryName, $pullRequest, $comment->generateComment())); + } + } + + if (!empty($latestCommentId)) { + $teamId = $project->getAttribute('teamId', ''); + + $latestComment = Authorization::skip(fn () => $dbForConsole->createDocument('vcsComments', new Document([ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::team(ID::custom($teamId))), + Permission::update(Role::team(ID::custom($teamId), 'owner')), + Permission::update(Role::team(ID::custom($teamId), 'developer')), + Permission::delete(Role::team(ID::custom($teamId), 'owner')), + Permission::delete(Role::team(ID::custom($teamId), 'developer')), + ], + 'vcsInstallationInternalId' => $vcsInstallationInternalId, + 'vcsInstallationId' => $vcsInstallationId, + 'projectInternalId' => $project->getInternalId(), + 'projectId' => $project->getId(), + 'repositoryId' => $repositoryId, + 'branch' => $branchName, + 'pullRequestId' => $pullRequest, + 'commentId' => $latestCommentId + ]))); + } + } else { + $comment = new Comment(); + $comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId)); + $comment->addBuild($project, $function, $commentStatus, $deploymentId); + + $latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment())); + } + + if (!$isAuthorized) { + $functionName = $function->getAttribute('name'); + $projectName = $project->getAttribute('name'); + $name = "{$functionName} ({$projectName})"; + $message = 'Authorization required for external contributor.'; + $vcsTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/git/authorize-contributor?projectId={$projectId}&installationId={$vcsInstallationId}&vcsRepositoryId={$vcsRepoId}&pullRequest={$pullRequest}"; + + $repositoryId = $resource->getAttribute('repositoryId'); + $repositoryName = $github->getRepositoryName($repositoryId); + $owner = $github->getOwnerName($installationId); + $github->updateCommitStatus($repositoryName, $vcsCommitHash, $owner, 'failure', $message, $vcsTargetUrl, $name); + continue; + } + + $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' => $function->getAttribute('entrypoint'), + 'commands' => $function->getAttribute('commands'), + 'type' => 'vcs', + 'vcsInstallationId' => $vcsInstallationId, + 'vcsInstallationInternalId' => $vcsInstallationInternalId, + 'vcsRepositoryId' => $repositoryId, + 'vcsRepositoryDocId' => $vcsRepoId, + 'vcsRepositoryDocInternalId' => $vcsRepoInternalId, + 'vcsCommentId' => \strval($latestCommentId), + 'vcsBranch' => $branchName, + 'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint')]), + 'activate' => $activate, + ])); + + $vcsTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/functions/function-$functionId"; + + if (!empty($vcsCommitHash) && $function->getAttribute('vcsSilentMode', false) === false) { + $functionName = $function->getAttribute('name'); + $projectName = $project->getAttribute('name'); + $name = "{$functionName} ({$projectName})"; + $message = 'Starting...'; + + $repositoryId = $resource->getAttribute('repositoryId'); + $repositoryName = $github->getRepositoryName($repositoryId); + $owner = $github->getOwnerName($installationId); + $github->updateCommitStatus($repositoryName, $vcsCommitHash, $owner, 'pending', $message, $vcsTargetUrl, $name); + } + + $contribution = new Document([]); + if ($external) { + $pullRequestResponse = $github->getPullRequest($owner, $repositoryName, $pullRequest); + + $contribution->setAttribute('ownerName', $pullRequestResponse['head']['repo']['owner']['login']); + $contribution->setAttribute('repositoryName', $pullRequestResponse['head']['repo']['name']); + } + + $buildEvent = new Build(); + $buildEvent + ->setType(BUILD_TYPE_DEPLOYMENT) + ->setResource($function) + ->setVcsContribution($contribution) + ->setDeployment($deployment) + ->setVcsTargetUrl($vcsTargetUrl) + ->setVcsCommitHash($vcsCommitHash) + ->setProject($project) + ->trigger(); + + //TODO: Add event? + } + } +}; + App::get('/v1/vcs/github/installations') ->desc('Install GitHub App') ->groups(['api', 'vcs']) @@ -515,195 +704,6 @@ App::get('/v1/vcs/github/installations/:installationId/repositories/:repositoryI ]), Response::MODEL_BRANCH_LIST); }); -$createGitDeployments = function (GitHub $github, string $installationId, array $vcsRepos, string $branchName, string $vcsCommitHash, string $pullRequest, bool $external, Database $dbForConsole, callable $getProjectDB, Request $request) { - foreach ($vcsRepos as $resource) { - $resourceType = $resource->getAttribute('resourceType'); - - if ($resourceType === "function") { - $projectId = $resource->getAttribute('projectId'); - $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); - $dbForProject = $getProjectDB($project); - - $functionId = $resource->getAttribute('resourceId'); - $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); - - $deploymentId = ID::unique(); - $vcsRepoId = $resource->getId(); - $vcsRepoInternalId = $resource->getInternalId(); - $repositoryId = $resource->getAttribute('repositoryId'); - $vcsInstallationId = $resource->getAttribute('vcsInstallationId'); - $vcsInstallationInternalId = $resource->getAttribute('vcsInstallationInternalId'); - $productionBranch = $function->getAttribute('vcsBranch'); - $activate = false; - - if ($branchName == $productionBranch && $external === false) { - $activate = true; - } - - $latestCommentId = ''; - - if (!empty($pullRequest)) { - $latestComment = Authorization::skip(fn () => $dbForConsole->findOne('vcsComments', [ - Query::equal('vcsInstallationInternalId', [$vcsInstallationInternalId]), - Query::equal('projectInternalId', [$project->getInternalId()]), - Query::equal('repositoryId', [$repositoryId]), - Query::equal('pullRequestId', [$pullRequest]), - Query::orderDesc('$createdAt'), - ])); - - if ($latestComment !== false && !$latestComment->isEmpty()) { - $latestCommentId = $latestComment->getAttribute('commentId', ''); - } - } elseif (!empty($branchName)) { - $latestComment = Authorization::skip(fn () => $dbForConsole->findOne('vcsComments', [ - Query::equal('vcsInstallationInternalId', [$vcsInstallationInternalId]), - Query::equal('projectInternalId', [$project->getInternalId()]), - Query::equal('repositoryId', [$repositoryId]), - Query::equal('branch', [$branchName]), - Query::orderDesc('$createdAt'), - ])); - - if ($latestComment !== false && !$latestComment->isEmpty()) { - $latestCommentId = $latestComment->getAttribute('commentId', ''); - } - } - - $owner = $github->getOwnerName($installationId) ?? ''; - $repositoryName = $github->getRepositoryName($repositoryId) ?? ''; - - if (empty($repositoryName)) { - throw new Exception(Exception::REPOSITORY_NOT_FOUND); - } - - $isAuthorized = !$external; - - if (!$isAuthorized && !empty($pullRequest)) { - if (\in_array($pullRequest, $resource->getAttribute('pullRequests', []))) { - $isAuthorized = true; - } - } - - $commentStatus = $isAuthorized ? 'waiting' : 'failed'; - - if (empty($latestCommentId)) { - $comment = new Comment(); - $comment->addBuild($project, $function, $commentStatus, $deploymentId); - - if (!empty($pullRequest)) { - $latestCommentId = \strval($github->createComment($owner, $repositoryName, $pullRequest, $comment->generateComment())); - } elseif (!empty($branchName)) { - $gitPullRequest = $github->getBranchPullRequest($owner, $repositoryName, $branchName); - $pullRequest = \strval($gitPullRequest['number'] ?? ''); - if (!empty($pullRequest)) { - $latestCommentId = \strval($github->createComment($owner, $repositoryName, $pullRequest, $comment->generateComment())); - } - } - - if (!empty($latestCommentId)) { - $teamId = $project->getAttribute('teamId', ''); - - $latestComment = Authorization::skip(fn () => $dbForConsole->createDocument('vcsComments', new Document([ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::team(ID::custom($teamId))), - Permission::update(Role::team(ID::custom($teamId), 'owner')), - Permission::update(Role::team(ID::custom($teamId), 'developer')), - Permission::delete(Role::team(ID::custom($teamId), 'owner')), - Permission::delete(Role::team(ID::custom($teamId), 'developer')), - ], - 'vcsInstallationInternalId' => $vcsInstallationInternalId, - 'vcsInstallationId' => $vcsInstallationId, - 'projectInternalId' => $project->getInternalId(), - 'projectId' => $project->getId(), - 'repositoryId' => $repositoryId, - 'branch' => $branchName, - 'pullRequestId' => $pullRequest, - 'commentId' => $latestCommentId - ]))); - } - } else { - $comment = new Comment(); - $comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId)); - $comment->addBuild($project, $function, $commentStatus, $deploymentId); - - $latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment())); - } - - if (!$isAuthorized) { - $functionName = $function->getAttribute('name'); - $projectName = $project->getAttribute('name'); - $name = "{$functionName} ({$projectName})"; - $message = 'Authorization required for external contributor.'; - $vcsTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/git/authorize-contributor?projectId={$projectId}&installationId={$vcsInstallationId}&vcsRepositoryId={$vcsRepoId}&pullRequest={$pullRequest}"; - - $repositoryId = $resource->getAttribute('repositoryId'); - $repositoryName = $github->getRepositoryName($repositoryId); - $owner = $github->getOwnerName($installationId); - $github->updateCommitStatus($repositoryName, $vcsCommitHash, $owner, 'failure', $message, $vcsTargetUrl, $name); - continue; - } - - $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' => $function->getAttribute('entrypoint'), - 'commands' => $function->getAttribute('commands'), - 'type' => 'vcs', - 'vcsInstallationId' => $vcsInstallationId, - 'vcsInstallationInternalId' => $vcsInstallationInternalId, - 'vcsRepositoryId' => $repositoryId, - 'vcsRepositoryDocId' => $vcsRepoId, - 'vcsRepositoryDocInternalId' => $vcsRepoInternalId, - 'vcsCommentId' => \strval($latestCommentId), - 'vcsBranch' => $branchName, - 'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint')]), - 'activate' => $activate, - ])); - - $vcsTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/functions/function-$functionId"; - - if (!empty($vcsCommitHash) && $function->getAttribute('vcsSilentMode', false) === false) { - $functionName = $function->getAttribute('name'); - $projectName = $project->getAttribute('name'); - $name = "{$functionName} ({$projectName})"; - $message = 'Starting...'; - - $repositoryId = $resource->getAttribute('repositoryId'); - $repositoryName = $github->getRepositoryName($repositoryId); - $owner = $github->getOwnerName($installationId); - $github->updateCommitStatus($repositoryName, $vcsCommitHash, $owner, 'pending', $message, $vcsTargetUrl, $name); - } - - $contribution = new Document([]); - if ($external) { - $pullRequestResponse = $github->getPullRequest($owner, $repositoryName, $pullRequest); - - $contribution->setAttribute('ownerName', $pullRequestResponse['head']['repo']['owner']['login']); - $contribution->setAttribute('repositoryName', $pullRequestResponse['head']['repo']['name']); - } - - $buildEvent = new Build(); - $buildEvent - ->setType(BUILD_TYPE_DEPLOYMENT) - ->setResource($function) - ->setVcsContribution($contribution) - ->setDeployment($deployment) - ->setVcsTargetUrl($vcsTargetUrl) - ->setVcsCommitHash($vcsCommitHash) - ->setProject($project) - ->trigger(); - - //TODO: Add event? - } - } -}; - App::post('/v1/vcs/github/events') ->desc('Create Event') ->groups(['api', 'vcs']) diff --git a/app/controllers/general.php b/app/controllers/general.php index af0c733470..87c0b475cc 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -145,7 +145,7 @@ function router(App $utopia, Database $dbForConsole, SwooleRequest $swooleReques } elseif ($type === 'api') { return false; } else { - throw new AppwriteException(AppwriteException::ROUTER_INVALID_TYPE); + throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Unknown resource type ' . $type); } return false; diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index b000dd1dfe..d63436026c 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -173,12 +173,9 @@ class Exception extends \Exception /** Router */ public const ROUTER_UNKNOWN_HOST = 'router_unknown_host'; - public const ROUTER_INVALID_TYPE = 'router_unknown_type'; /** Proxy */ - public const RULE_CONFIGURATION_MISSING = 'rule_configuration_missing'; - public const RULE_RESOURCE_ID_MISSING = 'rule_resource_id_missing'; - public const RULE_RESOURCE_NOT_FOUND = 'rule_resource_not_found'; + public const RULE_RESOURCE_NOT_FOUND = 'rule_resource_not_found'; public const RULE_NOT_FOUND = 'rule_not_found'; public const RULE_ALREADY_EXISTS = 'rule_already_exists'; public const RULE_VERIFICATION_FAILED = 'rule_verification_failed'; diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Executions.php b/src/Appwrite/Utopia/Database/Validator/Queries/Executions.php index 30f9674264..2bfe46e28d 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Executions.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Executions.php @@ -6,9 +6,6 @@ class Executions extends Base { public const ALLOWED_ATTRIBUTES = [ 'trigger', - 'method', - 'path', - 'agent', 'status', 'statusCode', 'duration'