From 9793cf4ece402c98ec36341d79f3166b3d3841fa Mon Sep 17 00:00:00 2001 From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com> Date: Wed, 23 Oct 2024 17:07:28 +0200 Subject: [PATCH] Add all deployment endpoints for sites --- .../Http/Deployments/CancelDeployment.php | 124 ++++++++++++++++++ .../Http/Deployments/CreateDeployment.php | 3 +- .../Http/Deployments/DeleteDeployment.php | 96 ++++++++++++++ .../Http/Deployments/DownloadDeployment.php | 115 ++++++++++++++++ .../Sites/Http/Deployments/GetDeployment.php | 70 ++++++++++ .../Http/Deployments/ListDeployments.php | 116 ++++++++++++++++ .../Http/Deployments/RebuildDeployment.php | 99 ++++++++++++++ .../Http/Deployments/UpdateDeployment.php | 83 ++++++++++++ .../Platform/Modules/Sites/Services/Http.php | 14 ++ 9 files changed, 718 insertions(+), 2 deletions(-) create mode 100644 src/Appwrite/Platform/Modules/Sites/Http/Deployments/CancelDeployment.php create mode 100644 src/Appwrite/Platform/Modules/Sites/Http/Deployments/DeleteDeployment.php create mode 100644 src/Appwrite/Platform/Modules/Sites/Http/Deployments/DownloadDeployment.php create mode 100644 src/Appwrite/Platform/Modules/Sites/Http/Deployments/GetDeployment.php create mode 100644 src/Appwrite/Platform/Modules/Sites/Http/Deployments/ListDeployments.php create mode 100644 src/Appwrite/Platform/Modules/Sites/Http/Deployments/RebuildDeployment.php create mode 100644 src/Appwrite/Platform/Modules/Sites/Http/Deployments/UpdateDeployment.php diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/CancelDeployment.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/CancelDeployment.php new file mode 100644 index 0000000000..ef6acefdc5 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/CancelDeployment.php @@ -0,0 +1,124 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/sites/:siteId/deployments/:deploymentId/build') + ->desc('Cancel deployment') + ->groups(['api', 'sites']) + ->label('scope', 'functions.write') //TODO: Update the scope to sites later + ->label('audits.event', 'deployment.update') + ->label('audits.resource', 'site/{request.siteId}') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'sites') + ->label('sdk.method', 'updateDeploymentBuild') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_BUILD) + ->param('siteId', '', new UID(), 'Site ID.') + ->param('deploymentId', '', new UID(), 'Deployment ID.') + ->inject('response') + ->inject('dbForProject') + ->inject('project') + ->inject('queueForEvents') + ->callback([$this, 'action']); + } + + public function action(string $siteId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents) + { + $site = $dbForProject->getDocument('sites', $siteId); + + if ($site->isEmpty()) { + throw new Exception(Exception::SITE_NOT_FOUND); + } + + $deployment = $dbForProject->getDocument('deployments', $deploymentId); + + if ($deployment->isEmpty()) { + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); + } + + $build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); + + if ($build->isEmpty()) { + $buildId = ID::unique(); + $build = $dbForProject->createDocument('builds', new Document([ + '$id' => $buildId, + '$permissions' => [], + 'startTime' => DateTime::now(), + 'deploymentInternalId' => $deployment->getInternalId(), + 'deploymentId' => $deployment->getId(), + 'status' => 'canceled', + 'path' => '', + 'runtime' => $site->getAttribute('framework'), + 'source' => $deployment->getAttribute('path', ''), + 'sourceType' => '', + 'logs' => '', + 'duration' => 0, + 'size' => 0 + ])); + + $deployment->setAttribute('buildId', $build->getId()); + $deployment->setAttribute('buildInternalId', $build->getInternalId()); + $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); + } else { + if (\in_array($build->getAttribute('status'), ['ready', 'failed'])) { + throw new Exception(Exception::BUILD_ALREADY_COMPLETED); + } + + $startTime = new \DateTime($build->getAttribute('startTime')); + $endTime = new \DateTime('now'); + $duration = $endTime->getTimestamp() - $startTime->getTimestamp(); + + $build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttributes([ + 'endTime' => DateTime::now(), + 'duration' => $duration, + 'status' => 'canceled' + ])); + } + + $dbForProject->purgeCachedDocument('deployments', $deployment->getId()); + + try { + $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); + $executor->deleteRuntime($project->getId(), $deploymentId . "-build"); + } catch (\Throwable $th) { + // Don't throw if the deployment doesn't exist + if ($th->getCode() !== 404) { + throw $th; + } + } + + $queueForEvents + ->setParam('siteId', $site->getId()) + ->setParam('deploymentId', $deployment->getId()); + + $response->dynamic($build, Response::MODEL_BUILD); + } +} diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/CreateDeployment.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/CreateDeployment.php index b710305dc0..b35708fb50 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/CreateDeployment.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/CreateDeployment.php @@ -65,7 +65,6 @@ class CreateDeployment extends Action ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('project') ->inject('deviceForSites') ->inject('deviceForFunctions') // TODO: Remove this later once volume is added to executor ->inject('deviceForLocal') @@ -73,7 +72,7 @@ class CreateDeployment extends Action ->callback([$this, 'action']); } - public function action(string $siteId, ?string $installCommand, ?string $buildCommand, ?string $outputDirectory, mixed $code, mixed $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceForSites, Device $deviceForFunctions, Device $deviceForLocal, Build $queueForBuilds) + public function action(string $siteId, ?string $installCommand, ?string $buildCommand, ?string $outputDirectory, mixed $code, mixed $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Device $deviceForSites, Device $deviceForFunctions, Device $deviceForLocal, Build $queueForBuilds) { $activate = \strval($activate) === 'true' || \strval($activate) === '1'; diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/DeleteDeployment.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/DeleteDeployment.php new file mode 100644 index 0000000000..313870bb97 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/DeleteDeployment.php @@ -0,0 +1,96 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/sites/:siteId/deployments/:deploymentId') + ->desc('Delete deployment') + ->groups(['api', 'sites']) + ->label('scope', 'functions.write') //TODO: Update the scope to sites later + ->label('event', 'sites.[siteId].deployments.[deploymentId].delete') + ->label('audits.event', 'deployment.delete') + ->label('audits.resource', 'site/{request.siteId}') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'sites') + ->label('sdk.method', 'deleteDeployment') + ->label('sdk.description', '/docs/references/sites/delete-deployment.md') + ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) + ->label('sdk.response.model', Response::MODEL_NONE) + ->param('siteId', '', new UID(), 'Site ID.') + ->param('deploymentId', '', new UID(), 'Deployment ID.') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForDeletes') + ->inject('queueForEvents') + ->inject('deviceForSites') + ->inject('deviceForFunctions') //TODO: remove it later + ->callback([$this, 'action']); + } + + public function action(string $siteId, string $deploymentId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Device $deviceForSites, Device $deviceForFunctions) + { + $site = $dbForProject->getDocument('sites', $siteId); + if ($site->isEmpty()) { + throw new Exception(Exception::SITE_NOT_FOUND); + } + + $deployment = $dbForProject->getDocument('deployments', $deploymentId); + if ($deployment->isEmpty()) { + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); + } + + if ($deployment->getAttribute('resourceId') !== $site->getId()) { + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); + } + + if (!$dbForProject->deleteDocument('deployments', $deployment->getId())) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove deployment from DB'); + } + + if (!empty($deployment->getAttribute('path', ''))) { + if (!($deviceForFunctions->delete($deployment->getAttribute('path', '')))) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove deployment from storage'); + } + } + + if ($site->getAttribute('deployment') === $deployment->getId()) { // Reset site deployment + $site = $dbForProject->updateDocument('sites', $site->getId(), new Document(array_merge($site->getArrayCopy(), [ + 'deployment' => '', + 'deploymentInternalId' => '', + ]))); + } + + $queueForEvents + ->setParam('siteId', $site->getId()) + ->setParam('deploymentId', $deployment->getId()); + + $queueForDeletes + ->setType(DELETE_TYPE_DOCUMENT) + ->setDocument($deployment); + + $response->noContent(); + } +} diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/DownloadDeployment.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/DownloadDeployment.php new file mode 100644 index 0000000000..0d748514b1 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/DownloadDeployment.php @@ -0,0 +1,115 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/sites/:siteId/deployments/:deploymentId/download') + ->desc('Download deployment') + ->groups(['api', 'sites']) + ->label('scope', 'functions.read') //TODO: Update the scope to sites later + ->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) + ->label('sdk.namespace', 'sites') + ->label('sdk.method', 'getDeploymentDownload') + ->label('sdk.description', '/docs/references/sites/get-deployment-download.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', '*/*') + ->label('sdk.methodType', 'location') + ->param('siteId', '', new UID(), 'Site ID.') + ->param('deploymentId', '', new UID(), 'Deployment ID.') + ->inject('response') + ->inject('request') + ->inject('dbForProject') + ->inject('deviceForSites') + ->inject('deviceForFunctions') //TODO: Remove this later + ->callback([$this, 'action']); + } + + public function action(string $siteId, string $deploymentId, Response $response, Request $request, Database $dbForProject, Device $deviceForSites, Device $deviceForFunctions) + { + $site = $dbForProject->getDocument('sites', $siteId); + if ($site->isEmpty()) { + throw new Exception(Exception::SITE_NOT_FOUND); + } + + $deployment = $dbForProject->getDocument('deployments', $deploymentId); + if ($deployment->isEmpty()) { + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); + } + + if ($deployment->getAttribute('resourceId') !== $site->getId()) { + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); + } + + $path = $deployment->getAttribute('path', ''); + if (!$deviceForFunctions->exists($path)) { + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); + } + + $response + ->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"'); + + $size = $deviceForFunctions->getFileSize($path); + $rangeHeader = $request->getHeader('range'); + + if (!empty($rangeHeader)) { + $start = $request->getRangeStart(); + $end = $request->getRangeEnd(); + $unit = $request->getRangeUnit(); + + if ($end === null) { + $end = min(($start + MAX_OUTPUT_CHUNK_SIZE - 1), ($size - 1)); + } + + if ($unit !== 'bytes' || $start >= $end || $end >= $size) { + throw new Exception(Exception::STORAGE_INVALID_RANGE); + } + + $response + ->addHeader('Accept-Ranges', 'bytes') + ->addHeader('Content-Range', 'bytes ' . $start . '-' . $end . '/' . $size) + ->addHeader('Content-Length', $end - $start + 1) + ->setStatusCode(Response::STATUS_CODE_PARTIALCONTENT); + + $response->send($deviceForFunctions->read($path, $start, ($end - $start + 1))); + } + + if ($size > APP_STORAGE_READ_BUFFER) { + for ($i = 0; $i < ceil($size / MAX_OUTPUT_CHUNK_SIZE); $i++) { + $response->chunk( + $deviceForFunctions->read( + $path, + ($i * MAX_OUTPUT_CHUNK_SIZE), + min(MAX_OUTPUT_CHUNK_SIZE, $size - ($i * MAX_OUTPUT_CHUNK_SIZE)) + ), + (($i + 1) * MAX_OUTPUT_CHUNK_SIZE) >= $size + ); + } + } else { + $response->send($deviceForFunctions->read($path)); + } + } +} diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/GetDeployment.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/GetDeployment.php new file mode 100644 index 0000000000..b83aa75c6e --- /dev/null +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/GetDeployment.php @@ -0,0 +1,70 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/sites/:siteId/deployments/:deploymentId') + ->desc('Get deployment') + ->groups(['api', 'sites']) + ->label('scope', 'functions.read') //TODO: Update the scope to sites later + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'sites') + ->label('sdk.method', 'getDeployment') + ->label('sdk.description', '/docs/references/sites/get-deployment.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_DEPLOYMENT) + ->param('siteId', '', new UID(), 'Site ID.') + ->param('deploymentId', '', new UID(), 'Deployment ID.') + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $siteId, string $deploymentId, Response $response, Database $dbForProject) + { + $site = $dbForProject->getDocument('sites', $siteId); + + if ($site->isEmpty()) { + throw new Exception(Exception::SITE_NOT_FOUND); + } + + $deployment = $dbForProject->getDocument('deployments', $deploymentId); + + if ($deployment->getAttribute('resourceId') !== $site->getId()) { + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); + } + + if ($deployment->isEmpty()) { + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); + } + + $build = $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')); + $deployment->setAttribute('status', $build->getAttribute('status', 'waiting')); + $deployment->setAttribute('buildLogs', $build->getAttribute('logs', '')); + $deployment->setAttribute('buildTime', $build->getAttribute('duration', 0)); + $deployment->setAttribute('buildSize', $build->getAttribute('size', 0)); + $deployment->setAttribute('size', $deployment->getAttribute('size', 0)); + + $response->dynamic($deployment, Response::MODEL_DEPLOYMENT); + } +} diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/ListDeployments.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/ListDeployments.php new file mode 100644 index 0000000000..2d2adb9572 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/ListDeployments.php @@ -0,0 +1,116 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/sites/:siteId/deployments') + ->desc('List deployments') + ->groups(['api', 'sites']) + ->label('scope', 'functions.read') //TODO: Update the scope to sites later + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'sites') + ->label('sdk.method', 'listDeployments') + ->label('sdk.description', '/docs/references/sites/list-deployments.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_DEPLOYMENT_LIST) + ->param('siteId', '', new UID(), 'Site ID.') + ->param('queries', [], new Deployments(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Deployments::ALLOWED_ATTRIBUTES), true) + ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) + ->inject('response') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $siteId, array $queries, string $search, Response $response, Database $dbForProject) + { + $site = $dbForProject->getDocument('sites', $siteId); + + if ($site->isEmpty()) { + throw new Exception(Exception::SITE_NOT_FOUND); + } + + try { + $queries = Query::parseQueries($queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + + if (!empty($search)) { + $queries[] = Query::search('search', $search); + } + + // Set resource queries + $queries[] = Query::equal('resourceInternalId', [$site->getInternalId()]); + $queries[] = Query::equal('resourceType', ['sites']); + + /** + * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries + */ + $cursor = \array_filter($queries, function ($query) { + return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); + }); + $cursor = reset($cursor); + if ($cursor) { + /** @var Query $cursor */ + + $validator = new Cursor(); + if (!$validator->isValid($cursor)) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); + } + + $deploymentId = $cursor->getValue(); + $cursorDocument = $dbForProject->getDocument('deployments', $deploymentId); + + if ($cursorDocument->isEmpty()) { + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Deployment '{$deploymentId}' for the 'cursor' value not found."); + } + + $cursor->setValue($cursorDocument); + } + + $filterQueries = Query::groupByType($queries)['filters']; + + $results = $dbForProject->find('deployments', $queries); + $total = $dbForProject->count('deployments', $filterQueries, APP_LIMIT_COUNT); + + foreach ($results as $result) { + $build = $dbForProject->getDocument('builds', $result->getAttribute('buildId', '')); + $result->setAttribute('status', $build->getAttribute('status', 'processing')); + $result->setAttribute('buildLogs', $build->getAttribute('logs', '')); + $result->setAttribute('buildTime', $build->getAttribute('duration', 0)); + $result->setAttribute('buildSize', $build->getAttribute('size', 0)); + $result->setAttribute('size', $result->getAttribute('size', 0)); + } + + $response->dynamic(new Document([ + 'deployments' => $results, + 'total' => $total, + ]), Response::MODEL_DEPLOYMENT_LIST); + } +} diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/RebuildDeployment.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/RebuildDeployment.php new file mode 100644 index 0000000000..ee09cf9fd0 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/RebuildDeployment.php @@ -0,0 +1,99 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/sites/:siteId/deployments/:deploymentId/build') + ->desc('Rebuild deployment') + ->groups(['api', 'sites']) + ->label('scope', 'functions.write') //TODO: Update the scope to sites later + ->label('event', 'sites.[siteId].deployments.[deploymentId].update') + ->label('audits.event', 'deployment.update') + ->label('audits.resource', 'site/{request.siteId}') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'sites') + ->label('sdk.method', 'createBuild') + ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) + ->label('sdk.response.model', Response::MODEL_NONE) + ->param('siteId', '', new UID(), 'Site ID.') + ->param('deploymentId', '', new UID(), 'Deployment ID.') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('queueForBuilds') + ->inject('deviceForSites') + ->inject('deviceForFunctions') //TODO: remove it later + ->callback([$this, 'action']); + } + + public function action(string $siteId, string $deploymentId, Response $response, Database $dbForProject, Event $queueForEvents, Build $queueForBuilds, Device $deviceForSites, Device $deviceForFunctions) + { + $site = $dbForProject->getDocument('sites', $siteId); + + if ($site->isEmpty()) { + throw new Exception(Exception::SITE_NOT_FOUND); + } + $deployment = $dbForProject->getDocument('deployments', $deploymentId); + + if ($deployment->isEmpty()) { + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); + } + + $path = $deployment->getAttribute('path'); + if (empty($path) || !$deviceForFunctions->exists($path)) { + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); + } + + $deploymentId = ID::unique(); + + $destination = $deviceForFunctions->getPath($deploymentId . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION)); + $deviceForFunctions->transfer($path, $destination, $deviceForFunctions); + + $deployment->removeAttribute('$internalId'); + $deployment = $dbForProject->createDocument('deployments', $deployment->setAttributes([ + '$internalId' => '', + '$id' => $deploymentId, + 'buildId' => '', + 'buildInternalId' => '', + 'path' => $destination, + 'buildCommand' => $site->getAttribute('buildCommand', ''), + 'installCommand' => $site->getAttribute('installCommand', ''), + 'outputDirectory' => $site->getAttribute('outputDirectory', ''), + 'search' => implode(' ', [$deploymentId]), + ])); + + $queueForBuilds + ->setType(BUILD_TYPE_DEPLOYMENT) + ->setResource($site) + ->setDeployment($deployment); + + $queueForEvents + ->setParam('siteId', $site->getId()) + ->setParam('deploymentId', $deployment->getId()); + + $response->noContent(); + } +} diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/UpdateDeployment.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/UpdateDeployment.php new file mode 100644 index 0000000000..ea798a1513 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/UpdateDeployment.php @@ -0,0 +1,83 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/sites/:siteId/deployments/:deploymentId') + ->desc('Update deployment') + ->groups(['api', 'sites']) + ->label('scope', 'functions.write') //TODO: Update the scope to sites later + ->label('event', 'sites.[siteId].deployments.[deploymentId].update') + ->label('audits.event', 'deployment.update') + ->label('audits.resource', 'site/{request.siteId}') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'sites') + ->label('sdk.method', 'updateDeployment') + ->label('sdk.description', '/docs/references/sites/update-site-deployment.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_SITE) + ->param('siteId', '', new UID(), 'Site ID.') + ->param('deploymentId', '', new UID(), 'Deployment ID.') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('dbForConsole') + ->callback([$this, 'action']); + } + + public function action(string $siteId, string $deploymentId, Response $response, Database $dbForProject, Event $queueForEvents, Database $dbForConsole) + { + $site = $dbForProject->getDocument('sites', $siteId); + $deployment = $dbForProject->getDocument('deployments', $deploymentId); + $build = $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')); + + if ($site->isEmpty()) { + throw new Exception(Exception::SITE_NOT_FOUND); + } + + if ($deployment->isEmpty()) { + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); + } + + if ($build->isEmpty()) { + throw new Exception(Exception::BUILD_NOT_FOUND); + } + + if ($build->getAttribute('status') !== 'ready') { + throw new Exception(Exception::BUILD_NOT_READY); + } + + $site = $dbForProject->updateDocument('sites', $site->getId(), new Document(array_merge($site->getArrayCopy(), [ + 'deploymentInternalId' => $deployment->getInternalId(), + 'deploymentId' => $deployment->getId(), + ]))); + + $queueForEvents + ->setParam('siteId', $site->getId()) + ->setParam('deploymentId', $deployment->getId()); + + $response->dynamic($site, Response::MODEL_SITE); + } +} diff --git a/src/Appwrite/Platform/Modules/Sites/Services/Http.php b/src/Appwrite/Platform/Modules/Sites/Services/Http.php index fcfd122c02..7a66646ab3 100644 --- a/src/Appwrite/Platform/Modules/Sites/Services/Http.php +++ b/src/Appwrite/Platform/Modules/Sites/Services/Http.php @@ -2,7 +2,14 @@ namespace Appwrite\Platform\Modules\Sites\Services; +use Appwrite\Platform\Modules\Sites\Http\Deployments\CancelDeployment; use Appwrite\Platform\Modules\Sites\Http\Deployments\CreateDeployment; +use Appwrite\Platform\Modules\Sites\Http\Deployments\DeleteDeployment; +use Appwrite\Platform\Modules\Sites\Http\Deployments\DownloadDeployment; +use Appwrite\Platform\Modules\Sites\Http\Deployments\GetDeployment; +use Appwrite\Platform\Modules\Sites\Http\Deployments\ListDeployments; +use Appwrite\Platform\Modules\Sites\Http\Deployments\RebuildDeployment; +use Appwrite\Platform\Modules\Sites\Http\Deployments\UpdateDeployment; use Appwrite\Platform\Modules\Sites\Http\Sites\CreateSite; use Appwrite\Platform\Modules\Sites\Http\Sites\GetSite; use Appwrite\Platform\Modules\Sites\Http\Sites\ListFrameworks; @@ -21,5 +28,12 @@ class Http extends Service $this->addAction(UpdateSite::getName(), new UpdateSite()); $this->addAction(ListFrameworks::getName(), new ListFrameworks()); $this->addAction(CreateDeployment::getName(), new CreateDeployment()); + $this->addAction(GetDeployment::getName(), new GetDeployment()); + $this->addAction(ListDeployments::getName(), new ListDeployments()); + $this->addAction(UpdateDeployment::getName(), new UpdateDeployment()); + $this->addAction(DeleteDeployment::getName(), new DeleteDeployment()); + $this->addAction(DownloadDeployment::getName(), new DownloadDeployment()); + $this->addAction(RebuildDeployment::getName(), new RebuildDeployment()); + $this->addAction(CancelDeployment::getName(), new CancelDeployment()); } }