diff --git a/.env b/.env index feeb74d849..ad551e705a 100644 --- a/.env +++ b/.env @@ -91,7 +91,7 @@ _APP_GRAPHQL_MAX_DEPTH=3 _APP_DOCKER_HUB_USERNAME= _APP_DOCKER_HUB_PASSWORD= _APP_VCS_GITHUB_APP_NAME= -_APP_VCS_GITHUB_PRIVATE_KEY="" +_APP_VCS_GITHUB_PRIVATE_KEY=disabled _APP_VCS_GITHUB_APP_ID= _APP_VCS_GITHUB_CLIENT_ID= _APP_VCS_GITHUB_CLIENT_SECRET= diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ed9d7d1f19..842d61ff1c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -98,6 +98,7 @@ jobs: Teams, Users, Webhooks, + VCS, ] steps: diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 4a96308492..d370a7109c 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -46,6 +46,7 @@ use Utopia\Validator\Boolean; use Utopia\Database\Exception\Duplicate as DuplicateException; use MaxMind\Db\Reader; use Utopia\VCS\Adapter\Git\GitHub; +use Utopia\VCS\Exception\RepositoryNotFound; include_once __DIR__ . '/../shared/api.php'; @@ -58,7 +59,14 @@ $redeployVcs = function (Request $request, Document $function, Document $project $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); $owner = $github->getOwnerName($providerInstallationId); $providerRepositoryId = $function->getAttribute('providerRepositoryId', ''); - $repositoryName = $github->getRepositoryName($providerRepositoryId); + try { + $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; + if (empty($repositoryName)) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } + } catch (RepositoryNotFound $e) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } $providerBranch = $function->getAttribute('providerBranch', 'main'); $authorUrl = "https://github.com/$owner"; $repositoryUrl = "https://github.com/$owner/$repositoryName"; @@ -287,9 +295,8 @@ App::post('/v1/functions') $ruleModel = new Rule(); $ruleCreate = $queueForEvents - ->setClass(Event::WEBHOOK_CLASS_NAME) - ->setQueue(Event::WEBHOOK_QUEUE_NAME) - ; + ->setClass(Event::WEBHOOK_CLASS_NAME) + ->setQueue(Event::WEBHOOK_QUEUE_NAME); $ruleCreate ->setProject($project) @@ -870,8 +877,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download') ->setContentType('application/gzip') ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache ->addHeader('X-Peak', \memory_get_peak_usage()) - ->addHeader('Content-Disposition', 'attachment; filename="' . $deploymentId . '.tar.gz"') - ; + ->addHeader('Content-Disposition', 'attachment; filename="' . $deploymentId . '.tar.gz"'); $size = $deviceFunctions->getFileSize($path); $rangeHeader = $request->getHeader('range'); diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 55602136fc..8b61580b76 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -22,7 +22,6 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; -use Utopia\Database\Validator\UID; use Utopia\Detector\Adapter\Bun; use Utopia\Detector\Adapter\CPP; use Utopia\Detector\Adapter\Dart; @@ -36,6 +35,7 @@ use Utopia\Detector\Adapter\Ruby; use Utopia\Detector\Adapter\Swift; use Utopia\Detector\Detector; use Utopia\Validator\Boolean; +use Utopia\VCS\Exception\RepositoryNotFound; use function Swoole\Coroutine\batch; @@ -66,7 +66,14 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId } $owner = $github->getOwnerName($providerInstallationId) ?? ''; - $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; + try { + $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; + if (empty($repositoryName)) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } + } catch (RepositoryNotFound $e) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } if (empty($repositoryName)) { throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); @@ -154,7 +161,14 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $message = 'Authorization required for external contributor.'; $providerRepositoryId = $resource->getAttribute('providerRepositoryId'); - $repositoryName = $github->getRepositoryName($providerRepositoryId); + try { + $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; + if (empty($repositoryName)) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } + } catch (RepositoryNotFound $e) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } $owner = $github->getOwnerName($providerInstallationId); $github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'failure', $message, $authorizeUrl, $name); continue; @@ -206,7 +220,14 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $message = 'Starting...'; $providerRepositoryId = $resource->getAttribute('providerRepositoryId'); - $repositoryName = $github->getRepositoryName($providerRepositoryId); + try { + $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; + if (empty($repositoryName)) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } + } catch (RepositoryNotFound $e) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } $owner = $github->getOwnerName($providerInstallationId); $providerTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/functions/function-$functionId"; @@ -273,7 +294,7 @@ App::get('/v1/vcs/github/callback') ->label('scope', 'public') ->label('error', __DIR__ . '/../../views/general/error.phtml') ->param('installation_id', '', new Text(256, 0), 'GitHub installation ID', true) - ->param('setup_action', '', new Text(256, 0), 'GitHub setup actuon type', true) + ->param('setup_action', '', new Text(256, 0), 'GitHub setup action type', true) ->param('state', '', new Text(2048), 'GitHub state. Contains info sent when starting authorization flow.', true) ->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) ->inject('gitHub') @@ -458,9 +479,12 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:pr $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); $owner = $github->getOwnerName($providerInstallationId); - $repositoryName = $github->getRepositoryName($providerRepositoryId); - - if (empty($repositoryName)) { + try { + $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; + if (empty($repositoryName)) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } + } catch (RepositoryNotFound $e) { throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); } @@ -720,9 +744,12 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); $owner = $github->getOwnerName($providerInstallationId) ?? ''; - $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; - - if (empty($repositoryName)) { + try { + $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; + if (empty($repositoryName)) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } + } catch (RepositoryNotFound $e) { throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); } @@ -766,9 +793,12 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); $owner = $github->getOwnerName($providerInstallationId) ?? ''; - $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; - - if (empty($repositoryName)) { + try { + $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; + if (empty($repositoryName)) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } + } catch (RepositoryNotFound $e) { throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); } @@ -1090,7 +1120,14 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor $providerRepositoryId = $repository->getAttribute('providerRepositoryId'); $owner = $github->getOwnerName($providerInstallationId); - $repositoryName = $github->getRepositoryName($providerRepositoryId); + try { + $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; + if (empty($repositoryName)) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } + } catch (RepositoryNotFound $e) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } $pullRequestResponse = $github->getPullRequest($owner, $repositoryName, $providerPullRequestId); $providerBranch = \explode(':', $pullRequestResponse['head']['label'])[1] ?? ''; diff --git a/app/controllers/mock.php b/app/controllers/mock.php index 593063ae84..bc4f463b5c 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -17,6 +17,9 @@ use Utopia\Validator\WhiteList; use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\UID; use Utopia\Validator\Nullable; +use Utopia\VCS\Adapter\Git\GitHub; +use Utopia\Database\Helpers\Permission; +use Utopia\Database\Helpers\Role; App::get('/v1/mock/tests/foo') ->desc('Get Foo') @@ -248,8 +251,7 @@ App::get('/v1/mock/tests/general/download') ->addHeader('Content-Disposition', 'attachment; filename="test.txt"') ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache ->addHeader('X-Peak', \memory_get_peak_usage()) - ->send("GET:/v1/mock/tests/general/download:passed") - ; + ->send("GET:/v1/mock/tests/general/download:passed"); }); App::post('/v1/mock/tests/general/upload') @@ -331,7 +333,7 @@ App::post('/v1/mock/tests/general/upload') } if ($file['size'] !== 38756) { - throw new Exception(Exception::GENERAL_MOCK, 'Wrong file size'); + throw new Exception(Exception::GENERAL_MOCK, 'Wrong file size'); } if (\md5(\file_get_contents($file['tmp_name'])) !== 'd80e7e6999a3eb2ae0d631a96fe135a4') { @@ -506,8 +508,7 @@ App::get('/v1/mock/tests/general/502-error') $response ->setStatusCode(502) - ->text('This is a text error') - ; + ->text('This is a text error'); }); App::get('/v1/mock/tests/general/oauth2') @@ -646,6 +647,66 @@ App::patch('/v1/mock/functions-v2') $response->noContent(); }); +App::get('/v1/mock/github/callback') + ->desc('Create installation document using GitHub installation id') + ->groups(['mock', 'api', 'vcs']) + ->label('scope', 'public') + ->label('docs', false) + ->param('providerInstallationId', '', new UID(), 'GitHub installation ID') + ->param('projectId', '', new UID(), 'Project ID of the project where app is to be installed') + ->inject('gitHub') + ->inject('project') + ->inject('response') + ->inject('dbForConsole') + ->action(function (string $providerInstallationId, string $projectId, GitHub $github, Document $project, Response $response, Database $dbForConsole) { + $isDevelopment = App::getEnv('_APP_ENV', 'development') === 'development'; + + if (!$isDevelopment) { + throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED); + } + + $project = $dbForConsole->getDocument('projects', $projectId); + + if ($project->isEmpty()) { + $error = 'Project with the ID from state could not be found.'; + throw new Exception(Exception::PROJECT_NOT_FOUND, $error); + } + + if (!empty($providerInstallationId)) { + $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); + $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); + $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); + $owner = $github->getOwnerName($providerInstallationId) ?? ''; + + $projectInternalId = $project->getInternalId(); + + $teamId = $project->getAttribute('teamId', ''); + + $installation = 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')), + ], + 'providerInstallationId' => $providerInstallationId, + 'projectId' => $projectId, + 'projectInternalId' => $projectInternalId, + 'provider' => 'github', + 'organization' => $owner, + 'personal' => false + ]); + + $installation = $dbForConsole->createDocument('installations', $installation); + } + + $response->json([ + 'installationId' => $installation->getId(), + ]); + }); + App::shutdown() ->groups(['mock']) ->inject('utopia') diff --git a/composer.json b/composer.json index 98c620df36..4ce175abea 100644 --- a/composer.json +++ b/composer.json @@ -66,7 +66,7 @@ "utopia-php/registry": "0.5.*", "utopia-php/storage": "0.18.*", "utopia-php/swoole": "0.5.*", - "utopia-php/vcs": "0.5.*", + "utopia-php/vcs": "0.6.*", "utopia-php/websocket": "0.1.*", "matomo/device-detector": "6.1.*", "dragonmantank/cron-expression": "3.3.2", diff --git a/composer.lock b/composer.lock index cb467be2de..11729e8957 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "06c2610579f319495ea7d2d28f42d376", + "content-hash": "0422497b0ed14e96f68dda2c6209ddda", "packages": [ { "name": "adhocore/jwt", @@ -2904,16 +2904,16 @@ }, { "name": "utopia-php/vcs", - "version": "0.5.0", + "version": "0.6.1", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c" + "reference": "d161d1156ef336d197a8d45384b531e5ec31243d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/47144f272030b7ed1b05471f2cb3aabeb8cb831c", - "reference": "47144f272030b7ed1b05471f2cb3aabeb8cb831c", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/d161d1156ef336d197a8d45384b531e5ec31243d", + "reference": "d161d1156ef336d197a8d45384b531e5ec31243d", "shasum": "" }, "require": { @@ -2947,9 +2947,9 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.5.0" + "source": "https://github.com/utopia-php/vcs/tree/0.6.1" }, - "time": "2023-09-13T19:05:52+00:00" + "time": "2023-10-19T07:43:31+00:00" }, { "name": "utopia-php/websocket", @@ -5822,5 +5822,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } diff --git a/tests/e2e/Services/VCS/VCSBase.php b/tests/e2e/Services/VCS/VCSBase.php new file mode 100644 index 0000000000..7531ea3bc8 --- /dev/null +++ b/tests/e2e/Services/VCS/VCSBase.php @@ -0,0 +1,17 @@ +markTestSkipped('VCS is not enabled.'); + } + } +} diff --git a/tests/e2e/Services/VCS/VCSConsoleClientTest.php b/tests/e2e/Services/VCS/VCSConsoleClientTest.php new file mode 100644 index 0000000000..697fbdba91 --- /dev/null +++ b/tests/e2e/Services/VCS/VCSConsoleClientTest.php @@ -0,0 +1,319 @@ +client->call(Client::METHOD_GET, '/mock/github/callback', array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'providerInstallationId' => $this->providerInstallationId, + 'projectId' => $this->getProject()['$id'], + ]); + + $this->assertNotEmpty($response['body']['installationId']); + $installationId = $response['body']['installationId']; + return $installationId; + } + + /** + * @depends testGitHubAuthorize + */ + public function testGetInstallation(string $installationId): void + { + /** + * Test for SUCCESS + */ + + $installation = $this->client->call(Client::METHOD_GET, '/vcs/installations/' . $installationId, array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $installation['headers']['status-code']); + $this->assertEquals('github', $installation['body']['provider']); + $this->assertEquals('appwrite-test', $installation['body']['organization']); + } + + /** + * @depends testGitHubAuthorize + */ + public function testDetectRuntime(string $installationId): void + { + /** + * Test for SUCCESS + */ + + $runtime = $this->client->call(Client::METHOD_POST, '/vcs/github/installations/' . $installationId . '/providerRepositories/' . $this->providerRepositoryId . '/detection', array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $runtime['headers']['status-code']); + $this->assertEquals($runtime['body']['runtime'], 'ruby-3.1'); + + /** + * Test for FAILURE + */ + + $runtime = $this->client->call(Client::METHOD_POST, '/vcs/github/installations/' . $installationId . '/providerRepositories/randomRepositoryId/detection', array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $runtime['headers']['status-code']); + } + + /** + * @depends testGitHubAuthorize + */ + public function testListRepositories(string $installationId): void + { + /** + * Test for SUCCESS + */ + + $repositories = $this->client->call(Client::METHOD_GET, '/vcs/github/installations/' . $installationId . '/providerRepositories', array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $repositories['headers']['status-code']); + $this->assertEquals($repositories['body']['total'], 3); + $this->assertEquals($repositories['body']['providerRepositories'][0]['name'], 'function1.4'); + $this->assertEquals($repositories['body']['providerRepositories'][0]['organization'], 'appwrite-test'); + $this->assertEquals($repositories['body']['providerRepositories'][0]['provider'], 'github'); + $this->assertEquals($repositories['body']['providerRepositories'][1]['name'], 'appwrite'); + $this->assertEquals($repositories['body']['providerRepositories'][2]['name'], 'ruby-starter'); + + + $searchedRepositories = $this->client->call(Client::METHOD_GET, '/vcs/github/installations/' . $installationId . '/providerRepositories', array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => 'func' + ]); + + $this->assertEquals(200, $searchedRepositories['headers']['status-code']); + $this->assertEquals($searchedRepositories['body']['total'], 1); + $this->assertEquals($searchedRepositories['body']['providerRepositories'][0]['name'], 'function1.4'); + + /** + * Test for FAILURE + */ + + $repositories = $this->client->call(Client::METHOD_GET, '/vcs/github/installations/randomInstallationId/providerRepositories', array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $repositories['headers']['status-code']); + } + + /** + * @depends testGitHubAuthorize + */ + public function testGetRepository(string $installationId): void + { + /** + * Test for SUCCESS + */ + + $repository = $this->client->call(Client::METHOD_GET, '/vcs/github/installations/' . $installationId . '/providerRepositories/' . $this->providerRepositoryId, array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $repository['headers']['status-code']); + $this->assertEquals($repository['body']['name'], 'ruby-starter'); + $this->assertEquals($repository['body']['organization'], 'appwrite-test'); + $this->assertEquals($repository['body']['private'], false); + + $repository = $this->client->call(Client::METHOD_GET, '/vcs/github/installations/' . $installationId . '/providerRepositories/' . $this->providerRepositoryId2, array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $repository['headers']['status-code']); + $this->assertEquals($repository['body']['name'], 'function1.4'); + $this->assertEquals($repository['body']['organization'], 'appwrite-test'); + $this->assertEquals($repository['body']['private'], true); + + /** + * Test for FAILURE + */ + + $repository = $this->client->call(Client::METHOD_GET, '/vcs/github/installations/' . $installationId . '/providerRepositories/randomRepositoryId', array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $repository['headers']['status-code']); + } + + /** + * @depends testGitHubAuthorize + */ + public function testListRepositoryBranches(string $installationId): void + { + /** + * Test for SUCCESS + */ + + $repositoryBranches = $this->client->call(Client::METHOD_GET, '/vcs/github/installations/' . $installationId . '/providerRepositories/' . $this->providerRepositoryId . '/branches', array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $repositoryBranches['headers']['status-code']); + $this->assertEquals($repositoryBranches['body']['total'], 2); + $this->assertEquals($repositoryBranches['body']['branches'][0]['name'], 'main'); + $this->assertEquals($repositoryBranches['body']['branches'][1]['name'], 'test'); + + /** + * Test for FAILURE + */ + + $repositoryBranches = $this->client->call(Client::METHOD_GET, '/vcs/github/installations/' . $installationId . '/providerRepositories/randomRepositoryId/branches', array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(404, $repositoryBranches['headers']['status-code']); + } + + /** + * @depends testGitHubAuthorize + */ + public function testCreateFunctionUsingVCS(string $installationId): array + { + $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'functionId' => ID::unique(), + 'name' => 'Test', + 'execute' => [Role::user($this->getUser()['$id'])->toString()], + 'runtime' => 'php-8.0', + 'entrypoint' => 'index.php', + 'events' => [ + 'users.*.create', + 'users.*.delete', + ], + 'schedule' => '0 0 1 1 *', + 'timeout' => 10, + 'installationId' => $installationId, + 'providerRepositoryId' => $this->providerRepositoryId, + 'providerBranch' => 'main', + ]); + + $this->assertEquals(201, $function['headers']['status-code']); + $this->assertEquals('Test', $function['body']['name']); + $this->assertEquals('php-8.0', $function['body']['runtime']); + $this->assertEquals('index.php', $function['body']['entrypoint']); + $this->assertEquals('705764267', $function['body']['providerRepositoryId']); + $this->assertEquals('main', $function['body']['providerBranch']); + + return [ + 'installationId' => $installationId, + 'functionId' => $function['body']['$id'] + ]; + } + + /** + * @depends testCreateFunctionUsingVCS + */ + public function testUpdateFunctionUsingVCS(array $data): string + { + $function = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'functionId' => ID::unique(), + 'name' => 'Test', + 'execute' => [Role::user($this->getUser()['$id'])->toString()], + 'runtime' => 'php-8.0', + 'entrypoint' => 'index.php', + 'events' => [ + 'users.*.create', + 'users.*.delete', + ], + 'schedule' => '0 0 1 1 *', + 'timeout' => 10, + 'installationId' => $data['installationId'], + 'providerRepositoryId' => $this->providerRepositoryId2, + 'providerBranch' => 'main', + ]); + + $this->assertEquals(200, $function['headers']['status-code']); + $this->assertEquals('Test', $function['body']['name']); + $this->assertEquals('php-8.0', $function['body']['runtime']); + $this->assertEquals('index.php', $function['body']['entrypoint']); + $this->assertEquals('708688544', $function['body']['providerRepositoryId']); + $this->assertEquals('main', $function['body']['providerBranch']); + + return $function['body']['$id']; + } + + /** + * @depends testGitHubAuthorize + */ + public function testCreateRepository(string $installationId): void + { + /** + * Test for SUCCESS + */ + + $github = new GitHub(new Cache(new None())); + $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); + $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); + $github->initializeVariables($this->providerInstallationId, $privateKey, $githubAppId); + + $repository = $this->client->call(Client::METHOD_POST, '/vcs/github/installations/' . $installationId . '/providerRepositories', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'test-repo-1', + 'private' => true + ]); + + $this->assertEquals('test-repo-1', $repository['body']['name']); + $this->assertEquals('appwrite-test', $repository['body']['organization']); + $this->assertEquals('github', $repository['body']['provider']); + + /** + * Test for FAILURE + */ + + $repository = $this->client->call(Client::METHOD_POST, '/vcs/github/installations/' . $installationId . '/providerRepositories', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'name' => 'test-repo-1', + 'private' => true + ]); + + $this->assertEquals(400, $repository['headers']['status-code']); + $this->assertEquals('Provider Error: Repository creation failed. name already exists on this account', $repository['body']['message']); + + /** + * Test for SUCCESS + */ + + $result = $github->deleteRepository('appwrite-test', 'test-repo-1'); + $this->assertEquals($result, true); + } +} diff --git a/tests/e2e/Services/VCS/VCSCustomClientTest.php b/tests/e2e/Services/VCS/VCSCustomClientTest.php new file mode 100644 index 0000000000..084255cd69 --- /dev/null +++ b/tests/e2e/Services/VCS/VCSCustomClientTest.php @@ -0,0 +1,24 @@ +client->call(Client::METHOD_GET, '/vcs/installations/' . $installationId, array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(401, $installation['headers']['status-code']); + } +} diff --git a/tests/e2e/Services/VCS/VCSCustomServerTest.php b/tests/e2e/Services/VCS/VCSCustomServerTest.php new file mode 100644 index 0000000000..dee4cd7ced --- /dev/null +++ b/tests/e2e/Services/VCS/VCSCustomServerTest.php @@ -0,0 +1,25 @@ +client->call(Client::METHOD_GET, '/vcs/installations/' . $installationId, array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], $this->getHeaders())); + + $this->assertEquals(401, $installation['headers']['status-code']); + } +}