From 88e354f4abe335be0c50cf0dce23c6bef5edeef7 Mon Sep 17 00:00:00 2001 From: Pankaj Sha Date: Fri, 3 Jan 2025 01:57:54 +0530 Subject: [PATCH] feat: add feature to update request before approval or rejection --- constants/requests.ts | 1 + controllers/requests.ts | 64 ++++++++++++++++++- .../validators/updateRequestValidator.ts | 31 +++++++++ routes/requests.ts | 5 +- types/requests.d.ts | 12 ++++ 5 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 middlewares/validators/updateRequestValidator.ts diff --git a/constants/requests.ts b/constants/requests.ts index 1c71d3adb..2751be85f 100644 --- a/constants/requests.ts +++ b/constants/requests.ts @@ -24,6 +24,7 @@ export const REQUEST_LOG_TYPE = { REQUEST_REJECTED: "REQUEST_REJECTED", REQUEST_BLOCKED: "REQUEST_BLOCKED", REQUEST_CANCELLED: "REQUEST_CANCELLED", + REQUEST_UPDATED: "REQUEST_UPDATED", }; export const REQUEST_CREATED_SUCCESSFULLY = "Request created successfully"; diff --git a/controllers/requests.ts b/controllers/requests.ts index d4bf87179..c37a397f3 100644 --- a/controllers/requests.ts +++ b/controllers/requests.ts @@ -1,6 +1,12 @@ +import firestore from "../utils/firestore"; import { ERROR_WHILE_FETCHING_REQUEST, + ERROR_WHILE_UPDATING_REQUEST, + LOG_ACTION, + REQUEST_DOES_NOT_EXIST, REQUEST_FETCHED_SUCCESSFULLY, + REQUEST_LOG_TYPE, + REQUEST_STATE, REQUEST_TYPE, } from "../constants/requests"; import { getRequests } from "../models/requests"; @@ -10,9 +16,11 @@ import { OooRequestCreateRequest, OooRequestResponse } from "../types/oooRequest import { CustomResponse } from "../typeDefinitions/global"; import { ExtensionRequestRequest, ExtensionRequestResponse } from "../types/extensionRequests"; import { createTaskExtensionRequest, updateTaskExtensionRequest } from "./extensionRequestsv2"; -import { UpdateRequest } from "../types/requests"; +import { UpdateRequest, UpdateRequestBeforeApproval } from "../types/requests"; import { TaskRequestRequest } from "../types/taskRequests"; import { createTaskRequestController } from "./taskRequestsv2"; +import { addLog } from "../models/logs"; +const requestModel = firestore.collection("requests"); export const createRequestController = async ( req: OooRequestCreateRequest | ExtensionRequestRequest | TaskRequestRequest, @@ -97,3 +105,57 @@ export const getRequestsController = async (req: any, res: any) => { return res.boom.badImplementation(ERROR_WHILE_FETCHING_REQUEST); } }; + +export const updateRequestBeforeApprovalController = async (req: UpdateRequestBeforeApproval, res: CustomResponse) => { + const body = req.body; + const id = req.params.id; + const lastModifiedBy = req?.userData?.id; + + try{ + const extensionRequest = await requestModel.doc(id).get() as unknown as {id: string, oldEndsOn: number, state: string}; + + if(!extensionRequest){ + return res.boom.notFound(REQUEST_DOES_NOT_EXIST); + } + + if(extensionRequest.oldEndsOn > body.newEndsOn) { + return res.boom.badRequest("Request new deadline must be greater than old deadline."); + } + + if(extensionRequest.state != REQUEST_STATE.PENDING){ + return res.boom.badRequest("Request state is not pending"); + } + + const requestBody = { + ...body, + lastModifiedBy, + updatedAt: Date.now(), + } + + await requestModel.doc(id).update(requestBody); + + const requestLog = { + type: REQUEST_LOG_TYPE.REQUEST_UPDATED, + meta: { + requestId: extensionRequest.id, + action: LOG_ACTION.UPDATE, + createdBy: lastModifiedBy, + createdAt: Date.now(), + }, + body: requestBody, + }; + + await addLog(requestLog.type, requestLog.meta, requestLog.body); + + return res.status(200).json({ + message: "Request updated successfully", + data: { + id: extensionRequest.id, + ...requestBody + } + }) + }catch(error){ + logger.error(ERROR_WHILE_UPDATING_REQUEST, error); + return res.boom.badImplementation(ERROR_WHILE_UPDATING_REQUEST); + } +} \ No newline at end of file diff --git a/middlewares/validators/updateRequestValidator.ts b/middlewares/validators/updateRequestValidator.ts new file mode 100644 index 000000000..f4ae10db5 --- /dev/null +++ b/middlewares/validators/updateRequestValidator.ts @@ -0,0 +1,31 @@ +import { NextFunction, Request } from "express"; +import joi from "joi"; +import { CustomResponse } from "../../types/global"; + +export const updateRequestValidator = async ( + req: Request, + res: CustomResponse, + next: NextFunction + ) => { + + const schema = joi + .object() + .strict() + .keys({ + reason: joi.string().optional(), + newEndsOn: joi.number().positive().min(Date.now()).required().messages({ + 'number.any': 'newEndsOn is required', + 'number.base': 'newEndsOn must be a number', + 'number.positive': 'newEndsOn must be positive', + 'number.greater': 'newEndsOn must be greater than current date', + })}) + + try { + await schema.validateAsync(req.body, { abortEarly: false }); + next(); + } catch (error) { + const errorMessages = error.details.map((detail:{message: string}) => detail.message); + logger.error(`Error while validating request payload : ${errorMessages}`); + return res.boom.badRequest(errorMessages); + } +}; \ No newline at end of file diff --git a/routes/requests.ts b/routes/requests.ts index 5cda581b6..fcf763983 100644 --- a/routes/requests.ts +++ b/routes/requests.ts @@ -5,9 +5,12 @@ const { SUPERUSER } = require("../constants/roles"); import authenticate from "../middlewares/authenticate"; import { createRequestsMiddleware,updateRequestsMiddleware,getRequestsMiddleware } from "../middlewares/validators/requests"; -import { createRequestController , updateRequestController, getRequestsController} from "../controllers/requests"; +import { createRequestController , updateRequestController, getRequestsController, updateRequestBeforeApprovalController, } from "../controllers/requests"; +import { updateRequestValidator } from "../middlewares/validators/updateRequestValidator"; router.get("/", getRequestsMiddleware, getRequestsController); router.post("/",authenticate, createRequestsMiddleware, createRequestController); router.put("/:id",authenticate, authorizeRoles([SUPERUSER]), updateRequestsMiddleware, updateRequestController); +router.patch("/:id", authenticate, updateRequestValidator, updateRequestBeforeApprovalController); module.exports = router; + diff --git a/types/requests.d.ts b/types/requests.d.ts index 6a903ff96..f20d0b14a 100644 --- a/types/requests.d.ts +++ b/types/requests.d.ts @@ -29,3 +29,15 @@ export type UpdateRequest = Request & { query: RequestQuery; params: RequestParams; }; + +export type UpdateRequestBeforeApprovalBody = { + reason?: string + newEndsOn: number +} + +export type UpdateRequestBeforeApproval = Request & { + body: UpdateRequestBeforeApprovalBody; + userData: userData; + query: RequestQuery; + params: RequestParams; +} \ No newline at end of file