From a0a82b86dc80167d279b9c0e39637e7a408361ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 5 Mar 2025 11:29:43 +0100 Subject: [PATCH 1/5] Migrate redeploy endpoint --- .../Http/Deployments/{Builds => Duplicate}/Create.php | 11 ++++++----- .../Platform/Modules/Functions/Services/Http.php | 4 ++-- .../Http/Deployments/{Builds => Duplicate}/Create.php | 10 +++++----- src/Appwrite/Platform/Modules/Sites/Services/Http.php | 4 ++-- 4 files changed, 15 insertions(+), 14 deletions(-) rename src/Appwrite/Platform/Modules/Functions/Http/Deployments/{Builds => Duplicate}/Create.php (93%) rename src/Appwrite/Platform/Modules/Sites/Http/Deployments/{Builds => Duplicate}/Create.php (94%) diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Builds/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Duplicate/Create.php similarity index 93% rename from src/Appwrite/Platform/Modules/Functions/Http/Deployments/Builds/Create.php rename to src/Appwrite/Platform/Modules/Functions/Http/Deployments/Duplicate/Create.php index 1d85bcb11d..970f79c9fd 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Builds/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Duplicate/Create.php @@ -1,6 +1,6 @@ setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) - ->setHttpPath('/v1/functions/:functionId/deployments/:deploymentId/build') + ->setHttpPath('/v1/functions/:functionId/deployments/duplicate') + ->httpAlias('/v1/functions/:functionId/deployments/:deploymentId/build') ->httpAlias('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') - ->desc('Rebuild deployment') + ->desc('Create duplicate deployment') ->groups(['api', 'functions']) ->label('scope', 'functions.write') ->label('resourceType', RESOURCE_TYPE_FUNCTIONS) @@ -40,7 +41,7 @@ class Create extends Action ->label('audits.resource', 'function/{request.functionId}') ->label('sdk', new Method( namespace: 'functions', - name: 'createBuild', + name: 'createDuplicateDeployment', description: <<addAction(CreateTemplateDeployment::getName(), new CreateTemplateDeployment()); $this->addAction(CreateVcsDeployment::getName(), new CreateVcsDeployment()); $this->addAction(DownloadDeployment::getName(), new DownloadDeployment()); - $this->addAction(CreateBuild::getName(), new CreateBuild()); + $this->addAction(CreateDuplicateDeployment::getName(), new CreateDuplicateDeployment()); $this->addAction(UpdateBuild::getName(), new UpdateBuild()); // Executions diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Builds/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php similarity index 94% rename from src/Appwrite/Platform/Modules/Sites/Http/Deployments/Builds/Create.php rename to src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php index 2f4da2d289..7c06c5f175 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Builds/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php @@ -1,6 +1,6 @@ setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) - ->setHttpPath('/v1/sites/:siteId/deployments/:deploymentId/build') - ->desc('Rebuild deployment') + ->setHttpPath('/v1/sites/:siteId/deployments/duplicate') + ->desc('Create duplicate deployment') ->groups(['api', 'sites']) ->label('scope', 'sites.write') ->label('event', 'sites.[siteId].deployments.[deploymentId].update') @@ -41,7 +41,7 @@ class Create extends Action ->label('audits.resource', 'site/{request.siteId}') ->label('sdk', new Method( namespace: 'sites', - name: 'createDeploymentBuild', + name: 'createDuplicateDeployment', description: <<addAction(UpdateDeployment::getName(), new UpdateDeployment()); $this->addAction(DeleteDeployment::getName(), new DeleteDeployment()); $this->addAction(DownloadDeployment::getName(), new DownloadDeployment()); - $this->addAction(CreateBuild::getName(), new CreateBuild()); + $this->addAction(CreateDuplicateDeployment::getName(), new CreateDuplicateDeployment()); $this->addAction(UpdateBuild::getName(), new UpdateBuild()); // Logs From 049ad14121f864da7bee61ca11930a99de4c61b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 5 Mar 2025 11:47:51 +0100 Subject: [PATCH 2/5] Fix tests --- tests/e2e/Services/GraphQL/Base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index 0b3250cecf..5c38a10472 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -1620,7 +1620,7 @@ trait Base }'; case self::$RETRY_BUILD: return 'mutation retryBuild($functionId: String!, $deploymentId: String!, $buildId: String!) { - functionsCreateBuild(functionId: $functionId, deploymentId: $deploymentId, buildId: $buildId) { + functionsCreateDuplicateDeployment(functionId: $functionId, deploymentId: $deploymentId, buildId: $buildId) { status } }'; From f6674f07e4338f253bb371fbf90cf2de2138a909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 5 Mar 2025 12:29:27 +0100 Subject: [PATCH 3/5] Add redeploy tests --- .../Http/Deployments/Duplicate/Create.php | 8 ++-- .../Http/Deployments/Duplicate/Create.php | 8 ++-- .../e2e/Services/Functions/FunctionsBase.php | 43 +++++++++++++++++ .../Functions/FunctionsCustomServerTest.php | 40 ++++++++++++++++ tests/e2e/Services/Sites/SitesBase.php | 33 +++++++++++++ .../Services/Sites/SitesCustomServerTest.php | 46 +++++++++++++++++++ tests/resources/functions/node/maintenance.js | 3 ++ 7 files changed, 175 insertions(+), 6 deletions(-) create mode 100644 tests/resources/functions/node/maintenance.js diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Duplicate/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Duplicate/Create.php index 970f79c9fd..624a410dcd 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Duplicate/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Duplicate/Create.php @@ -48,8 +48,8 @@ class Create extends Action auth: [AuthType::KEY], responses: [ new SDKResponse( - code: Response::STATUS_CODE_NOCONTENT, - model: Response::MODEL_NONE, + code: Response::STATUS_CODE_ACCEPTED, + model: Response::MODEL_DEPLOYMENT, ) ] )) @@ -108,6 +108,8 @@ class Create extends Action ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()); - $response->noContent(); + $response + ->setStatusCode(Response::STATUS_CODE_ACCEPTED) + ->dynamic($deployment, Response::MODEL_DEPLOYMENT); } } diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php index 7c06c5f175..b30c07838f 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php @@ -48,8 +48,8 @@ class Create extends Action auth: [AuthType::KEY], responses: [ new SDKResponse( - code: Response::STATUS_CODE_NOCONTENT, - model: Response::MODEL_NONE, + code: Response::STATUS_CODE_ACCEPTED, + model: Response::MODEL_DEPLOYMENT, ) ] )) @@ -130,6 +130,8 @@ class Create extends Action ->setParam('siteId', $site->getId()) ->setParam('deploymentId', $deployment->getId()); - $response->noContent(); + $response + ->setStatusCode(Response::STATUS_CODE_ACCEPTED) + ->dynamic($deployment, Response::MODEL_DEPLOYMENT); } } diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index f97245b0af..25dfc0bcb6 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -87,6 +87,16 @@ trait FunctionsBase return $function; } + protected function updateFunction(string $functionId, mixed $params): mixed + { + $function = $this->client->call(Client::METHOD_PUT, '/functions/' . $functionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), $params); + + return $function; + } + protected function createVariable(string $functionId, mixed $params): mixed { $variable = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', array_merge([ @@ -324,4 +334,37 @@ trait FunctionsBase return $response; } + + protected function setupDuplicateDeployment(string $functionId, string $deploymentId): string + { + $deployment = $this->createDuplicateDeployment($functionId, $deploymentId); + $this->assertEquals(202, $deployment['headers']['status-code']); + + $deploymentId = $deployment['body']['$id']; + $this->assertNotEmpty($deploymentId); + + $this->assertEventually(function () use ($functionId, $deploymentId) { + $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals('ready', $deployment['body']['status'], 'Deployment status is not ready, deployment: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT)); + }, 100000, 500); + + $this->assertEventually(function () use ($functionId, $deploymentId) { + $function = $this->getFunction($functionId); + $this->assertEquals($deploymentId, $function['body']['deployment'], 'Deployment is not activated, deployment: ' . json_encode($function['body'], JSON_PRETTY_PRINT)); + }, 100000, 500); + + return $deploymentId; + } + + protected function createDuplicateDeployment(string $functionId, string $deploymentId): mixed + { + $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments/duplicate', array_merge([ + 'content-type' => 'multipart/form-data', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'deploymentId' => $deploymentId, + ]); + + return $deployment; + } } diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index f4cd9ca493..ad033a5124 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -1989,4 +1989,44 @@ class FunctionsCustomServerTest extends Scope $this->cleanupFunction($functionId); } + + public function testDuplicateDeployment(): void + { + $functionId = $this->setupFunction([ + 'functionId' => ID::unique(), + 'runtime' => 'node-18.0', + 'name' => 'Duplicate Deployment Test', + 'entrypoint' => 'index.js', + 'commands' => '' + ]); + $this->assertNotEmpty($functionId); + + $deploymentId1 = $this->setupDeployment($functionId, [ + 'code' => $this->packageFunction('node'), + 'activate' => true + ]); + $this->assertNotEmpty($deploymentId1); + + $execution = $this->createExecution($functionId); + $this->assertEquals(201, $execution['headers']['status-code']); + $this->assertStringContainsString('APPWRITE_FUNCTION_ID', $execution['body']['responseBody']); + + $site = $this->updateFunction($functionId, [ + 'runtime' => 'node-18.0', + 'name' => 'Duplicate Deployment Test', + 'entrypoint' => 'index.js', + 'commands' => 'rm index.js && mv maintenance.js index.js' + ]); + $this->assertEquals(200, $site['headers']['status-code']); + $this->assertStringContainsString('maintenance.js', $site['body']['commands']); + + $deploymentId2 = $this->setupDuplicateDeployment($functionId, $deploymentId1); + $this->assertNotEmpty($deploymentId2); + + $execution = $this->createExecution($functionId); + $this->assertEquals(201, $execution['headers']['status-code']); + $this->assertStringContainsString('Maintenance', $execution['body']['responseBody']); + + $this->cleanupFunction($functionId); + } } diff --git a/tests/e2e/Services/Sites/SitesBase.php b/tests/e2e/Services/Sites/SitesBase.php index 6e597b31ec..fe271e18e2 100644 --- a/tests/e2e/Services/Sites/SitesBase.php +++ b/tests/e2e/Services/Sites/SitesBase.php @@ -244,6 +244,39 @@ trait SitesBase return $deployment; } + protected function setupDuplicateDeployment(string $siteId, string $deploymentId): string + { + $deployment = $this->createDuplicateDeployment($siteId, $deploymentId); + $this->assertEquals(202, $deployment['headers']['status-code']); + + $deploymentId = $deployment['body']['$id']; + $this->assertNotEmpty($deploymentId); + + $this->assertEventually(function () use ($siteId, $deploymentId) { + $deployment = $this->getDeployment($siteId, $deploymentId); + $this->assertEquals('ready', $deployment['body']['status'], 'Deployment status is not ready, deployment: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT)); + }, 100000, 500); + + $this->assertEventually(function () use ($siteId, $deploymentId) { + $site = $this->getSite($siteId); + $this->assertEquals($deploymentId, $site['body']['deploymentId'], 'Deployment is not activated, deployment: ' . json_encode($site['body'], JSON_PRETTY_PRINT)); + }, 100000, 500); + + return $deploymentId; + } + + protected function createDuplicateDeployment(string $siteId, string $deploymentId): mixed + { + $deployment = $this->client->call(Client::METHOD_POST, '/sites/' . $siteId . '/deployments/duplicate', array_merge([ + 'content-type' => 'multipart/form-data', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'deploymentId' => $deploymentId, + ]); + + return $deployment; + } + protected function createTemplateDeployment(string $siteId, mixed $params = []): mixed { $deployment = $this->client->call(Client::METHOD_POST, '/sites/' . $siteId . '/deployments/template', array_merge([ diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index 3e6132c845..e2c8e6a6a3 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -1747,4 +1747,50 @@ class SitesCustomServerTest extends Scope $this->cleanupSite($siteId); } + + public function testDuplicateDeployment(): void + { + $siteId = $this->setupSite([ + 'buildRuntime' => 'ssr-22', + 'framework' => 'other', + 'name' => 'Duplicate deployment Site', + 'adapter' => 'static', + 'fallbackFile' => '404.html', + 'siteId' => ID::unique() + ]); + $this->assertNotEmpty($siteId); + + $domain = $this->setupSiteDomain($siteId); + $this->assertNotEmpty($domain); + $proxyClient = new Client(); + $proxyClient->setEndpoint('http://' . $domain); + + $deploymentId1 = $this->setupDeployment($siteId, [ + 'code' => $this->packageSite('static-spa'), + 'activate' => true + ]); + $this->assertNotEmpty($deploymentId1); + + $response = $proxyClient->call(Client::METHOD_GET, '/not-found'); + $this->assertStringContainsString("Customized 404 page", $response['body']); + + $site = $this->updateSite([ + '$id' => $siteId, + 'buildRuntime' => 'ssr-22', + 'framework' => 'other', + 'name' => 'Duplicate deployment Site', + 'adapter' => 'static', + 'fallbackFile' => 'index.html', + ]); + $this->assertEquals(200, $site['headers']['status-code']); + $this->assertEquals('index.html', $site['body']['fallbackFile']); + + $deploymentId2 = $this->setupDuplicateDeployment($siteId, $deploymentId1); + $this->assertNotEmpty($deploymentId2); + + $response = $proxyClient->call(Client::METHOD_GET, '/not-found'); + $this->assertStringContainsString("Index page", $response['body']); + + $this->cleanupSite($siteId); + } } diff --git a/tests/resources/functions/node/maintenance.js b/tests/resources/functions/node/maintenance.js new file mode 100644 index 0000000000..0885f11041 --- /dev/null +++ b/tests/resources/functions/node/maintenance.js @@ -0,0 +1,3 @@ +module.exports = async(context) => { + return context.res.send('Maintenance'); +} \ No newline at end of file From 77d27bdd2002b51a07fa9dfa8e3fb9ff639d6ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 5 Mar 2025 12:33:32 +0100 Subject: [PATCH 4/5] Post-merge formatting --- tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 2 +- tests/e2e/Services/Sites/SitesCustomServerTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 42e9eb054e..a1904edea1 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -2029,7 +2029,7 @@ class FunctionsCustomServerTest extends Scope $this->cleanupFunction($functionId); } - + public function testUpdateDeploymentStatus(): void { diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index dadca3f004..605c5abb3e 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -1789,7 +1789,7 @@ class SitesCustomServerTest extends Scope $this->cleanupSite($siteId); } - + public function testUpdateDeploymentStatus(): void { $siteId = $this->setupSite([ From 2f3c43d0dbe76e40fbb29ae229275ba87a25ff6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 5 Mar 2025 13:00:06 +0100 Subject: [PATCH 5/5] Fix failing tests --- tests/e2e/Services/GraphQL/FunctionsServerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Services/GraphQL/FunctionsServerTest.php b/tests/e2e/Services/GraphQL/FunctionsServerTest.php index abd86b30ec..e45e75228b 100644 --- a/tests/e2e/Services/GraphQL/FunctionsServerTest.php +++ b/tests/e2e/Services/GraphQL/FunctionsServerTest.php @@ -186,8 +186,8 @@ class FunctionsServerTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $gqlPayload); - $this->assertIsNotArray($response['body']); - $this->assertEquals(204, $response['headers']['status-code']); + $this->assertIsArray($response['body']['data']); + $this->assertEquals(200, $response['headers']['status-code']); } public function testGetFunctions(): array