Merge pull request #9445 from appwrite/chore-rename-redeploy-endpoint

Chore: Migrate redeploy endpoint
This commit is contained in:
Matej Bačo 2025-03-06 09:29:38 +01:00 committed by GitHub
commit 4428ed1f7d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 193 additions and 23 deletions

View file

@ -1,6 +1,6 @@
<?php
namespace Appwrite\Platform\Modules\Functions\Http\Deployments\Builds;
namespace Appwrite\Platform\Modules\Functions\Http\Deployments\Duplicate;
use Appwrite\Event\Build;
use Appwrite\Event\Event;
@ -22,16 +22,17 @@ class Create extends Action
public static function getName()
{
return 'createDeploymentBuild';
return 'createDuplicateDeployment';
}
public function __construct()
{
$this
->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,15 +41,15 @@ class Create extends Action
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
name: 'createBuild',
name: 'createDuplicateDeployment',
description: <<<EOT
Create a new build for an existing function deployment. This endpoint allows you to rebuild a deployment with the updated function configuration, including its entrypoint and build commands if they have been modified. The build process will be queued and executed asynchronously. The original deployment's code will be preserved and used for the new build.
EOT,
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_NOCONTENT,
model: Response::MODEL_NONE,
code: Response::STATUS_CODE_ACCEPTED,
model: Response::MODEL_DEPLOYMENT,
)
]
))
@ -107,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);
}
}

View file

@ -2,11 +2,11 @@
namespace Appwrite\Platform\Modules\Functions\Services;
use Appwrite\Platform\Modules\Functions\Http\Deployments\Builds\Create as CreateBuild;
use Appwrite\Platform\Modules\Functions\Http\Deployments\Builds\Update as UpdateBuild;
use Appwrite\Platform\Modules\Functions\Http\Deployments\Create as CreateDeployment;
use Appwrite\Platform\Modules\Functions\Http\Deployments\Delete as DeleteDeployment;
use Appwrite\Platform\Modules\Functions\Http\Deployments\Download\Get as DownloadDeployment;
use Appwrite\Platform\Modules\Functions\Http\Deployments\Duplicate\Create as CreateDuplicateDeployment;
use Appwrite\Platform\Modules\Functions\Http\Deployments\Get as GetDeployment;
use Appwrite\Platform\Modules\Functions\Http\Deployments\Template\Create as CreateTemplateDeployment;
use Appwrite\Platform\Modules\Functions\Http\Deployments\Vcs\Create as CreateVcsDeployment;
@ -62,7 +62,7 @@ class Http extends Service
$this->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

View file

@ -1,6 +1,6 @@
<?php
namespace Appwrite\Platform\Modules\Sites\Http\Deployments\Builds;
namespace Appwrite\Platform\Modules\Sites\Http\Deployments\Duplicate;
use Appwrite\Event\Build;
use Appwrite\Event\Event;
@ -25,15 +25,15 @@ class Create extends Action
public static function getName()
{
return 'createDeploymentBuild';
return 'createDuplicateDeployment';
}
public function __construct()
{
$this
->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,15 +41,15 @@ class Create extends Action
->label('audits.resource', 'site/{request.siteId}')
->label('sdk', new Method(
namespace: 'sites',
name: 'createDeploymentBuild',
name: 'createDuplicateDeployment',
description: <<<EOT
Create a new build for an existing site deployment. This endpoint allows you to rebuild a deployment with the updated site configuration, including its commands and output directory if they have been modified. The build process will be queued and executed asynchronously. The original deployment's code will be preserved and used for the new build.
EOT,
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);
}
}

View file

@ -2,11 +2,11 @@
namespace Appwrite\Platform\Modules\Sites\Services;
use Appwrite\Platform\Modules\Sites\Http\Deployments\Builds\Create as CreateBuild;
use Appwrite\Platform\Modules\Sites\Http\Deployments\Builds\Update as UpdateBuild;
use Appwrite\Platform\Modules\Sites\Http\Deployments\Create as CreateDeployment;
use Appwrite\Platform\Modules\Sites\Http\Deployments\Delete as DeleteDeployment;
use Appwrite\Platform\Modules\Sites\Http\Deployments\Download\Get as DownloadDeployment;
use Appwrite\Platform\Modules\Sites\Http\Deployments\Duplicate\Create as CreateDuplicateDeployment;
use Appwrite\Platform\Modules\Sites\Http\Deployments\Get as GetDeployment;
use Appwrite\Platform\Modules\Sites\Http\Deployments\Template\Create as CreateTemplateDeployment;
use Appwrite\Platform\Modules\Sites\Http\Deployments\Vcs\Create as CreateVcsDeployment;
@ -57,7 +57,7 @@ class Http extends Service
$this->addAction(UpdateSiteDeployment::getName(), new UpdateSiteDeployment());
$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

View file

@ -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([
@ -325,6 +335,39 @@ 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;
}
protected function updateFunctionDeployment(string $functionId, string $deploymentId): mixed
{
$function = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployment', array_merge([

View file

@ -1990,6 +1990,46 @@ 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);
}
public function testUpdateDeploymentStatus(): void
{

View file

@ -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
}
}';

View file

@ -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

View file

@ -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([

View file

@ -1744,6 +1744,52 @@ 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);
}
public function testUpdateDeploymentStatus(): void
{
$siteId = $this->setupSite([

View file

@ -0,0 +1,3 @@
module.exports = async(context) => {
return context.res.send('Maintenance');
}