From 0e793fdaf5863688d26a0a63842edc4687a9429b Mon Sep 17 00:00:00 2001 From: Michael Leone Date: Fri, 30 Jan 2026 15:41:25 -0500 Subject: [PATCH 1/2] feat: implement delete for resource by identifier * adds the ability to delete a resource by identifier over the api fix: remove commented code fix: update openapi to match responses * update openapi to match not found response fix: add 500 error handling * adds 500 error handling for deleteResrouceByIdentifier endpoint --- apps/api/openapi/lib/openapi.libsonnet | 39 ++- apps/api/openapi/openapi.json | 53 ++++ apps/api/openapi/paths/resources.jsonnet | 21 +- .../api/src/routes/v1/workspaces/resources.ts | 283 ++++++++++-------- apps/api/src/types/openapi.ts | 47 ++- 5 files changed, 305 insertions(+), 138 deletions(-) diff --git a/apps/api/openapi/lib/openapi.libsonnet b/apps/api/openapi/lib/openapi.libsonnet index 349cf1dfb..11b94f2fa 100644 --- a/apps/api/openapi/lib/openapi.libsonnet +++ b/apps/api/openapi/lib/openapi.libsonnet @@ -25,7 +25,7 @@ providerIdParam():: self.stringParam('providerId', 'ID of the resource provider'), relationshipRuleIdParam():: self.stringParam('relationshipRuleId', 'ID of the relationship rule'), - limitParam(defaultValue = 50):: { + limitParam(defaultValue=50):: { name: 'limit', 'in': 'query', required: false, @@ -37,7 +37,7 @@ default: defaultValue, }, }, - + offsetParam():: { name: 'offset', 'in': 'query', @@ -59,16 +59,16 @@ type: 'string', }, }, - + // Response helpers - schemaRef(name, nullable = false):: ( + schemaRef(name, nullable=false):: ( if nullable then { '$ref': '#/components/schemas/' + name, nullable: true } else { '$ref': '#/components/schemas/' + name } ), - - okResponse(schema, description = "OK response"):: { + + okResponse(schema, description='OK response'):: { '200': { description: description, content: { @@ -78,8 +78,8 @@ }, }, }, - - acceptedResponse(schema, description = "Accepted response"):: { + + acceptedResponse(schema, description='Accepted response'):: { '202': { description: description, content: { @@ -90,7 +90,7 @@ }, }, - createdResponse(schema, description = "Resource created successfully"):: { + createdResponse(schema, description='Resource created successfully'):: { '201': { description: description, content: { @@ -101,6 +101,12 @@ }, }, + noContent():: { + '204': { + description: 'No content', + }, + }, + notFoundResponse():: { '404': { description: 'Resource not found', @@ -111,7 +117,7 @@ }, }, }, - + badRequestResponse():: { '400': { description: 'Invalid request', @@ -123,7 +129,18 @@ }, }, - paginatedResponse(itemsSchema, description = "Paginated list of items"):: { + internalServerError():: { + '500': { + description: 'Internal server error', + content: { + 'application/json': { + schema: { '$ref': '#/components/schemas/ErrorResponse' }, + }, + }, + }, + }, + + paginatedResponse(itemsSchema, description='Paginated list of items'):: { '200': { description: description, content: { diff --git a/apps/api/openapi/openapi.json b/apps/api/openapi/openapi.json index bc2406531..a0c36221e 100644 --- a/apps/api/openapi/openapi.json +++ b/apps/api/openapi/openapi.json @@ -5647,6 +5647,59 @@ } }, "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}": { + "delete": { + "description": "Deletes a resource by its identifier.", + "operationId": "deleteResourceByIdentifier", + "parameters": [ + { + "description": "ID of the workspace", + "in": "path", + "name": "workspaceId", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Identifier of the resource", + "in": "path", + "name": "identifier", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No content" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Invalid request" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Resource not found" + } + }, + "summary": "Delete resource by identifier", + "tags": [ + "Resources" + ] + }, "get": { "description": "Returns a resource by its identifier.", "operationId": "getResourceByIdentifier", diff --git a/apps/api/openapi/paths/resources.jsonnet b/apps/api/openapi/paths/resources.jsonnet index 62b4649dc..5f7d6a432 100644 --- a/apps/api/openapi/paths/resources.jsonnet +++ b/apps/api/openapi/paths/resources.jsonnet @@ -29,6 +29,17 @@ local openapi = import '../lib/openapi.libsonnet'; ], responses: openapi.okResponse(openapi.schemaRef('Resource')), }, + delete: { + tags: ['Resources'], + summary: 'Delete resource by identifier', + operationId: 'deleteResourceByIdentifier', + description: 'Deletes a resource by its identifier.', + parameters: [ + openapi.workspaceIdParam(), + openapi.identifierParam(), + ], + responses: openapi.noContent() + openapi.notFoundResponse() + openapi.badRequestResponse(), + }, }, '/v1/workspaces/{workspaceId}/resources/identifier/{identifier}/variables': { get: { @@ -42,9 +53,9 @@ local openapi = import '../lib/openapi.libsonnet'; openapi.limitParam(), openapi.offsetParam(), ], - responses: openapi.paginatedResponse(openapi.schemaRef('ResourceVariable'), 'The requested variables') + - openapi.notFoundResponse() + - openapi.badRequestResponse(), + responses: openapi.paginatedResponse(openapi.schemaRef('ResourceVariable'), 'The requested variables') + + openapi.notFoundResponse() + + openapi.badRequestResponse(), }, patch: { tags: ['Resources'], @@ -87,8 +98,8 @@ local openapi = import '../lib/openapi.libsonnet'; openapi.deploymentIdParam(), ], responses: openapi.okResponse(openapi.schemaRef('ReleaseTarget'), 'The requested release target') + - openapi.notFoundResponse() + - openapi.badRequestResponse(), + openapi.notFoundResponse() + + openapi.badRequestResponse(), }, }, } diff --git a/apps/api/src/routes/v1/workspaces/resources.ts b/apps/api/src/routes/v1/workspaces/resources.ts index 61a8fe550..28e885863 100644 --- a/apps/api/src/routes/v1/workspaces/resources.ts +++ b/apps/api/src/routes/v1/workspaces/resources.ts @@ -6,156 +6,197 @@ import { Event, sendGoEvent } from "@ctrlplane/events"; import { getClientFor } from "@ctrlplane/workspace-engine-sdk"; const listResources: AsyncTypedHandler< - "/v1/workspaces/{workspaceId}/resources", - "get" + "/v1/workspaces/{workspaceId}/resources", + "get" > = async (req, res) => { - const { workspaceId } = req.params; - const { limit, offset, cel } = req.query; - - const decodedCel = - typeof cel === "string" ? decodeURIComponent(cel.replace(/\+/g, " ")) : cel; - - const result = await getClientFor(workspaceId).POST( - "/v1/workspaces/{workspaceId}/resources/query", - { - body: { - filter: decodedCel != null ? { cel: decodedCel } : undefined, - }, - params: { - path: { workspaceId }, - query: { limit, offset }, - }, - }, - ); - - if (result.error != null) - throw new ApiError( - result.error.error ?? "Failed to list resources", - result.response.status, + const { workspaceId } = req.params; + const { limit, offset, cel } = req.query; + + const decodedCel = + typeof cel === "string" ? decodeURIComponent(cel.replace(/\+/g, " ")) : cel; + + const result = await getClientFor(workspaceId).POST( + "/v1/workspaces/{workspaceId}/resources/query", + { + body: { + filter: decodedCel != null ? { cel: decodedCel } : undefined, + }, + params: { + path: { workspaceId }, + query: { limit, offset }, + }, + }, ); - res.status(200).json(result.data); + if (result.error != null) + throw new ApiError( + result.error.error ?? "Failed to list resources", + result.response.status, + ); + + res.status(200).json(result.data); }; const getResourceByIdentifier: AsyncTypedHandler< - "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}", - "get" + "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}", + "get" > = async (req, res) => { - const { workspaceId, identifier } = req.params; - - const resourceIdentifier = encodeURIComponent(identifier); - const result = await getClientFor(workspaceId).GET( - "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}", - { params: { path: { workspaceId, resourceIdentifier } } }, - ); - - if (result.error != null) - throw new ApiError( - result.error.error ?? "Resource not found", - result.response.status, + const { workspaceId, identifier } = req.params; + + const resourceIdentifier = encodeURIComponent(identifier); + const result = await getClientFor(workspaceId).GET( + "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}", + { params: { path: { workspaceId, resourceIdentifier } } }, ); - res.status(200).json(result.data); + if (result.error != null) + throw new ApiError( + result.error.error ?? "Resource not found", + result.response.status, + ); + + res.status(200).json(result.data); }; -const getVariablesForResource: AsyncTypedHandler< - "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}/variables", - "get" +const deleteResourceByIdentifier: AsyncTypedHandler< + "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}", + "delete" > = async (req, res) => { - const { workspaceId, identifier } = req.params; - const { limit, offset } = req.query; - - const resourceIdentifier = encodeURIComponent(identifier); - const result = await getClientFor(workspaceId).GET( - "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}/variables", - { - params: { path: { workspaceId, resourceIdentifier } }, - query: { limit, offset }, - }, - ); - - if (result.error != null) - throw new ApiError( - result.error.error ?? "Failed to get variables for resource", - result.response.status, + const { workspaceId, identifier } = req.params; + + const resourceIdentifier = encodeURIComponent(identifier); + const resourceResponse = await getClientFor(workspaceId).GET( + "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}", + { params: { path: { workspaceId, resourceIdentifier } } }, ); - res.status(200).json(result.data); + if (resourceResponse.error != null) { + const status = resourceResponse.response.status; + if (status >= 500) { + throw new ApiError( + resourceResponse.error.error ?? "Internal server error", + status, + ); + } + throw new ApiError( + resourceResponse.error.error ?? "Resource not found", + status, + ); + } + + await sendGoEvent({ + workspaceId, + eventType: Event.ResourceDeleted, + timestamp: Date.now(), + data: resourceResponse.data, + }); + + res.status(204).end(); }; -const updateVariablesForResource: AsyncTypedHandler< - "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}/variables", - "patch" + +const getVariablesForResource: AsyncTypedHandler< + "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}/variables", + "get" > = async (req, res) => { - const { workspaceId, identifier } = req.params; - const { body } = req; - - const resourceIdentifier = encodeURIComponent(identifier); - const resourceResponse = await getClientFor(workspaceId).GET( - "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}", - { params: { path: { workspaceId, resourceIdentifier } } }, - ); - - if (resourceResponse.error != null) { - throw new ApiError( - resourceResponse.error.error ?? "Failed to get resource", - resourceResponse.response.status, + const { workspaceId, identifier } = req.params; + const { limit, offset } = req.query; + + const resourceIdentifier = encodeURIComponent(identifier); + const result = await getClientFor(workspaceId).GET( + "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}/variables", + { + params: { path: { workspaceId, resourceIdentifier } }, + query: { limit, offset }, + }, ); - } - await sendGoEvent({ - workspaceId, - eventType: Event.ResourceVariablesBulkUpdated, - timestamp: Date.now(), - data: { resourceId: resourceResponse.data.id, variables: body }, - }); + if (result.error != null) + throw new ApiError( + result.error.error ?? "Failed to get variables for resource", + result.response.status, + ); - res.status(204).end(); + res.status(200).json(result.data); }; -const getReleaseTargetForResourceInDeployment: AsyncTypedHandler< - "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}/release-targets/deployment/{deploymentId}", - "get" +const updateVariablesForResource: AsyncTypedHandler< + "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}/variables", + "patch" > = async (req, res) => { - const { workspaceId, resourceIdentifier, deploymentId } = req.params; + const { workspaceId, identifier } = req.params; + const { body } = req; - const encodedResourceIdentifier = encodeURIComponent(resourceIdentifier); + const resourceIdentifier = encodeURIComponent(identifier); + const resourceResponse = await getClientFor(workspaceId).GET( + "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}", + { params: { path: { workspaceId, resourceIdentifier } } }, + ); + + if (resourceResponse.error != null) { + throw new ApiError( + resourceResponse.error.error ?? "Failed to get resource", + resourceResponse.response.status, + ); + } + + await sendGoEvent({ + workspaceId, + eventType: Event.ResourceVariablesBulkUpdated, + timestamp: Date.now(), + data: { resourceId: resourceResponse.data.id, variables: body }, + }); + + res.status(204).end(); +}; - const result = await getClientFor(workspaceId).GET( +const getReleaseTargetForResourceInDeployment: AsyncTypedHandler< "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}/release-targets/deployment/{deploymentId}", - { - params: { - path: { - workspaceId, - resourceIdentifier: encodedResourceIdentifier, - deploymentId, + "get" +> = async (req, res) => { + const { workspaceId, resourceIdentifier, deploymentId } = req.params; + + const encodedResourceIdentifier = encodeURIComponent(resourceIdentifier); + + const result = await getClientFor(workspaceId).GET( + "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}/release-targets/deployment/{deploymentId}", + { + params: { + path: { + workspaceId, + resourceIdentifier: encodedResourceIdentifier, + deploymentId, + }, + }, }, - }, - }, - ); - - if (result.error != null) - throw new ApiError( - result.error.error ?? - "Failed to get release target for resource in deployment", - result.response.status, ); - res.status(200).json(result.data); + if (result.error != null) + throw new ApiError( + result.error.error ?? + "Failed to get release target for resource in deployment", + result.response.status, + ); + + res.status(200).json(result.data); }; export const resourceRouter = Router({ mergeParams: true }) - .get("/", asyncHandler(listResources)) - .get("/identifier/:identifier", asyncHandler(getResourceByIdentifier)) - .get( - "/identifier/:identifier/variables", - asyncHandler(getVariablesForResource), - ) - .patch( - "/identifier/:identifier/variables", - asyncHandler(updateVariablesForResource), - ) - .get( - "/identifier/:identifier/release-targets/deployment/:deploymentId", - asyncHandler(getReleaseTargetForResourceInDeployment), - ); + .get("/", asyncHandler(listResources)) + .get("/identifier/:identifier", asyncHandler(getResourceByIdentifier)) + .delete( + "/identifier/:identifier", + asyncHandler(deleteResourceByIdentifier), + ) + .get( + "/identifier/:identifier/variables", + asyncHandler(getVariablesForResource), + ) + .patch( + "/identifier/:identifier/variables", + asyncHandler(updateVariablesForResource), + ) + .get( + "/identifier/:identifier/release-targets/deployment/:deploymentId", + asyncHandler(getReleaseTargetForResourceInDeployment), + ); diff --git a/apps/api/src/types/openapi.ts b/apps/api/src/types/openapi.ts index a303b31da..6d94ddb04 100644 --- a/apps/api/src/types/openapi.ts +++ b/apps/api/src/types/openapi.ts @@ -662,7 +662,11 @@ export interface paths { get: operations["getResourceByIdentifier"]; put?: never; post?: never; - delete?: never; + /** + * Delete resource by identifier + * @description Deletes a resource by its identifier. + */ + delete: operations["deleteResourceByIdentifier"]; options?: never; head?: never; patch?: never; @@ -3754,6 +3758,47 @@ export interface operations { }; }; }; + deleteResourceByIdentifier: { + parameters: { + query?: never; + header?: never; + path: { + /** @description ID of the workspace */ + workspaceId: string; + /** @description Identifier of the resource */ + identifier: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description No content */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Invalid request */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + /** @description Resource not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ErrorResponse"]; + }; + }; + }; + }; getVariablesForResource: { parameters: { query?: { From 8bb99fb68fe97d3f4adce0f75b6392f13d403c9e Mon Sep 17 00:00:00 2001 From: Michael Leone Date: Mon, 2 Feb 2026 09:10:39 -0500 Subject: [PATCH 2/2] fix: formatting --- .../api/src/routes/v1/workspaces/resources.ts | 305 +++++++++--------- 1 file changed, 150 insertions(+), 155 deletions(-) diff --git a/apps/api/src/routes/v1/workspaces/resources.ts b/apps/api/src/routes/v1/workspaces/resources.ts index 28e885863..95b10fc39 100644 --- a/apps/api/src/routes/v1/workspaces/resources.ts +++ b/apps/api/src/routes/v1/workspaces/resources.ts @@ -6,197 +6,192 @@ import { Event, sendGoEvent } from "@ctrlplane/events"; import { getClientFor } from "@ctrlplane/workspace-engine-sdk"; const listResources: AsyncTypedHandler< - "/v1/workspaces/{workspaceId}/resources", - "get" + "/v1/workspaces/{workspaceId}/resources", + "get" > = async (req, res) => { - const { workspaceId } = req.params; - const { limit, offset, cel } = req.query; - - const decodedCel = - typeof cel === "string" ? decodeURIComponent(cel.replace(/\+/g, " ")) : cel; - - const result = await getClientFor(workspaceId).POST( - "/v1/workspaces/{workspaceId}/resources/query", - { - body: { - filter: decodedCel != null ? { cel: decodedCel } : undefined, - }, - params: { - path: { workspaceId }, - query: { limit, offset }, - }, - }, + const { workspaceId } = req.params; + const { limit, offset, cel } = req.query; + + const decodedCel = + typeof cel === "string" ? decodeURIComponent(cel.replace(/\+/g, " ")) : cel; + + const result = await getClientFor(workspaceId).POST( + "/v1/workspaces/{workspaceId}/resources/query", + { + body: { + filter: decodedCel != null ? { cel: decodedCel } : undefined, + }, + params: { + path: { workspaceId }, + query: { limit, offset }, + }, + }, + ); + + if (result.error != null) + throw new ApiError( + result.error.error ?? "Failed to list resources", + result.response.status, ); - if (result.error != null) - throw new ApiError( - result.error.error ?? "Failed to list resources", - result.response.status, - ); - - res.status(200).json(result.data); + res.status(200).json(result.data); }; const getResourceByIdentifier: AsyncTypedHandler< - "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}", - "get" + "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}", + "get" > = async (req, res) => { - const { workspaceId, identifier } = req.params; - - const resourceIdentifier = encodeURIComponent(identifier); - const result = await getClientFor(workspaceId).GET( - "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}", - { params: { path: { workspaceId, resourceIdentifier } } }, + const { workspaceId, identifier } = req.params; + + const resourceIdentifier = encodeURIComponent(identifier); + const result = await getClientFor(workspaceId).GET( + "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}", + { params: { path: { workspaceId, resourceIdentifier } } }, + ); + + if (result.error != null) + throw new ApiError( + result.error.error ?? "Resource not found", + result.response.status, ); - if (result.error != null) - throw new ApiError( - result.error.error ?? "Resource not found", - result.response.status, - ); - - res.status(200).json(result.data); + res.status(200).json(result.data); }; const deleteResourceByIdentifier: AsyncTypedHandler< - "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}", - "delete" + "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}", + "delete" > = async (req, res) => { - const { workspaceId, identifier } = req.params; - - const resourceIdentifier = encodeURIComponent(identifier); - const resourceResponse = await getClientFor(workspaceId).GET( - "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}", - { params: { path: { workspaceId, resourceIdentifier } } }, - ); - - if (resourceResponse.error != null) { - const status = resourceResponse.response.status; - if (status >= 500) { - throw new ApiError( - resourceResponse.error.error ?? "Internal server error", - status, - ); - } - throw new ApiError( - resourceResponse.error.error ?? "Resource not found", - status, - ); + const { workspaceId, identifier } = req.params; + + const resourceIdentifier = encodeURIComponent(identifier); + const resourceResponse = await getClientFor(workspaceId).GET( + "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}", + { params: { path: { workspaceId, resourceIdentifier } } }, + ); + if (resourceResponse.error != null) { + const status = resourceResponse.response.status; + if (status >= 500) { + throw new ApiError( + resourceResponse.error.error ?? "Internal server error", + status, + ); } + throw new ApiError( + resourceResponse.error.error ?? "Resource not found", + status, + ); + } - await sendGoEvent({ - workspaceId, - eventType: Event.ResourceDeleted, - timestamp: Date.now(), - data: resourceResponse.data, - }); + await sendGoEvent({ + workspaceId, + eventType: Event.ResourceDeleted, + timestamp: Date.now(), + data: resourceResponse.data, + }); - res.status(204).end(); + res.status(204).end(); }; - const getVariablesForResource: AsyncTypedHandler< - "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}/variables", - "get" + "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}/variables", + "get" > = async (req, res) => { - const { workspaceId, identifier } = req.params; - const { limit, offset } = req.query; - - const resourceIdentifier = encodeURIComponent(identifier); - const result = await getClientFor(workspaceId).GET( - "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}/variables", - { - params: { path: { workspaceId, resourceIdentifier } }, - query: { limit, offset }, - }, + const { workspaceId, identifier } = req.params; + const { limit, offset } = req.query; + + const resourceIdentifier = encodeURIComponent(identifier); + const result = await getClientFor(workspaceId).GET( + "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}/variables", + { + params: { path: { workspaceId, resourceIdentifier } }, + query: { limit, offset }, + }, + ); + + if (result.error != null) + throw new ApiError( + result.error.error ?? "Failed to get variables for resource", + result.response.status, ); - if (result.error != null) - throw new ApiError( - result.error.error ?? "Failed to get variables for resource", - result.response.status, - ); - - res.status(200).json(result.data); + res.status(200).json(result.data); }; const updateVariablesForResource: AsyncTypedHandler< - "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}/variables", - "patch" + "/v1/workspaces/{workspaceId}/resources/identifier/{identifier}/variables", + "patch" > = async (req, res) => { - const { workspaceId, identifier } = req.params; - const { body } = req; - - const resourceIdentifier = encodeURIComponent(identifier); - const resourceResponse = await getClientFor(workspaceId).GET( - "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}", - { params: { path: { workspaceId, resourceIdentifier } } }, + const { workspaceId, identifier } = req.params; + const { body } = req; + + const resourceIdentifier = encodeURIComponent(identifier); + const resourceResponse = await getClientFor(workspaceId).GET( + "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}", + { params: { path: { workspaceId, resourceIdentifier } } }, + ); + + if (resourceResponse.error != null) { + throw new ApiError( + resourceResponse.error.error ?? "Failed to get resource", + resourceResponse.response.status, ); + } - if (resourceResponse.error != null) { - throw new ApiError( - resourceResponse.error.error ?? "Failed to get resource", - resourceResponse.response.status, - ); - } + await sendGoEvent({ + workspaceId, + eventType: Event.ResourceVariablesBulkUpdated, + timestamp: Date.now(), + data: { resourceId: resourceResponse.data.id, variables: body }, + }); - await sendGoEvent({ - workspaceId, - eventType: Event.ResourceVariablesBulkUpdated, - timestamp: Date.now(), - data: { resourceId: resourceResponse.data.id, variables: body }, - }); - - res.status(204).end(); + res.status(204).end(); }; const getReleaseTargetForResourceInDeployment: AsyncTypedHandler< - "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}/release-targets/deployment/{deploymentId}", - "get" + "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}/release-targets/deployment/{deploymentId}", + "get" > = async (req, res) => { - const { workspaceId, resourceIdentifier, deploymentId } = req.params; - - const encodedResourceIdentifier = encodeURIComponent(resourceIdentifier); - - const result = await getClientFor(workspaceId).GET( - "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}/release-targets/deployment/{deploymentId}", - { - params: { - path: { - workspaceId, - resourceIdentifier: encodedResourceIdentifier, - deploymentId, - }, - }, + const { workspaceId, resourceIdentifier, deploymentId } = req.params; + + const encodedResourceIdentifier = encodeURIComponent(resourceIdentifier); + + const result = await getClientFor(workspaceId).GET( + "/v1/workspaces/{workspaceId}/resources/{resourceIdentifier}/release-targets/deployment/{deploymentId}", + { + params: { + path: { + workspaceId, + resourceIdentifier: encodedResourceIdentifier, + deploymentId, }, + }, + }, + ); + + if (result.error != null) + throw new ApiError( + result.error.error ?? + "Failed to get release target for resource in deployment", + result.response.status, ); - if (result.error != null) - throw new ApiError( - result.error.error ?? - "Failed to get release target for resource in deployment", - result.response.status, - ); - - res.status(200).json(result.data); + res.status(200).json(result.data); }; export const resourceRouter = Router({ mergeParams: true }) - .get("/", asyncHandler(listResources)) - .get("/identifier/:identifier", asyncHandler(getResourceByIdentifier)) - .delete( - "/identifier/:identifier", - asyncHandler(deleteResourceByIdentifier), - ) - .get( - "/identifier/:identifier/variables", - asyncHandler(getVariablesForResource), - ) - .patch( - "/identifier/:identifier/variables", - asyncHandler(updateVariablesForResource), - ) - .get( - "/identifier/:identifier/release-targets/deployment/:deploymentId", - asyncHandler(getReleaseTargetForResourceInDeployment), - ); + .get("/", asyncHandler(listResources)) + .get("/identifier/:identifier", asyncHandler(getResourceByIdentifier)) + .delete("/identifier/:identifier", asyncHandler(deleteResourceByIdentifier)) + .get( + "/identifier/:identifier/variables", + asyncHandler(getVariablesForResource), + ) + .patch( + "/identifier/:identifier/variables", + asyncHandler(updateVariablesForResource), + ) + .get( + "/identifier/:identifier/release-targets/deployment/:deploymentId", + asyncHandler(getReleaseTargetForResourceInDeployment), + );