diff --git a/backend/src/server/routes/v1/approval-policy-routers/approval-policy-endpoints.ts b/backend/src/server/routes/v1/approval-policy-routers/approval-policy-endpoints.ts index 77143fdb7..4a84f40ab 100644 --- a/backend/src/server/routes/v1/approval-policy-routers/approval-policy-endpoints.ts +++ b/backend/src/server/routes/v1/approval-policy-routers/approval-policy-endpoints.ts @@ -338,4 +338,31 @@ export const registerApprovalPolicyEndpoints =
({ return { request }; } }); + + server.route({ + method: "POST", + url: "/requests/:requestId/cancel", + config: { + rateLimit: writeLimit + }, + schema: { + description: "Cancel approval request", + params: z.object({ + requestId: z.string().uuid() + }), + response: { + 200: z.object({ + request: requestResponseSchema + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const { request } = await server.services.approvalPolicy.cancelRequest(req.params.requestId, req.permission); + + // TODO(andrey): Audit log + + return { request }; + } + }); }; diff --git a/backend/src/services/approval-policy/approval-policy-service.ts b/backend/src/services/approval-policy/approval-policy-service.ts index a66106113..d9493229f 100644 --- a/backend/src/services/approval-policy/approval-policy-service.ts +++ b/backend/src/services/approval-policy/approval-policy-service.ts @@ -673,6 +673,29 @@ export const approvalPolicyServiceFactory = ({ return { requests }; }; + const cancelRequest = async (requestId: string, actor: OrgServiceActor) => { + const request = await approvalRequestDAL.findById(requestId); + if (!request) { + throw new ForbiddenRequestError({ message: "Request not found" }); + } + + if (request.status !== ApprovalRequestStatus.Pending) { + throw new BadRequestError({ message: "Request is not pending" }); + } + + if (request.requesterId !== actor.id) { + throw new ForbiddenRequestError({ message: "You are not the requester of this request" }); + } + + const updatedRequest = await approvalRequestDAL.updateById(requestId, { + status: ApprovalRequestStatus.Cancelled + }); + + const steps = await approvalRequestDAL.findStepsByRequestId(requestId); + + return { request: { ...updatedRequest, steps } }; + }; + return { create, list, @@ -683,6 +706,7 @@ export const approvalPolicyServiceFactory = ({ listRequests, getRequestById, approveRequest, - rejectRequest + rejectRequest, + cancelRequest }; };