From 402cc8b47b144e2d6fff758521ca3474dc5cfd60 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Sat, 23 Aug 2025 06:16:08 +0530 Subject: [PATCH 01/23] initial commit --- constants/requests.ts | 4 + controllers/oooRequests.ts | 51 ++++++++++- controllers/requests.ts | 11 ++- middlewares/conditionalOooChecks.ts | 32 +++++++ middlewares/validators/oooRequests.ts | 46 +++++++++- middlewares/validators/requests.ts | 15 +++- models/requests.ts | 17 +++- routes/requests.ts | 3 +- services/oooRequest.ts | 121 ++++++++++++++++++++++++-- types/oooRequest.d.ts | 17 ++++ 10 files changed, 295 insertions(+), 22 deletions(-) create mode 100644 middlewares/conditionalOooChecks.ts diff --git a/constants/requests.ts b/constants/requests.ts index e9d5100127..b7fb33999a 100644 --- a/constants/requests.ts +++ b/constants/requests.ts @@ -26,6 +26,8 @@ export const REQUEST_LOG_TYPE = { REQUEST_CANCELLED: "REQUEST_CANCELLED", REQUEST_UPDATED: "REQUEST_UPDATED", PENDING_REQUEST_FOUND: "PENDING_REQUEST_FOUND", + REQUEST_ALREADY_APPROVED: "REQUEST_ALREADY_APPROVED", + REQUEST_ALREADY_REJECTED: "REQUEST_ALREADY_REJECTED", }; export const REQUEST_CREATED_SUCCESSFULLY = "Request created successfully"; @@ -39,7 +41,9 @@ export const REQUEST_ALREADY_REJECTED = "Request already rejected"; export const ERROR_WHILE_FETCHING_REQUEST = "Error while fetching request"; export const ERROR_WHILE_CREATING_REQUEST = "Error while creating request"; export const ERROR_WHILE_UPDATING_REQUEST = "Error while updating request"; +export const ERROR_WHILE_ACKNOWLEDGING_REQUEST = "Error while acknowledging request"; +export const REQUEST_ID_REQUIRED = "Request id is required"; export const REQUEST_DOES_NOT_EXIST = "Request does not exist"; export const REQUEST_ALREADY_PENDING = "Request already exists please wait for approval or rejection"; export const UNAUTHORIZED_TO_CREATE_OOO_REQUEST = "Unauthorized to create OOO request"; diff --git a/controllers/oooRequests.ts b/controllers/oooRequests.ts index 741c88cfac..1be13b5194 100644 --- a/controllers/oooRequests.ts +++ b/controllers/oooRequests.ts @@ -12,6 +12,9 @@ import { REQUEST_ALREADY_PENDING, USER_STATUS_NOT_FOUND, OOO_STATUS_ALREADY_EXIST, + UNAUTHORIZED_TO_UPDATE_REQUEST, + ERROR_WHILE_ACKNOWLEDGING_REQUEST, + REQUEST_ID_REQUIRED, } from "../constants/requests"; import { statusState } from "../constants/userStatus"; import { logType } from "../constants/logs"; @@ -20,9 +23,11 @@ import { getRequestByKeyValues, getRequests, updateRequest } from "../models/req import { createUserFutureStatus } from "../models/userFutureStatus"; import { getUserStatus, addFutureStatus } from "../models/userStatus"; import { createOooRequest, validateUserStatus } from "../services/oooRequest"; +import * as oooRequestService from "../services/oooRequest"; import { CustomResponse } from "../typeDefinitions/global"; -import { OooRequestCreateRequest, OooRequestResponse, OooStatusRequest } from "../types/oooRequest"; +import { AcknowledgeOooRequest, OooRequestCreateRequest, OooRequestResponse, OooStatusRequest } from "../types/oooRequest"; import { UpdateRequest } from "../types/requests"; +import { NextFunction } from "express"; /** * Controller to handle the creation of OOO requests. @@ -78,7 +83,7 @@ export const createOooRequestController = async ( return res.boom.conflict(REQUEST_ALREADY_PENDING); } - await createOooRequest(requestBody, username, userId); + await createOooRequest(requestBody, userId); return res.status(201).json({ message: REQUEST_CREATED_SUCCESSFULLY, @@ -148,3 +153,45 @@ export const updateOooRequestController = async (req: UpdateRequest, res: Custom return res.boom.badImplementation(ERROR_WHILE_UPDATING_REQUEST); } }; +/** + * Acknowledges an Out-of-Office (OOO) request + * + * @param {AcknowledgeOooRequest} req - The request object. + * @param {OooRequestResponse} res - The response object. + * @returns {Promise} Resolves with success or failure. + */ +export const acknowledgeOooRequest = async ( + req: AcknowledgeOooRequest, + res: OooRequestResponse, + next: NextFunction +) + : Promise => { + try { + const dev = req.query.dev === "true"; + if(!dev) return res.boom.notImplemented("Feature not implemented"); + + const isSuperuser = req.userData?.roles?.super_user; + if (!isSuperuser) { + return res.boom.forbidden(UNAUTHORIZED_TO_UPDATE_REQUEST); + } + + const requestBody = req.body; + const superUserId = req.userData.id; + const requestId = req.params.id; + + if (!requestId) { + return res.boom.badRequest(REQUEST_ID_REQUIRED); + } + + const response = await oooRequestService.acknowledgeOooRequest(requestId, requestBody, superUserId); + + return res.status(200).json({ + message: response.message, + }); + } + catch(error){ + logger.error(ERROR_WHILE_ACKNOWLEDGING_REQUEST, error); + next(error); + return res; + } +}; diff --git a/controllers/requests.ts b/controllers/requests.ts index fd8974ea0b..c8830f7fec 100644 --- a/controllers/requests.ts +++ b/controllers/requests.ts @@ -5,8 +5,8 @@ import { } from "../constants/requests"; import { getRequests } from "../models/requests"; import { getPaginatedLink } from "../utils/helper"; -import { createOooRequestController, updateOooRequestController } from "./oooRequests"; -import { OooRequestCreateRequest, OooRequestResponse } from "../types/oooRequest"; +import { acknowledgeOooRequest, createOooRequestController, updateOooRequestController } from "./oooRequests"; +import { AcknowledgeOooRequest, OooRequestCreateRequest, OooRequestResponse } from "../types/oooRequest"; import { CustomResponse } from "../typeDefinitions/global"; import { ExtensionRequestRequest, ExtensionRequestResponse } from "../types/extensionRequests"; import { createTaskExtensionRequest, updateTaskExtensionRequest } from "./extensionRequestsv2"; @@ -17,7 +17,7 @@ import { OnboardingExtensionCreateRequest, OnboardingExtensionResponse, UpdateOn import { createOnboardingExtensionRequestController, updateOnboardingExtensionRequestController, updateOnboardingExtensionRequestState } from "./onboardingExtension"; import { UpdateOnboardingExtensionRequest } from "../types/onboardingExtension"; -import { Request } from "express"; +import { Request, NextFunction } from "express"; export const createRequestController = async ( req: OooRequestCreateRequest | ExtensionRequestRequest | TaskRequestRequest | OnboardingExtensionCreateRequest, @@ -121,9 +121,12 @@ export const getRequestsController = async (req: any, res: any) => { * @param {CustomResponse} res - The response object. * @returns {Promise} Resolves or sends an error for invalid types. */ -export const updateRequestBeforeAcknowledgedController = async (req: Request, res: CustomResponse) => { +export const updateRequestBeforeAcknowledgedController = async (req: Request, res: CustomResponse, next: NextFunction) => { const type = req.body.type; switch(type){ + case REQUEST_TYPE.OOO: + await acknowledgeOooRequest(req as AcknowledgeOooRequest, res as OooRequestResponse, next); + break; case REQUEST_TYPE.ONBOARDING: await updateOnboardingExtensionRequestController(req as UpdateOnboardingExtensionRequest, res as OnboardingExtensionResponse); break; diff --git a/middlewares/conditionalOooChecks.ts b/middlewares/conditionalOooChecks.ts new file mode 100644 index 0000000000..a21803c6a2 --- /dev/null +++ b/middlewares/conditionalOooChecks.ts @@ -0,0 +1,32 @@ +import { NextFunction } from "express"; +import { CustomRequest, CustomResponse } from "../types/global"; +import { REQUEST_TYPE } from "../constants/requests"; +import { devFlagMiddleware } from "./devFlag"; +import authorizeRoles from "./authorizeRoles"; +const { SUPERUSER } = require("../constants/roles"); + +/** + * Conditional middleware that applies devFlag and superuser checks only for OOO requests. + * This allows onboarding requests to bypass these checks while maintaining security for OOO operations. + * + * @param {CustomRequest} req - The request object + * @param {CustomResponse} res - The response object + * @param {NextFunction} next - The next middleware function + */ +export const conditionalOooChecks = (req: CustomRequest, res: CustomResponse, next: NextFunction) => { + const requestType = req.body?.type; + + + if (requestType === REQUEST_TYPE.OOO) { + + devFlagMiddleware(req, res, (err: any) => { + if (err) return next(err); + + + authorizeRoles([SUPERUSER])(req, res, next); + }); + } else { + + next(); + } +}; \ No newline at end of file diff --git a/middlewares/validators/oooRequests.ts b/middlewares/validators/oooRequests.ts index ab73929f16..5bf4ccda7c 100644 --- a/middlewares/validators/oooRequests.ts +++ b/middlewares/validators/oooRequests.ts @@ -1,7 +1,7 @@ import joi from "joi"; import { NextFunction } from "express"; import { REQUEST_STATE, REQUEST_TYPE } from "../../constants/requests"; -import { OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest"; +import { AcknowledgeOooRequest, OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest"; export const createOooStatusRequestValidator = async ( req: OooRequestCreateRequest, @@ -38,3 +38,47 @@ export const createOooStatusRequestValidator = async ( await schema.validateAsync(req.body, { abortEarly: false }); }; + +const schema = joi + .object() + .strict() + .keys({ + comment: joi.string().optional() + .messages({ + "string.empty": "comment cannot be empty", + }), + status: joi + .string() + .valid(REQUEST_STATE.APPROVED, REQUEST_STATE.REJECTED) + .required() + .messages({ + "any.only": "status must be APPROVED or REJECTED", + }), + type: joi.string().equal(REQUEST_TYPE.OOO).required().messages({ + "any.required": "type is required", + "any.only": "type must be OOO" + }) + }); + +/** + * Middleware to validate the acknowledge Out-Of-Office (OOO) request payload. + * + * @param {AcknowledgeOooRequest} req - The request object containing the body to be validated. + * @param {OooRequestResponse} res - The response object used to send error responses if validation fails. + * @param {NextFunction} next - The next middleware function to call if validation succeeds. + * @returns {Promise} Resolves or sends errors. + */ +export const acknowledgeOooRequest = async ( + req: AcknowledgeOooRequest, + res: OooRequestResponse, + next: NextFunction +): Promise => { + try { + await schema.validateAsync(req.body, { abortEarly: false }); + next(); + } catch (error) { + const errorMessages = error.details.map((detail) => detail.message); + logger.error(`Error while validating request payload : ${errorMessages}`); + return res.boom.badRequest(errorMessages); + } +}; diff --git a/middlewares/validators/requests.ts b/middlewares/validators/requests.ts index 80ff0478b7..2df3c29277 100644 --- a/middlewares/validators/requests.ts +++ b/middlewares/validators/requests.ts @@ -1,8 +1,8 @@ import joi from "joi"; import { NextFunction } from "express"; import { REQUEST_STATE, REQUEST_TYPE } from "../../constants/requests"; -import { OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest"; -import { createOooStatusRequestValidator } from "./oooRequests"; +import { AcknowledgeOooRequest, OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest"; +import { acknowledgeOooRequest, createOooStatusRequestValidator } from "./oooRequests"; import { createExtensionRequestValidator } from "./extensionRequestsv2"; import {createTaskRequestValidator} from "./taskRequests"; import { ExtensionRequestRequest, ExtensionRequestResponse } from "../../types/extensionRequests"; @@ -92,6 +92,10 @@ export const getRequestsMiddleware = async (req: OooRequestCreateRequest, res: O .string() .valid(REQUEST_STATE.APPROVED, REQUEST_STATE.PENDING, REQUEST_STATE.REJECTED) .optional(), + status: joi + .string() + .valid(REQUEST_STATE.APPROVED, REQUEST_STATE.PENDING, REQUEST_STATE.REJECTED) + .optional(), page: joi.number().integer().min(0).when("next", { is: joi.exist(), then: joi.forbidden().messages({ @@ -125,13 +129,13 @@ export const getRequestsMiddleware = async (req: OooRequestCreateRequest, res: O /** * Validates update requests based on their type. * - * @param {UpdateOnboardingExtensionRequest} req - Request object. + * @param {UpdateOnboardingExtensionRequest | AcknowledgeOooRequest} req - Request object. * @param {CustomResponse} res - Response object. * @param {NextFunction} next - Next middleware if valid. * @returns {Promise} Resolves or sends errors. */ export const updateRequestValidator = async ( - req: UpdateOnboardingExtensionRequest, + req: UpdateOnboardingExtensionRequest | AcknowledgeOooRequest, res: CustomResponse, next: NextFunction ): Promise => { @@ -142,6 +146,9 @@ export const updateRequestValidator = async ( req, res as OnboardingExtensionResponse, next); break; + case REQUEST_TYPE.OOO: + await acknowledgeOooRequest(req, res as OooRequestResponse, next); + break; default: return res.boom.badRequest("Invalid type"); } diff --git a/models/requests.ts b/models/requests.ts index 064eebd8ca..f396618ef0 100644 --- a/models/requests.ts +++ b/models/requests.ts @@ -1,6 +1,6 @@ import firestore from "../utils/firestore"; const requestModel = firestore.collection("requests"); -import { REQUEST_ALREADY_APPROVED, REQUEST_ALREADY_REJECTED, REQUEST_STATE } from "../constants/requests"; +import { REQUEST_ALREADY_APPROVED, REQUEST_ALREADY_REJECTED, REQUEST_STATE,REQUEST_TYPE } from "../constants/requests"; import { ERROR_WHILE_FETCHING_REQUEST, ERROR_WHILE_CREATING_REQUEST, @@ -8,6 +8,8 @@ import { REQUEST_DOES_NOT_EXIST, } from "../constants/requests"; import { getUserId } from "../utils/users"; +import {NotFound} from "http-errors" +import {fetchUser} from "./users" const SIZE = 5; export const createRequest = async (body: any) => { @@ -69,6 +71,19 @@ export const updateRequest = async (id: string, body: any, lastModifiedBy: strin } }; +export const getRequestById = async (id: string) => { + try { + const requestDoc = await requestModel.doc(id).get(); + if (!requestDoc.exists) { + throw new NotFound("Request not found"); + } + return requestDoc.data(); + } catch (error) { + logger.error(ERROR_WHILE_FETCHING_REQUEST, error); + throw error; + } +} + export const getRequests = async (query: any) => { let { id, type, requestedBy, state, prev, next, page, size = SIZE } = query; const dev = query.dev === "true"; diff --git a/routes/requests.ts b/routes/requests.ts index 098e00a82a..b524fcf5f2 100644 --- a/routes/requests.ts +++ b/routes/requests.ts @@ -3,6 +3,7 @@ const router = express.Router(); const authorizeRoles = require("../middlewares/authorizeRoles"); const { SUPERUSER } = require("../constants/roles"); import authenticate from "../middlewares/authenticate"; +import { conditionalOooChecks } from "../middlewares/conditionalOooChecks"; import { createRequestsMiddleware, updateRequestsMiddleware, @@ -22,6 +23,6 @@ import { verifyDiscordBot } from "../middlewares/authorizeBot"; router.get("/", getRequestsMiddleware, getRequestsController); router.post("/", skipAuthenticateForOnboardingExtensionRequest(authenticate, verifyDiscordBot), createRequestsMiddleware, createRequestController); router.put("/:id",authenticate, authorizeRoles([SUPERUSER]), updateRequestsMiddleware, updateRequestController); -router.patch("/:id", authenticate, updateRequestValidator, updateRequestBeforeAcknowledgedController); +router.patch("/:id", authenticate, conditionalOooChecks, updateRequestValidator, updateRequestBeforeAcknowledgedController); module.exports = router; diff --git a/services/oooRequest.ts b/services/oooRequest.ts index aa32b4f429..d01bbfab79 100644 --- a/services/oooRequest.ts +++ b/services/oooRequest.ts @@ -4,13 +4,22 @@ import { OOO_STATUS_ALREADY_EXIST, REQUEST_LOG_TYPE, REQUEST_STATE, + REQUEST_TYPE, USER_STATUS_NOT_FOUND, + INVALID_REQUEST_TYPE, + REQUEST_ALREADY_APPROVED, + REQUEST_ALREADY_REJECTED, + REQUEST_APPROVED_SUCCESSFULLY, + REQUEST_REJECTED_SUCCESSFULLY, } from "../constants/requests"; -import { userState } from "../constants/userStatus"; -import { createRequest } from "../models/requests"; -import { OooStatusRequest, OooStatusRequestBody } from "../types/oooRequest"; +import { statusState, userState } from "../constants/userStatus"; +import { createRequest, getRequestById, updateRequest } from "../models/requests"; +import { AcknowledgeOooRequestBody, OooStatusRequest, OooStatusRequestBody } from "../types/oooRequest"; import { UserStatus } from "../types/userStatus"; import { addLog } from "./logService"; +import { BadRequest, Conflict } from "http-errors"; +import { addFutureStatus } from "../models/userStatus"; +import { createUserFutureStatus } from "../models/userFutureStatus"; /** * Validates the user status. @@ -51,22 +60,20 @@ export const validateUserStatus = async ( * Create an OOO request for a user. * * @param {OooStatusRequestBody} body - The request body containing OOO details. - * @param {string} username - The username of the person creating the request. - * @param {string} userId - The unique identifier of the user. + * @param {string} requestedBy - The userid of the person creating the request. * @returns {Promise} The created OOO request. * @throws {Error} Throws an error if an issue occurs during validation. */ export const createOooRequest = async ( body: OooStatusRequestBody, - username: string, - userId: string + requestedBy: string, ) => { try { const request: OooStatusRequest = await createRequest({ from: body.from, until: body.until, type: body.type, - requestedBy: userId, + requestedBy: requestedBy, reason: body.reason, comment: null, status: REQUEST_STATE.PENDING, @@ -78,7 +85,7 @@ export const createOooRequest = async ( meta: { requestId: request.id, action: LOG_ACTION.CREATE, - userId, + userId: requestedBy, createdAt: Date.now(), }, body: request, @@ -92,3 +99,99 @@ export const createOooRequest = async ( throw error; } } + +/** + * Validate the out-of-office acknowledgement request. + * + * @param {string} requestId - The unique identifier of the request. + * @param {string} requestType - The type of the request (OOO or OOO_ACKNOWLEDGEMENT). + * @param {string} requestStatus - The status of the request (PENDING, APPROVED, REJECTED). + * @throws {Error} Throws an error if an issue occurs during validation. + */ + +export const validateOooAcknowledgeRequest = async ( + requestType: string, + requestStatus: string, +) => { + try { + if (requestType !== REQUEST_TYPE.OOO) { + throw new BadRequest(INVALID_REQUEST_TYPE) + } + if (requestStatus === REQUEST_STATE.APPROVED) { + throw new Conflict(REQUEST_ALREADY_APPROVED) + } + if (requestStatus === REQUEST_STATE.REJECTED) { + throw new Conflict(REQUEST_ALREADY_REJECTED) + } + } catch (error) { + logger.error("Error while validating OOO acknowledge request", error); + throw error; + } +} + +/** + * Acknowledges the OOO request. + * + * @param {string} requestId - The id of the request to be acknowledged. + * @param {AcknowledgeOooRequestBody} body - The Acknowledgement request body. + * @param {string} superUserId - The id of the super user. + * @returns {Promise} The acknowledged OOO request. + * @throws {Error} Throws an error if an issue occurs during acknowledgement. + */ + +export const acknowledgeOooRequest = async ( + requestId: string, + body: AcknowledgeOooRequestBody, + superUserId: string, +) => { + try{ + const requestData = await getRequestById(requestId); + await validateOooAcknowledgeRequest(requestData.type, requestData.status); + const requestResult = await updateRequest(requestId, body, superUserId, REQUEST_TYPE.OOO); + if("error" in requestResult){ + throw new BadRequest(requestResult.error); + } + const [acknowledgeLogType, returnMessage]= + requestResult.status === REQUEST_STATE.APPROVED ? [REQUEST_LOG_TYPE.REQUEST_APPROVED, REQUEST_APPROVED_SUCCESSFULLY] : [REQUEST_LOG_TYPE.REQUEST_REJECTED, REQUEST_REJECTED_SUCCESSFULLY]; + const requestLog = { + type: acknowledgeLogType, + meta: { + requestId: requestId, + action: LOG_ACTION.UPDATE, + userId: superUserId, + }, + body: requestResult, + } + await addLog(requestLog.type, requestLog.meta, requestLog.body); + if (requestResult.status === REQUEST_STATE.APPROVED) { + await addFutureStatus({ + requestId, + state: REQUEST_TYPE.OOO, + from: requestData.from, + endsOn: requestData.until, + userId: requestData.requestedBy, + message: body.comment, + }); + await createUserFutureStatus({ + requestId, + status: userState.OOO, + state: statusState.UPCOMING, + from: requestData.from, + endsOn: requestData.until, + userId: requestData.requestedBy, + message: body.comment, + createdAt: Date.now() + }); + } + return { + message: returnMessage, + data: { + id: requestResult.id, + ...requestResult, + }, + }; + } catch (error) { + logger.error("Error while acknowledging OOO request", error); + throw error; + } +} diff --git a/types/oooRequest.d.ts b/types/oooRequest.d.ts index 6b1c282a8c..6bdfea29dc 100644 --- a/types/oooRequest.d.ts +++ b/types/oooRequest.d.ts @@ -42,3 +42,20 @@ export type OooRequestCreateRequest = Request & { }; export type OooRequestUpdateRequest = Request & { oooRequestUpdateBody , userData: userData , query: RequestQuery , params: RequestParams }; + +export type AcknowledgeOooRequestQuery = RequestQuery & { + dev?: string +}; + +export type AcknowledgeOooRequestBody = { + type: REQUEST_TYPE.OOO; + comment?: string; + status: REQUEST_STATE.APPROVED | REQUEST_STATE.REJECTED; +} + +export type AcknowledgeOooRequest = Request & { + body: AcknowledgeOooRequestBody; + userData: userData; + query: AcknowledgeOooRequestQuery; + params: RequestParams; +} \ No newline at end of file From 3666b9d45d3d45aadfcef797cd9f3e91a6b2a843 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Sat, 23 Aug 2025 19:35:38 +0530 Subject: [PATCH 02/23] fix-services-ooorequest-test --- test/unit/services/oooRequest.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/services/oooRequest.test.ts b/test/unit/services/oooRequest.test.ts index ae72a29eed..c71580b1a6 100644 --- a/test/unit/services/oooRequest.test.ts +++ b/test/unit/services/oooRequest.test.ts @@ -25,7 +25,7 @@ import { userState } from "../../../constants/userStatus"; import addUser from "../../utils/addUser"; import userDataFixture from "../../fixtures/user/user"; import * as logService from "../../../services/logService"; -import { acknowledgeOooRequest, createOooRequests3 } from "../../fixtures/oooRequest/oooRequest"; +import { createOooRequests3 } from "../../fixtures/oooRequest/oooRequest"; import { createRequest } from "../../../models/requests"; describe("Test OOO Request Service", function() { @@ -91,7 +91,7 @@ describe("Test OOO Request Service", function() { }); it("should create OOO request", async function() { - const response = await createOooRequest(validOooStatusRequests, testUserName, testUserId); + const response = await createOooRequest(validOooStatusRequests, testUserId); expect(response).to.deep.include({ ...createdOOORequest, id: response.id, @@ -103,7 +103,7 @@ describe("Test OOO Request Service", function() { sinon.stub(logService, "addLog").throws(new Error(errorMessage)); try { - await createOooRequest(validOooStatusRequests, testUserName, testUserId); + await createOooRequest(validOooStatusRequests, testUserId); } catch (error) { expect(error.message).to.equal(errorMessage); } From 303a1d7c2e4c22735ca749718b51361b99ce794b Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Tue, 26 Aug 2025 14:13:25 +0530 Subject: [PATCH 03/23] fix-module-imports --- controllers/oooRequests.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/controllers/oooRequests.ts b/controllers/oooRequests.ts index 1be13b5194..33e93a2617 100644 --- a/controllers/oooRequests.ts +++ b/controllers/oooRequests.ts @@ -22,8 +22,7 @@ import { addLog } from "../models/logs"; import { getRequestByKeyValues, getRequests, updateRequest } from "../models/requests"; import { createUserFutureStatus } from "../models/userFutureStatus"; import { getUserStatus, addFutureStatus } from "../models/userStatus"; -import { createOooRequest, validateUserStatus } from "../services/oooRequest"; -import * as oooRequestService from "../services/oooRequest"; +import { createOooRequest, validateUserStatus, acknowledgeOooRequest as acknowledgeOooRequestService } from "../services/oooRequest"; import { CustomResponse } from "../typeDefinitions/global"; import { AcknowledgeOooRequest, OooRequestCreateRequest, OooRequestResponse, OooStatusRequest } from "../types/oooRequest"; import { UpdateRequest } from "../types/requests"; @@ -183,7 +182,7 @@ export const acknowledgeOooRequest = async ( return res.boom.badRequest(REQUEST_ID_REQUIRED); } - const response = await oooRequestService.acknowledgeOooRequest(requestId, requestBody, superUserId); + const response = await acknowledgeOooRequestService(requestId, requestBody, superUserId); return res.status(200).json({ message: response.message, From e046d74912a2aeaf04b6b4abd67de6fcfb7637a5 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Fri, 29 Aug 2025 23:47:23 +0530 Subject: [PATCH 04/23] refactor: streamline OOO request handling and enhance validation - Removed unnecessary checks for development mode and user roles in createOooRequestController. - Improved documentation for acknowledgeOooRequest function to clarify parameters and return types. - Added validation for request parameters in acknowledgeOooRequest middleware. - Updated error handling in getRequestById to use a constant for not found messages. - Cleaned up imports in requests model to remove unused constants. --- controllers/oooRequests.ts | 32 +++++---------------------- middlewares/validators/oooRequests.ts | 15 +++++++++++++ middlewares/validators/requests.ts | 2 +- models/requests.ts | 5 ++--- services/oooRequest.ts | 1 - 5 files changed, 24 insertions(+), 31 deletions(-) diff --git a/controllers/oooRequests.ts b/controllers/oooRequests.ts index 33e93a2617..cca255f5b9 100644 --- a/controllers/oooRequests.ts +++ b/controllers/oooRequests.ts @@ -8,11 +8,9 @@ import { ERROR_WHILE_UPDATING_REQUEST, REQUEST_APPROVED_SUCCESSFULLY, REQUEST_REJECTED_SUCCESSFULLY, - UNAUTHORIZED_TO_CREATE_OOO_REQUEST, REQUEST_ALREADY_PENDING, USER_STATUS_NOT_FOUND, OOO_STATUS_ALREADY_EXIST, - UNAUTHORIZED_TO_UPDATE_REQUEST, ERROR_WHILE_ACKNOWLEDGING_REQUEST, REQUEST_ID_REQUIRED, } from "../constants/requests"; @@ -45,15 +43,7 @@ export const createOooRequestController = async ( ): Promise => { const requestBody = req.body; - const { id: userId, username } = req.userData; - const isUserPartOfDiscord = req.userData.roles.in_discord; - const dev = req.query.dev === "true"; - - if (!dev) return res.boom.notImplemented("Feature not implemented"); - - if (!isUserPartOfDiscord) { - return res.boom.forbidden(UNAUTHORIZED_TO_CREATE_OOO_REQUEST); - } + const { id: userId } = req.userData; try { const userStatus = await getUserStatus(userId); @@ -153,11 +143,12 @@ export const updateOooRequestController = async (req: UpdateRequest, res: Custom } }; /** - * Acknowledges an Out-of-Office (OOO) request + * Acknowledges an Out-of-Office (OOO) request by updating its status to approved or rejected * - * @param {AcknowledgeOooRequest} req - The request object. - * @param {OooRequestResponse} res - The response object. - * @returns {Promise} Resolves with success or failure. + * @param {AcknowledgeOooRequest} req - The request object containing acknowledgment details (status, comment) and request ID in params + * @param {OooRequestResponse} res - The response object for sending success/error responses + * @param {NextFunction} next - Express next function for error handling + * @returns {Promise} Resolves with success message or passes error to next middleware */ export const acknowledgeOooRequest = async ( req: AcknowledgeOooRequest, @@ -166,22 +157,11 @@ export const acknowledgeOooRequest = async ( ) : Promise => { try { - const dev = req.query.dev === "true"; - if(!dev) return res.boom.notImplemented("Feature not implemented"); - - const isSuperuser = req.userData?.roles?.super_user; - if (!isSuperuser) { - return res.boom.forbidden(UNAUTHORIZED_TO_UPDATE_REQUEST); - } const requestBody = req.body; const superUserId = req.userData.id; const requestId = req.params.id; - if (!requestId) { - return res.boom.badRequest(REQUEST_ID_REQUIRED); - } - const response = await acknowledgeOooRequestService(requestId, requestBody, superUserId); return res.status(200).json({ diff --git a/middlewares/validators/oooRequests.ts b/middlewares/validators/oooRequests.ts index 5bf4ccda7c..3973496b71 100644 --- a/middlewares/validators/oooRequests.ts +++ b/middlewares/validators/oooRequests.ts @@ -60,6 +60,16 @@ const schema = joi }) }); +const paramsSchema = joi + .object() + .strict() + .keys({ + id: joi.string().required().messages({ + "any.required": "Request ID is required", + "string.empty": "Request ID cannot be empty" + }) + }); + /** * Middleware to validate the acknowledge Out-Of-Office (OOO) request payload. * @@ -74,7 +84,12 @@ export const acknowledgeOooRequest = async ( next: NextFunction ): Promise => { try { + // Validate request body await schema.validateAsync(req.body, { abortEarly: false }); + + // Validate request params (ID) + await paramsSchema.validateAsync(req.params, { abortEarly: false }); + next(); } catch (error) { const errorMessages = error.details.map((detail) => detail.message); diff --git a/middlewares/validators/requests.ts b/middlewares/validators/requests.ts index 2df3c29277..5179a49ae2 100644 --- a/middlewares/validators/requests.ts +++ b/middlewares/validators/requests.ts @@ -88,7 +88,7 @@ export const getRequestsMiddleware = async (req: OooRequestCreateRequest, res: O .valid(REQUEST_TYPE.OOO, REQUEST_TYPE.EXTENSION, REQUEST_TYPE.TASK, REQUEST_TYPE.ALL, REQUEST_TYPE.ONBOARDING) .optional(), requestedBy: joi.string().insensitive().optional(), - state: joi + state: joi // TODO: Remove this validator once feature is tested and ready to be used .string() .valid(REQUEST_STATE.APPROVED, REQUEST_STATE.PENDING, REQUEST_STATE.REJECTED) .optional(), diff --git a/models/requests.ts b/models/requests.ts index cc2d724b7a..2876e87281 100644 --- a/models/requests.ts +++ b/models/requests.ts @@ -1,7 +1,7 @@ import firestore from "../utils/firestore"; import type { OooStatusRequest } from "../types/oooRequest"; const requestModel = firestore.collection("requests"); -import { REQUEST_ALREADY_APPROVED, REQUEST_ALREADY_REJECTED, REQUEST_STATE, REQUEST_TYPE } from "../constants/requests"; +import { REQUEST_ALREADY_APPROVED, REQUEST_ALREADY_REJECTED, REQUEST_STATE } from "../constants/requests"; import { ERROR_WHILE_FETCHING_REQUEST, ERROR_WHILE_CREATING_REQUEST, @@ -11,7 +11,6 @@ import { import { getUserId } from "../utils/users"; import { transformRequestResponse } from "../utils/requests"; import {NotFound} from "http-errors" -import {fetchUser} from "./users" const SIZE = 5; @@ -78,7 +77,7 @@ export const getRequestById = async (id: string) => { try { const requestDoc = await requestModel.doc(id).get(); if (!requestDoc.exists) { - throw new NotFound("Request not found"); + throw new NotFound(REQUEST_DOES_NOT_EXIST); } return requestDoc.data(); } catch (error) { diff --git a/services/oooRequest.ts b/services/oooRequest.ts index d01bbfab79..aa9298fb5b 100644 --- a/services/oooRequest.ts +++ b/services/oooRequest.ts @@ -103,7 +103,6 @@ export const createOooRequest = async ( /** * Validate the out-of-office acknowledgement request. * - * @param {string} requestId - The unique identifier of the request. * @param {string} requestType - The type of the request (OOO or OOO_ACKNOWLEDGEMENT). * @param {string} requestStatus - The status of the request (PENDING, APPROVED, REJECTED). * @throws {Error} Throws an error if an issue occurs during validation. From dcffdca26527d5fce2e9e30b2d7f9d7ee9d56eb4 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Sat, 30 Aug 2025 10:27:52 +0530 Subject: [PATCH 05/23] feat: enhance OOO request creation with user role validation - Added role validation to ensure only users part of Discord can create OOO requests. - Implemented a check for development mode to restrict access to the feature. - Updated the controller to handle unauthorized access appropriately. --- controllers/oooRequests.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/controllers/oooRequests.ts b/controllers/oooRequests.ts index cca255f5b9..67b43f7c0c 100644 --- a/controllers/oooRequests.ts +++ b/controllers/oooRequests.ts @@ -11,6 +11,7 @@ import { REQUEST_ALREADY_PENDING, USER_STATUS_NOT_FOUND, OOO_STATUS_ALREADY_EXIST, + UNAUTHORIZED_TO_CREATE_OOO_REQUEST, ERROR_WHILE_ACKNOWLEDGING_REQUEST, REQUEST_ID_REQUIRED, } from "../constants/requests"; @@ -43,7 +44,17 @@ export const createOooRequestController = async ( ): Promise => { const requestBody = req.body; - const { id: userId } = req.userData; + const { id: userId, roles } = req.userData; + const dev = req.query.dev === "true"; + const isUserPartOfDiscord = roles.in_discord; + + if (!dev) { + return res.boom.notImplemented("Feature not implemented"); + } + + if (!isUserPartOfDiscord) { + return res.boom.forbidden(UNAUTHORIZED_TO_CREATE_OOO_REQUEST); + } try { const userStatus = await getUserStatus(userId); From 1f8b831f69dad3d4f40352805af0693784765f37 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Sat, 30 Aug 2025 22:25:02 +0530 Subject: [PATCH 06/23] fix-getRequestByID-function --- middlewares/conditionalOooChecks.ts | 2 +- models/requests.ts | 22 ++++++++-------------- services/oooRequest.ts | 4 ++-- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/middlewares/conditionalOooChecks.ts b/middlewares/conditionalOooChecks.ts index a21803c6a2..aa9da40f91 100644 --- a/middlewares/conditionalOooChecks.ts +++ b/middlewares/conditionalOooChecks.ts @@ -18,7 +18,7 @@ export const conditionalOooChecks = (req: CustomRequest, res: CustomResponse, ne if (requestType === REQUEST_TYPE.OOO) { - + //TODO: Remove this middleware once the OOO feature is tested and ready to be used devFlagMiddleware(req, res, (err: any) => { if (err) return next(err); diff --git a/models/requests.ts b/models/requests.ts index 2876e87281..558eab7ee0 100644 --- a/models/requests.ts +++ b/models/requests.ts @@ -1,5 +1,4 @@ import firestore from "../utils/firestore"; -import type { OooStatusRequest } from "../types/oooRequest"; const requestModel = firestore.collection("requests"); import { REQUEST_ALREADY_APPROVED, REQUEST_ALREADY_REJECTED, REQUEST_STATE } from "../constants/requests"; import { @@ -73,19 +72,6 @@ export const updateRequest = async (id: string, body: any, lastModifiedBy: strin } }; -export const getRequestById = async (id: string) => { - try { - const requestDoc = await requestModel.doc(id).get(); - if (!requestDoc.exists) { - throw new NotFound(REQUEST_DOES_NOT_EXIST); - } - return requestDoc.data(); - } catch (error) { - logger.error(ERROR_WHILE_FETCHING_REQUEST, error); - throw error; - } -} - export const getRequests = async (query: any) => { let { id, type, requestedBy, state, prev, next, page, size = SIZE } = query; const dev = query.dev === "true"; @@ -98,8 +84,16 @@ export const getRequests = async (query: any) => { if (id) { const requestDoc = await requestModel.doc(id).get(); if (!requestDoc.exists) { + + if (type === 'OOO') { + throw new NotFound(REQUEST_DOES_NOT_EXIST); + } return null; } + + if (type === 'OOO') { + return requestDoc.data(); + } return { id: requestDoc.id, ...requestDoc.data(), diff --git a/services/oooRequest.ts b/services/oooRequest.ts index aa9298fb5b..2192332119 100644 --- a/services/oooRequest.ts +++ b/services/oooRequest.ts @@ -13,7 +13,7 @@ import { REQUEST_REJECTED_SUCCESSFULLY, } from "../constants/requests"; import { statusState, userState } from "../constants/userStatus"; -import { createRequest, getRequestById, updateRequest } from "../models/requests"; +import { createRequest, getRequests, updateRequest } from "../models/requests"; import { AcknowledgeOooRequestBody, OooStatusRequest, OooStatusRequestBody } from "../types/oooRequest"; import { UserStatus } from "../types/userStatus"; import { addLog } from "./logService"; @@ -144,7 +144,7 @@ export const acknowledgeOooRequest = async ( superUserId: string, ) => { try{ - const requestData = await getRequestById(requestId); + const requestData = await getRequests({ id: requestId }); await validateOooAcknowledgeRequest(requestData.type, requestData.status); const requestResult = await updateRequest(requestId, body, superUserId, REQUEST_TYPE.OOO); if("error" in requestResult){ From 03d31983d0d0436329a9d4d9aa37b655f611c94e Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Sat, 30 Aug 2025 22:55:16 +0530 Subject: [PATCH 07/23] fix-validator-name --- middlewares/validators/oooRequests.ts | 6 +----- middlewares/validators/requests.ts | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/middlewares/validators/oooRequests.ts b/middlewares/validators/oooRequests.ts index 3973496b71..efd6b4a0d6 100644 --- a/middlewares/validators/oooRequests.ts +++ b/middlewares/validators/oooRequests.ts @@ -78,18 +78,14 @@ const paramsSchema = joi * @param {NextFunction} next - The next middleware function to call if validation succeeds. * @returns {Promise} Resolves or sends errors. */ -export const acknowledgeOooRequest = async ( +export const acknowledgeOooRequestValidator = async ( req: AcknowledgeOooRequest, res: OooRequestResponse, next: NextFunction ): Promise => { try { - // Validate request body await schema.validateAsync(req.body, { abortEarly: false }); - - // Validate request params (ID) await paramsSchema.validateAsync(req.params, { abortEarly: false }); - next(); } catch (error) { const errorMessages = error.details.map((detail) => detail.message); diff --git a/middlewares/validators/requests.ts b/middlewares/validators/requests.ts index 5179a49ae2..9768e46a07 100644 --- a/middlewares/validators/requests.ts +++ b/middlewares/validators/requests.ts @@ -2,7 +2,7 @@ import joi from "joi"; import { NextFunction } from "express"; import { REQUEST_STATE, REQUEST_TYPE } from "../../constants/requests"; import { AcknowledgeOooRequest, OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest"; -import { acknowledgeOooRequest, createOooStatusRequestValidator } from "./oooRequests"; +import { acknowledgeOooRequestValidator, createOooStatusRequestValidator } from "./oooRequests"; import { createExtensionRequestValidator } from "./extensionRequestsv2"; import {createTaskRequestValidator} from "./taskRequests"; import { ExtensionRequestRequest, ExtensionRequestResponse } from "../../types/extensionRequests"; @@ -147,7 +147,7 @@ export const updateRequestValidator = async ( res as OnboardingExtensionResponse, next); break; case REQUEST_TYPE.OOO: - await acknowledgeOooRequest(req, res as OooRequestResponse, next); + await acknowledgeOooRequestValidator(req, res as OooRequestResponse, next); break; default: return res.boom.badRequest("Invalid type"); From 16b90ba55ca1624922f8fc1502f50ff27a65585e Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Sun, 31 Aug 2025 01:38:23 +0530 Subject: [PATCH 08/23] refactor: remove redundant request constants from REQUEST_LOG_TYPE --- constants/requests.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/constants/requests.ts b/constants/requests.ts index b7fb33999a..b1acc86193 100644 --- a/constants/requests.ts +++ b/constants/requests.ts @@ -25,9 +25,7 @@ export const REQUEST_LOG_TYPE = { REQUEST_BLOCKED: "REQUEST_BLOCKED", REQUEST_CANCELLED: "REQUEST_CANCELLED", REQUEST_UPDATED: "REQUEST_UPDATED", - PENDING_REQUEST_FOUND: "PENDING_REQUEST_FOUND", - REQUEST_ALREADY_APPROVED: "REQUEST_ALREADY_APPROVED", - REQUEST_ALREADY_REJECTED: "REQUEST_ALREADY_REJECTED", + PENDING_REQUEST_FOUND: "PENDING_REQUEST_FOUND" }; export const REQUEST_CREATED_SUCCESSFULLY = "Request created successfully"; From d94bbda097e98ae528101da12d0612cc63e716ba Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Mon, 1 Sep 2025 12:05:57 +0530 Subject: [PATCH 09/23] fix-ooo-models-condtions --- models/requests.ts | 6 ------ services/oooRequest.ts | 26 ++++++++++++++++++-------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/models/requests.ts b/models/requests.ts index 558eab7ee0..3f17bde207 100644 --- a/models/requests.ts +++ b/models/requests.ts @@ -85,15 +85,9 @@ export const getRequests = async (query: any) => { const requestDoc = await requestModel.doc(id).get(); if (!requestDoc.exists) { - if (type === 'OOO') { - throw new NotFound(REQUEST_DOES_NOT_EXIST); - } return null; } - if (type === 'OOO') { - return requestDoc.data(); - } return { id: requestDoc.id, ...requestDoc.data(), diff --git a/services/oooRequest.ts b/services/oooRequest.ts index 2192332119..2eb0f6cc32 100644 --- a/services/oooRequest.ts +++ b/services/oooRequest.ts @@ -60,7 +60,7 @@ export const validateUserStatus = async ( * Create an OOO request for a user. * * @param {OooStatusRequestBody} body - The request body containing OOO details. - * @param {string} requestedBy - The userid of the person creating the request. + * @param {string} requestedBy - The userId of the person creating the request. * @returns {Promise} The created OOO request. * @throws {Error} Throws an error if an issue occurs during validation. */ @@ -145,7 +145,17 @@ export const acknowledgeOooRequest = async ( ) => { try{ const requestData = await getRequests({ id: requestId }); - await validateOooAcknowledgeRequest(requestData.type, requestData.status); + if (!requestData) { + throw new BadRequest("Request not found"); + } + if (!('type' in requestData) || !('status' in requestData) || !('from' in requestData) || !('until' in requestData) || !('requestedBy' in requestData)) { + throw new BadRequest("Invalid request data structure"); + } + const { type, status, from, until, requestedBy } = requestData; + await validateOooAcknowledgeRequest(type as string, status as string); + const fromDate = from as number; + const untilDate = until as number; + const requestedByUser = requestedBy as string; const requestResult = await updateRequest(requestId, body, superUserId, REQUEST_TYPE.OOO); if("error" in requestResult){ throw new BadRequest(requestResult.error); @@ -166,18 +176,18 @@ export const acknowledgeOooRequest = async ( await addFutureStatus({ requestId, state: REQUEST_TYPE.OOO, - from: requestData.from, - endsOn: requestData.until, - userId: requestData.requestedBy, + from: fromDate, + endsOn: untilDate, + userId: requestedByUser, message: body.comment, }); await createUserFutureStatus({ requestId, status: userState.OOO, state: statusState.UPCOMING, - from: requestData.from, - endsOn: requestData.until, - userId: requestData.requestedBy, + from: fromDate, + endsOn: untilDate, + userId: requestedByUser, message: body.comment, createdAt: Date.now() }); From 602a0bbfb423d7aab6e2dbbf8e9e385a3c55f490 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Tue, 2 Sep 2025 20:24:14 +0530 Subject: [PATCH 10/23] refactor: enhance OOO request handling and validation - Updated createOooRequestController to use username instead of roles for user identification. - Introduced conditionalOooChecks middleware to apply role and dev checks specifically for OOO requests. - Improved validation error handling in acknowledgeOooRequest and added constants for error messages. - Refactored request validation schemas for clarity and consistency. - Cleaned up imports and ensured proper middleware usage in routes. --- controllers/oooRequests.ts | 8 ++---- ...OooChecks.ts => oooRoleCheckMiddleware.ts} | 2 -- middlewares/validators/oooRequests.ts | 10 +++---- middlewares/validators/requests.ts | 2 +- routes/requests.ts | 2 +- services/oooRequest.ts | 28 +++++++++++-------- 6 files changed, 27 insertions(+), 25 deletions(-) rename middlewares/{conditionalOooChecks.ts => oooRoleCheckMiddleware.ts} (99%) diff --git a/controllers/oooRequests.ts b/controllers/oooRequests.ts index 67b43f7c0c..5af8b81de0 100644 --- a/controllers/oooRequests.ts +++ b/controllers/oooRequests.ts @@ -44,13 +44,11 @@ export const createOooRequestController = async ( ): Promise => { const requestBody = req.body; - const { id: userId, roles } = req.userData; + const { id: userId, username } = req.userData; + const isUserPartOfDiscord = req.userData.roles.in_discord; const dev = req.query.dev === "true"; - const isUserPartOfDiscord = roles.in_discord; - if (!dev) { - return res.boom.notImplemented("Feature not implemented"); - } + if (!dev) return res.boom.notImplemented("Feature not implemented"); if (!isUserPartOfDiscord) { return res.boom.forbidden(UNAUTHORIZED_TO_CREATE_OOO_REQUEST); diff --git a/middlewares/conditionalOooChecks.ts b/middlewares/oooRoleCheckMiddleware.ts similarity index 99% rename from middlewares/conditionalOooChecks.ts rename to middlewares/oooRoleCheckMiddleware.ts index aa9da40f91..fc7d9c0d64 100644 --- a/middlewares/conditionalOooChecks.ts +++ b/middlewares/oooRoleCheckMiddleware.ts @@ -21,8 +21,6 @@ export const conditionalOooChecks = (req: CustomRequest, res: CustomResponse, ne //TODO: Remove this middleware once the OOO feature is tested and ready to be used devFlagMiddleware(req, res, (err: any) => { if (err) return next(err); - - authorizeRoles([SUPERUSER])(req, res, next); }); } else { diff --git a/middlewares/validators/oooRequests.ts b/middlewares/validators/oooRequests.ts index efd6b4a0d6..38e9d3086e 100644 --- a/middlewares/validators/oooRequests.ts +++ b/middlewares/validators/oooRequests.ts @@ -1,6 +1,6 @@ import joi from "joi"; import { NextFunction } from "express"; -import { REQUEST_STATE, REQUEST_TYPE } from "../../constants/requests"; +import { REQUEST_STATE, REQUEST_TYPE, ERROR_WHILE_ACKNOWLEDGING_REQUEST } from "../../constants/requests"; import { AcknowledgeOooRequest, OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest"; export const createOooStatusRequestValidator = async ( @@ -39,7 +39,7 @@ export const createOooStatusRequestValidator = async ( await schema.validateAsync(req.body, { abortEarly: false }); }; -const schema = joi +const acknowledgeOooRequestSchema = joi .object() .strict() .keys({ @@ -64,7 +64,7 @@ const paramsSchema = joi .object() .strict() .keys({ - id: joi.string().required().messages({ + id: joi.string().trim().required().messages({ "any.required": "Request ID is required", "string.empty": "Request ID cannot be empty" }) @@ -84,12 +84,12 @@ export const acknowledgeOooRequestValidator = async ( next: NextFunction ): Promise => { try { - await schema.validateAsync(req.body, { abortEarly: false }); + await acknowledgeOooRequestSchema.validateAsync(req.body, { abortEarly: false }); await paramsSchema.validateAsync(req.params, { abortEarly: false }); next(); } catch (error) { const errorMessages = error.details.map((detail) => detail.message); - logger.error(`Error while validating request payload : ${errorMessages}`); + logger.error(`${ERROR_WHILE_ACKNOWLEDGING_REQUEST}: ${errorMessages}`); return res.boom.badRequest(errorMessages); } }; diff --git a/middlewares/validators/requests.ts b/middlewares/validators/requests.ts index 9768e46a07..3987cfaf25 100644 --- a/middlewares/validators/requests.ts +++ b/middlewares/validators/requests.ts @@ -88,7 +88,7 @@ export const getRequestsMiddleware = async (req: OooRequestCreateRequest, res: O .valid(REQUEST_TYPE.OOO, REQUEST_TYPE.EXTENSION, REQUEST_TYPE.TASK, REQUEST_TYPE.ALL, REQUEST_TYPE.ONBOARDING) .optional(), requestedBy: joi.string().insensitive().optional(), - state: joi // TODO: Remove this validator once feature is tested and ready to be used + state: joi .string() .valid(REQUEST_STATE.APPROVED, REQUEST_STATE.PENDING, REQUEST_STATE.REJECTED) .optional(), diff --git a/routes/requests.ts b/routes/requests.ts index b524fcf5f2..c1e4a04e1a 100644 --- a/routes/requests.ts +++ b/routes/requests.ts @@ -3,7 +3,7 @@ const router = express.Router(); const authorizeRoles = require("../middlewares/authorizeRoles"); const { SUPERUSER } = require("../constants/roles"); import authenticate from "../middlewares/authenticate"; -import { conditionalOooChecks } from "../middlewares/conditionalOooChecks"; +import { conditionalOooChecks } from "../middlewares/oooRoleCheckMiddleware"; import { createRequestsMiddleware, updateRequestsMiddleware, diff --git a/services/oooRequest.ts b/services/oooRequest.ts index 2eb0f6cc32..bb6434a9c5 100644 --- a/services/oooRequest.ts +++ b/services/oooRequest.ts @@ -11,15 +11,18 @@ import { REQUEST_ALREADY_REJECTED, REQUEST_APPROVED_SUCCESSFULLY, REQUEST_REJECTED_SUCCESSFULLY, + ERROR_WHILE_ACKNOWLEDGING_REQUEST, + ERROR_WHILE_CREATING_REQUEST, } from "../constants/requests"; import { statusState, userState } from "../constants/userStatus"; import { createRequest, getRequests, updateRequest } from "../models/requests"; -import { AcknowledgeOooRequestBody, OooStatusRequest, OooStatusRequestBody } from "../types/oooRequest"; +import { AcknowledgeOooRequestBody, OooStatusRequest, oldOooStatusRequest, OooStatusRequestBody } from "../types/oooRequest"; import { UserStatus } from "../types/userStatus"; import { addLog } from "./logService"; import { BadRequest, Conflict } from "http-errors"; import { addFutureStatus } from "../models/userStatus"; import { createUserFutureStatus } from "../models/userFutureStatus"; +import { newOOOSchema} from "../utils/requests"; /** * Validates the user status. @@ -95,7 +98,7 @@ export const createOooRequest = async ( return request; } catch (error) { - logger.error("Error while creating OOO request", error); + logger.error(ERROR_WHILE_CREATING_REQUEST, error); throw error; } } @@ -148,16 +151,19 @@ export const acknowledgeOooRequest = async ( if (!requestData) { throw new BadRequest("Request not found"); } - if (!('type' in requestData) || !('status' in requestData) || !('from' in requestData) || !('until' in requestData) || !('requestedBy' in requestData)) { - throw new BadRequest("Invalid request data structure"); - } - const { type, status, from, until, requestedBy } = requestData; + const normalized: OooStatusRequest = ( + (requestData as OooStatusRequest).type === REQUEST_TYPE.OOO && + 'state' in (requestData as OooStatusRequest) && + !('status' in (requestData as OooStatusRequest)) + ) ? newOOOSchema(requestData as oldOooStatusRequest) as OooStatusRequest : requestData as OooStatusRequest; + + const { type, status, from, until, requestedBy } = normalized; await validateOooAcknowledgeRequest(type as string, status as string); - const fromDate = from as number; - const untilDate = until as number; - const requestedByUser = requestedBy as string; + const fromDate = from; + const untilDate = until; + const requestedByUser = requestedBy; const requestResult = await updateRequest(requestId, body, superUserId, REQUEST_TYPE.OOO); - if("error" in requestResult){ + if(requestResult.error){ throw new BadRequest(requestResult.error); } const [acknowledgeLogType, returnMessage]= @@ -200,7 +206,7 @@ export const acknowledgeOooRequest = async ( }, }; } catch (error) { - logger.error("Error while acknowledging OOO request", error); + logger.error(ERROR_WHILE_ACKNOWLEDGING_REQUEST, error); throw error; } } From d608d9ccb4e3c766d3247c56abaed57cbfa05c0c Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Wed, 3 Sep 2025 00:16:00 +0530 Subject: [PATCH 11/23] refactor: update error handling in OOO request service - Replaced BadRequest error with NotFound error in acknowledgeOooRequest for improved clarity. - Removed unused import of NotFound from requests model to streamline code. --- models/requests.ts | 1 - services/oooRequest.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/models/requests.ts b/models/requests.ts index 3f17bde207..2490f5af0d 100644 --- a/models/requests.ts +++ b/models/requests.ts @@ -9,7 +9,6 @@ import { } from "../constants/requests"; import { getUserId } from "../utils/users"; import { transformRequestResponse } from "../utils/requests"; -import {NotFound} from "http-errors" const SIZE = 5; diff --git a/services/oooRequest.ts b/services/oooRequest.ts index bb6434a9c5..8f88572a1b 100644 --- a/services/oooRequest.ts +++ b/services/oooRequest.ts @@ -19,7 +19,7 @@ import { createRequest, getRequests, updateRequest } from "../models/requests"; import { AcknowledgeOooRequestBody, OooStatusRequest, oldOooStatusRequest, OooStatusRequestBody } from "../types/oooRequest"; import { UserStatus } from "../types/userStatus"; import { addLog } from "./logService"; -import { BadRequest, Conflict } from "http-errors"; +import { BadRequest, Conflict, NotFound } from "http-errors"; import { addFutureStatus } from "../models/userStatus"; import { createUserFutureStatus } from "../models/userFutureStatus"; import { newOOOSchema} from "../utils/requests"; @@ -149,7 +149,7 @@ export const acknowledgeOooRequest = async ( try{ const requestData = await getRequests({ id: requestId }); if (!requestData) { - throw new BadRequest("Request not found"); + throw new NotFound("Request not found"); } const normalized: OooStatusRequest = ( (requestData as OooStatusRequest).type === REQUEST_TYPE.OOO && From 9ad304881c2d84ceccc4ecdd60028e7c83611823 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Wed, 3 Sep 2025 10:48:15 +0530 Subject: [PATCH 12/23] fix/function-name --- middlewares/oooRoleCheckMiddleware.ts | 2 +- models/requests.ts | 4 +--- routes/requests.ts | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/middlewares/oooRoleCheckMiddleware.ts b/middlewares/oooRoleCheckMiddleware.ts index fc7d9c0d64..7e30a540b4 100644 --- a/middlewares/oooRoleCheckMiddleware.ts +++ b/middlewares/oooRoleCheckMiddleware.ts @@ -13,7 +13,7 @@ const { SUPERUSER } = require("../constants/roles"); * @param {CustomResponse} res - The response object * @param {NextFunction} next - The next middleware function */ -export const conditionalOooChecks = (req: CustomRequest, res: CustomResponse, next: NextFunction) => { +export const oooRoleCheckMiddleware = (req: CustomRequest, res: CustomResponse, next: NextFunction) => { const requestType = req.body?.type; diff --git a/models/requests.ts b/models/requests.ts index 2490f5af0d..f0e541e9d8 100644 --- a/models/requests.ts +++ b/models/requests.ts @@ -82,11 +82,9 @@ export const getRequests = async (query: any) => { if (id) { const requestDoc = await requestModel.doc(id).get(); - if (!requestDoc.exists) { - + if (!requestDoc.exists) { return null; } - return { id: requestDoc.id, ...requestDoc.data(), diff --git a/routes/requests.ts b/routes/requests.ts index c1e4a04e1a..b688ef5123 100644 --- a/routes/requests.ts +++ b/routes/requests.ts @@ -3,7 +3,7 @@ const router = express.Router(); const authorizeRoles = require("../middlewares/authorizeRoles"); const { SUPERUSER } = require("../constants/roles"); import authenticate from "../middlewares/authenticate"; -import { conditionalOooChecks } from "../middlewares/oooRoleCheckMiddleware"; +import { oooRoleCheckMiddleware } from "../middlewares/oooRoleCheckMiddleware"; import { createRequestsMiddleware, updateRequestsMiddleware, @@ -23,6 +23,6 @@ import { verifyDiscordBot } from "../middlewares/authorizeBot"; router.get("/", getRequestsMiddleware, getRequestsController); router.post("/", skipAuthenticateForOnboardingExtensionRequest(authenticate, verifyDiscordBot), createRequestsMiddleware, createRequestController); router.put("/:id",authenticate, authorizeRoles([SUPERUSER]), updateRequestsMiddleware, updateRequestController); -router.patch("/:id", authenticate, conditionalOooChecks, updateRequestValidator, updateRequestBeforeAcknowledgedController); +router.patch("/:id", authenticate, oooRoleCheckMiddleware, updateRequestValidator, updateRequestBeforeAcknowledgedController); module.exports = router; From 577cef827501e1ced5d3cb212ee634925318d853 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Wed, 3 Sep 2025 10:53:18 +0530 Subject: [PATCH 13/23] fix/spaces --- constants/requests.ts | 2 +- models/requests.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/constants/requests.ts b/constants/requests.ts index b1acc86193..6a7358669e 100644 --- a/constants/requests.ts +++ b/constants/requests.ts @@ -25,7 +25,7 @@ export const REQUEST_LOG_TYPE = { REQUEST_BLOCKED: "REQUEST_BLOCKED", REQUEST_CANCELLED: "REQUEST_CANCELLED", REQUEST_UPDATED: "REQUEST_UPDATED", - PENDING_REQUEST_FOUND: "PENDING_REQUEST_FOUND" + PENDING_REQUEST_FOUND: "PENDING_REQUEST_FOUND", }; export const REQUEST_CREATED_SUCCESSFULLY = "Request created successfully"; diff --git a/models/requests.ts b/models/requests.ts index f0e541e9d8..aefa0012b4 100644 --- a/models/requests.ts +++ b/models/requests.ts @@ -82,7 +82,7 @@ export const getRequests = async (query: any) => { if (id) { const requestDoc = await requestModel.doc(id).get(); - if (!requestDoc.exists) { + if (!requestDoc.exists) { return null; } return { From f4ff1035d5e6f0f5227b72ebb4c605f8e307f9b7 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Wed, 3 Sep 2025 17:29:11 +0530 Subject: [PATCH 14/23] refactor: simplify variable usage in acknowledgeOooRequest - Removed redundant variable assignments for 'from', 'until', and 'requestedBy' in acknowledgeOooRequest function. - Directly used the parameters in subsequent function calls to enhance code clarity and reduce unnecessary lines. --- services/oooRequest.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/services/oooRequest.ts b/services/oooRequest.ts index 8f88572a1b..468ce3203d 100644 --- a/services/oooRequest.ts +++ b/services/oooRequest.ts @@ -159,9 +159,6 @@ export const acknowledgeOooRequest = async ( const { type, status, from, until, requestedBy } = normalized; await validateOooAcknowledgeRequest(type as string, status as string); - const fromDate = from; - const untilDate = until; - const requestedByUser = requestedBy; const requestResult = await updateRequest(requestId, body, superUserId, REQUEST_TYPE.OOO); if(requestResult.error){ throw new BadRequest(requestResult.error); @@ -182,18 +179,18 @@ export const acknowledgeOooRequest = async ( await addFutureStatus({ requestId, state: REQUEST_TYPE.OOO, - from: fromDate, - endsOn: untilDate, - userId: requestedByUser, + from: from, + endsOn: until, + userId: requestedBy, message: body.comment, }); await createUserFutureStatus({ requestId, status: userState.OOO, state: statusState.UPCOMING, - from: fromDate, - endsOn: untilDate, - userId: requestedByUser, + from: from, + endsOn: until, + userId: requestedBy, message: body.comment, createdAt: Date.now() }); From f1bfe6a972cd6a5e97070b958d29902221cad5a6 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Wed, 3 Sep 2025 19:16:18 +0530 Subject: [PATCH 15/23] refactor: rename acknowledgeOooRequest for consistency - Updated the function name from acknowledgeOooRequest to acknowledgeOooRequestController for clarity and to align with naming conventions in the codebase. - Adjusted import statements accordingly in the requests controller to reflect the new function name. --- controllers/oooRequests.ts | 2 +- controllers/requests.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/controllers/oooRequests.ts b/controllers/oooRequests.ts index 5af8b81de0..3590e18c61 100644 --- a/controllers/oooRequests.ts +++ b/controllers/oooRequests.ts @@ -159,7 +159,7 @@ export const updateOooRequestController = async (req: UpdateRequest, res: Custom * @param {NextFunction} next - Express next function for error handling * @returns {Promise} Resolves with success message or passes error to next middleware */ -export const acknowledgeOooRequest = async ( +export const acknowledgeOooRequestController = async ( req: AcknowledgeOooRequest, res: OooRequestResponse, next: NextFunction diff --git a/controllers/requests.ts b/controllers/requests.ts index c8830f7fec..e3a4fb6812 100644 --- a/controllers/requests.ts +++ b/controllers/requests.ts @@ -5,7 +5,7 @@ import { } from "../constants/requests"; import { getRequests } from "../models/requests"; import { getPaginatedLink } from "../utils/helper"; -import { acknowledgeOooRequest, createOooRequestController, updateOooRequestController } from "./oooRequests"; +import { acknowledgeOooRequestController, createOooRequestController, updateOooRequestController } from "./oooRequests"; import { AcknowledgeOooRequest, OooRequestCreateRequest, OooRequestResponse } from "../types/oooRequest"; import { CustomResponse } from "../typeDefinitions/global"; import { ExtensionRequestRequest, ExtensionRequestResponse } from "../types/extensionRequests"; @@ -125,7 +125,7 @@ export const updateRequestBeforeAcknowledgedController = async (req: Request, re const type = req.body.type; switch(type){ case REQUEST_TYPE.OOO: - await acknowledgeOooRequest(req as AcknowledgeOooRequest, res as OooRequestResponse, next); + await acknowledgeOooRequestController(req as AcknowledgeOooRequest, res as OooRequestResponse, next); break; case REQUEST_TYPE.ONBOARDING: await updateOnboardingExtensionRequestController(req as UpdateOnboardingExtensionRequest, res as OnboardingExtensionResponse); From b2fb635491b1217b4103da08f09b0ba6099a58fe Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Wed, 3 Sep 2025 22:22:03 +0530 Subject: [PATCH 16/23] refactor: update acknowledgeOooRequest to improve clarity and maintainability - Added a TODO comment to indicate the need for future removal of legacy request format handling. - Simplified the return structure by removing unnecessary data wrapping around requestResult. --- services/oooRequest.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/services/oooRequest.ts b/services/oooRequest.ts index 468ce3203d..4e9279e89f 100644 --- a/services/oooRequest.ts +++ b/services/oooRequest.ts @@ -151,6 +151,7 @@ export const acknowledgeOooRequest = async ( if (!requestData) { throw new NotFound("Request not found"); } + //TODO: Remove this after old request format is removed const normalized: OooStatusRequest = ( (requestData as OooStatusRequest).type === REQUEST_TYPE.OOO && 'state' in (requestData as OooStatusRequest) && @@ -197,10 +198,6 @@ export const acknowledgeOooRequest = async ( } return { message: returnMessage, - data: { - id: requestResult.id, - ...requestResult, - }, }; } catch (error) { logger.error(ERROR_WHILE_ACKNOWLEDGING_REQUEST, error); From f47848a6fdc30588fd4070301380115e95ea58f9 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Wed, 3 Sep 2025 23:23:35 +0530 Subject: [PATCH 17/23] refactor: enhance type handling in acknowledgeOooRequest - Updated requestData type assertion to include oldOooStatusRequest for better type safety. - Removed legacy request format normalization logic to streamline the function and improve clarity. --- services/oooRequest.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/services/oooRequest.ts b/services/oooRequest.ts index 4e9279e89f..6a66574dbb 100644 --- a/services/oooRequest.ts +++ b/services/oooRequest.ts @@ -147,18 +147,12 @@ export const acknowledgeOooRequest = async ( superUserId: string, ) => { try{ - const requestData = await getRequests({ id: requestId }); + const requestData = await getRequests({ id: requestId }) as OooStatusRequest | oldOooStatusRequest; if (!requestData) { throw new NotFound("Request not found"); } - //TODO: Remove this after old request format is removed - const normalized: OooStatusRequest = ( - (requestData as OooStatusRequest).type === REQUEST_TYPE.OOO && - 'state' in (requestData as OooStatusRequest) && - !('status' in (requestData as OooStatusRequest)) - ) ? newOOOSchema(requestData as oldOooStatusRequest) as OooStatusRequest : requestData as OooStatusRequest; + const { type, status, from, until, requestedBy } = requestData as OooStatusRequest; - const { type, status, from, until, requestedBy } = normalized; await validateOooAcknowledgeRequest(type as string, status as string); const requestResult = await updateRequest(requestId, body, superUserId, REQUEST_TYPE.OOO); if(requestResult.error){ From baad820e9fc372700a4f78e65351ffe851eac2a0 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Thu, 4 Sep 2025 03:02:05 +0530 Subject: [PATCH 18/23] fix-nested-loop-refactor --- utils/requests.ts | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/utils/requests.ts b/utils/requests.ts index f51e8e2e25..f94e79f01a 100644 --- a/utils/requests.ts +++ b/utils/requests.ts @@ -69,27 +69,20 @@ export const newOOOSchema = (request: oldOooStatusRequest) => ({ * @param {boolean} dev - Development flag to determine transformation logic */ export const transformRequestResponse = (allRequests: (OooStatusRequest | oldOooStatusRequest)[], dev: boolean = false): (OooStatusRequest | oldOooStatusRequest)[] => { - const transformedRequests: (OooStatusRequest | oldOooStatusRequest)[] = []; - - for (const request of allRequests) { - if (request.type === REQUEST_TYPE.OOO) { - if (dev) { - if ('status' in request) { - transformedRequests.push(oldOOOSchema(request as OooStatusRequest)); - } else { - transformedRequests.push(request); - } - } else { - if ('state' in request) { - transformedRequests.push(newOOOSchema(request as oldOooStatusRequest)); - } else { - transformedRequests.push(request); - } - } - } else { - transformedRequests.push(request); + return allRequests.map(request => { + + if (request.type !== REQUEST_TYPE.OOO) { + return request; } - } - - return transformedRequests; + + if (dev) { + return 'status' in request + ? oldOOOSchema(request as OooStatusRequest) + : request; + } + + return 'state' in request + ? newOOOSchema(request as oldOooStatusRequest) + : request; + }); }; \ No newline at end of file From ea2222c4c7a7b19ea3e8e17484b17a0e91f840f2 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Thu, 4 Sep 2025 15:10:21 +0530 Subject: [PATCH 19/23] fix/change-conflict-to-badrequest --- services/oooRequest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/oooRequest.ts b/services/oooRequest.ts index 6a66574dbb..cc953a2cd7 100644 --- a/services/oooRequest.ts +++ b/services/oooRequest.ts @@ -120,10 +120,10 @@ export const validateOooAcknowledgeRequest = async ( throw new BadRequest(INVALID_REQUEST_TYPE) } if (requestStatus === REQUEST_STATE.APPROVED) { - throw new Conflict(REQUEST_ALREADY_APPROVED) + throw new BadRequest(REQUEST_ALREADY_APPROVED) } if (requestStatus === REQUEST_STATE.REJECTED) { - throw new Conflict(REQUEST_ALREADY_REJECTED) + throw new BadRequest(REQUEST_ALREADY_REJECTED) } } catch (error) { logger.error("Error while validating OOO acknowledge request", error); From 6e63df6fde49622deb42c48f9dd2c03e6d8f4da0 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Thu, 4 Sep 2025 16:20:03 +0530 Subject: [PATCH 20/23] refactor: improve oooRoleCheckMiddleware and oooRequests validation - Enhanced the oooRoleCheckMiddleware for better error handling and code clarity. - Updated the acknowledgeOooRequestValidator to include specific Joi validation error handling. - Simplified the validateOooAcknowledgeRequest function by removing unnecessary try-catch nesting and improving logging for invalid request types. --- middlewares/oooRoleCheckMiddleware.ts | 34 +++--- middlewares/validators/oooRequests.ts | 19 +++- services/oooRequest.ts | 145 ++++++++++++++------------ 3 files changed, 109 insertions(+), 89 deletions(-) diff --git a/middlewares/oooRoleCheckMiddleware.ts b/middlewares/oooRoleCheckMiddleware.ts index 7e30a540b4..254932735f 100644 --- a/middlewares/oooRoleCheckMiddleware.ts +++ b/middlewares/oooRoleCheckMiddleware.ts @@ -3,28 +3,28 @@ import { CustomRequest, CustomResponse } from "../types/global"; import { REQUEST_TYPE } from "../constants/requests"; import { devFlagMiddleware } from "./devFlag"; import authorizeRoles from "./authorizeRoles"; + const { SUPERUSER } = require("../constants/roles"); -/** - * Conditional middleware that applies devFlag and superuser checks only for OOO requests. - * This allows onboarding requests to bypass these checks while maintaining security for OOO operations. - * - * @param {CustomRequest} req - The request object - * @param {CustomResponse} res - The response object - * @param {NextFunction} next - The next middleware function - */ -export const oooRoleCheckMiddleware = (req: CustomRequest, res: CustomResponse, next: NextFunction) => { +export const oooRoleCheckMiddleware = ( + req: CustomRequest, + res: CustomResponse, + next: NextFunction +) => { const requestType = req.body?.type; - if (requestType === REQUEST_TYPE.OOO) { - //TODO: Remove this middleware once the OOO feature is tested and ready to be used - devFlagMiddleware(req, res, (err: any) => { + // TODO: Remove this middleware once the OOO feature is tested and ready + return devFlagMiddleware(req, res, (err) => { if (err) return next(err); - authorizeRoles([SUPERUSER])(req, res, next); - }); - } else { - next(); + try { + return authorizeRoles([SUPERUSER])(req, res, next); + } catch (authErr) { + return next(authErr); + } + }); } -}; \ No newline at end of file + + return next(); +}; diff --git a/middlewares/validators/oooRequests.ts b/middlewares/validators/oooRequests.ts index 38e9d3086e..01c0c7069d 100644 --- a/middlewares/validators/oooRequests.ts +++ b/middlewares/validators/oooRequests.ts @@ -86,10 +86,19 @@ export const acknowledgeOooRequestValidator = async ( try { await acknowledgeOooRequestSchema.validateAsync(req.body, { abortEarly: false }); await paramsSchema.validateAsync(req.params, { abortEarly: false }); - next(); - } catch (error) { - const errorMessages = error.details.map((detail) => detail.message); - logger.error(`${ERROR_WHILE_ACKNOWLEDGING_REQUEST}: ${errorMessages}`); - return res.boom.badRequest(errorMessages); + return next(); + } catch (error: unknown) { + const isJoiValidationError = (err: unknown): err is joi.ValidationError => { + return Boolean(err) && typeof (err as any).isJoi === "boolean" && (err as any).isJoi === true && Array.isArray((err as any).details); + }; + + if (isJoiValidationError(error)) { + const errorMessages = error.details.map((detail) => detail.message); + logger.error(`${ERROR_WHILE_ACKNOWLEDGING_REQUEST}: ${errorMessages}`); + return res.boom.badRequest(errorMessages); + } + const genericMessage = typeof (error as any)?.message === "string" ? (error as any).message : String(error); + logger.error(`${ERROR_WHILE_ACKNOWLEDGING_REQUEST}: ${genericMessage}`); + return res.boom.badRequest([ERROR_WHILE_ACKNOWLEDGING_REQUEST]); } }; diff --git a/services/oooRequest.ts b/services/oooRequest.ts index cc953a2cd7..78f8e27e2d 100644 --- a/services/oooRequest.ts +++ b/services/oooRequest.ts @@ -111,25 +111,26 @@ export const createOooRequest = async ( * @throws {Error} Throws an error if an issue occurs during validation. */ -export const validateOooAcknowledgeRequest = async ( +export const validateOooAcknowledgeRequest = ( requestType: string, requestStatus: string, -) => { - try { - if (requestType !== REQUEST_TYPE.OOO) { - throw new BadRequest(INVALID_REQUEST_TYPE) - } - if (requestStatus === REQUEST_STATE.APPROVED) { - throw new BadRequest(REQUEST_ALREADY_APPROVED) - } - if (requestStatus === REQUEST_STATE.REJECTED) { - throw new BadRequest(REQUEST_ALREADY_REJECTED) - } - } catch (error) { - logger.error("Error while validating OOO acknowledge request", error); - throw error; + ) => { + if (requestType !== REQUEST_TYPE.OOO) { + logger.error(`Invalid request type: ${requestType}`); + throw new BadRequest(INVALID_REQUEST_TYPE); } -} + + if (requestStatus === REQUEST_STATE.APPROVED) { + logger.error(`Request already approved`); + throw new BadRequest(REQUEST_ALREADY_APPROVED); + } + + if (requestStatus === REQUEST_STATE.REJECTED) { + logger.error(`Request already rejected`); + throw new BadRequest(REQUEST_ALREADY_REJECTED); + } + }; + /** * Acknowledges the OOO request. @@ -145,56 +146,66 @@ export const acknowledgeOooRequest = async ( requestId: string, body: AcknowledgeOooRequestBody, superUserId: string, -) => { - try{ - const requestData = await getRequests({ id: requestId }) as OooStatusRequest | oldOooStatusRequest; - if (!requestData) { - throw new NotFound("Request not found"); - } - const { type, status, from, until, requestedBy } = requestData as OooStatusRequest; - - await validateOooAcknowledgeRequest(type as string, status as string); - const requestResult = await updateRequest(requestId, body, superUserId, REQUEST_TYPE.OOO); - if(requestResult.error){ - throw new BadRequest(requestResult.error); - } - const [acknowledgeLogType, returnMessage]= - requestResult.status === REQUEST_STATE.APPROVED ? [REQUEST_LOG_TYPE.REQUEST_APPROVED, REQUEST_APPROVED_SUCCESSFULLY] : [REQUEST_LOG_TYPE.REQUEST_REJECTED, REQUEST_REJECTED_SUCCESSFULLY]; - const requestLog = { - type: acknowledgeLogType, - meta: { - requestId: requestId, - action: LOG_ACTION.UPDATE, - userId: superUserId, - }, - body: requestResult, - } - await addLog(requestLog.type, requestLog.meta, requestLog.body); - if (requestResult.status === REQUEST_STATE.APPROVED) { - await addFutureStatus({ - requestId, - state: REQUEST_TYPE.OOO, - from: from, - endsOn: until, - userId: requestedBy, - message: body.comment, - }); - await createUserFutureStatus({ - requestId, - status: userState.OOO, - state: statusState.UPCOMING, - from: from, - endsOn: until, - userId: requestedBy, - message: body.comment, - createdAt: Date.now() - }); - } - return { - message: returnMessage, - }; + ) => { + try { + const requestData = await getRequests({ id: requestId }) as OooStatusRequest | oldOooStatusRequest; + if (!requestData) { + throw new NotFound("Request not found"); + } + + const { type, status, from, until, requestedBy } = requestData as OooStatusRequest; + + await validateOooAcknowledgeRequest(type, status); + + const requestResult = await updateRequest(requestId, body, superUserId, REQUEST_TYPE.OOO); + if (requestResult.error) { + throw new BadRequest(requestResult.error); + } + + const [acknowledgeLogType, returnMessage] = + requestResult.status === REQUEST_STATE.APPROVED + ? [REQUEST_LOG_TYPE.REQUEST_APPROVED, REQUEST_APPROVED_SUCCESSFULLY] + : [REQUEST_LOG_TYPE.REQUEST_REJECTED, REQUEST_REJECTED_SUCCESSFULLY]; + + await addLog( + acknowledgeLogType, + { + requestId, + action: LOG_ACTION.UPDATE, + userId: superUserId, + }, + requestResult, + ); + + if (requestResult.status === REQUEST_STATE.APPROVED) { + await addFutureStatus({ + requestId, + state: REQUEST_TYPE.OOO, + from, + endsOn: until, + userId: requestedBy, + message: body.comment ?? "", + }); + + await createUserFutureStatus({ + requestId, + status: userState.OOO, + state: statusState.UPCOMING, + from, + endsOn: until, + userId: requestedBy, + message: body.comment ?? "", + createdAt: Date.now(), + }); + } + + return { + message: returnMessage, + request: requestResult, + }; } catch (error) { - logger.error(ERROR_WHILE_ACKNOWLEDGING_REQUEST, error); - throw error; + logger.error(ERROR_WHILE_ACKNOWLEDGING_REQUEST, { error }); + throw error; } -} + }; + \ No newline at end of file From ac4714052aff8a1c18a5f55245198c8c3a7c0e25 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Thu, 4 Sep 2025 22:59:22 +0530 Subject: [PATCH 21/23] fix-jsdoc --- services/oooRequest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/oooRequest.ts b/services/oooRequest.ts index 78f8e27e2d..86603db810 100644 --- a/services/oooRequest.ts +++ b/services/oooRequest.ts @@ -106,7 +106,7 @@ export const createOooRequest = async ( /** * Validate the out-of-office acknowledgement request. * - * @param {string} requestType - The type of the request (OOO or OOO_ACKNOWLEDGEMENT). + * @param {string} requestType - The type of the request OOO. * @param {string} requestStatus - The status of the request (PENDING, APPROVED, REJECTED). * @throws {Error} Throws an error if an issue occurs during validation. */ From 4a6ccb3802021885f4abcf01cbcb66400b99b420 Mon Sep 17 00:00:00 2001 From: RishiChaubey31 Date: Fri, 5 Sep 2025 21:55:23 +0530 Subject: [PATCH 22/23] refactor: improve error handling and type safety in oooRequests - Updated acknowledgeOooRequestController to remove unnecessary return statement. - Enhanced acknowledgeOooRequestValidator to streamline Joi validation error handling. - Improved type assertions in updateRequestValidator for better clarity and type safety. - Refined transformRequestResponse logic to simplify request transformation based on environment conditions. --- controllers/oooRequests.ts | 2 +- middlewares/validators/oooRequests.ts | 10 ++++------ middlewares/validators/requests.ts | 2 +- services/oooRequest.ts | 7 ++++--- utils/requests.ts | 14 +++++++------- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/controllers/oooRequests.ts b/controllers/oooRequests.ts index 3590e18c61..18b31f71e2 100644 --- a/controllers/oooRequests.ts +++ b/controllers/oooRequests.ts @@ -180,6 +180,6 @@ export const acknowledgeOooRequestController = async ( catch(error){ logger.error(ERROR_WHILE_ACKNOWLEDGING_REQUEST, error); next(error); - return res; + return; } }; diff --git a/middlewares/validators/oooRequests.ts b/middlewares/validators/oooRequests.ts index 01c0c7069d..e2db46a822 100644 --- a/middlewares/validators/oooRequests.ts +++ b/middlewares/validators/oooRequests.ts @@ -88,17 +88,15 @@ export const acknowledgeOooRequestValidator = async ( await paramsSchema.validateAsync(req.params, { abortEarly: false }); return next(); } catch (error: unknown) { - const isJoiValidationError = (err: unknown): err is joi.ValidationError => { - return Boolean(err) && typeof (err as any).isJoi === "boolean" && (err as any).isJoi === true && Array.isArray((err as any).details); - }; - if (isJoiValidationError(error)) { + if (error instanceof joi.ValidationError) { const errorMessages = error.details.map((detail) => detail.message); logger.error(`${ERROR_WHILE_ACKNOWLEDGING_REQUEST}: ${errorMessages}`); return res.boom.badRequest(errorMessages); } - const genericMessage = typeof (error as any)?.message === "string" ? (error as any).message : String(error); - logger.error(`${ERROR_WHILE_ACKNOWLEDGING_REQUEST}: ${genericMessage}`); + + const errorMessage = error instanceof Error ? error.message : String(error); + logger.error(`${ERROR_WHILE_ACKNOWLEDGING_REQUEST}: ${errorMessage}`); return res.boom.badRequest([ERROR_WHILE_ACKNOWLEDGING_REQUEST]); } }; diff --git a/middlewares/validators/requests.ts b/middlewares/validators/requests.ts index 3987cfaf25..f7307f1f2c 100644 --- a/middlewares/validators/requests.ts +++ b/middlewares/validators/requests.ts @@ -147,7 +147,7 @@ export const updateRequestValidator = async ( res as OnboardingExtensionResponse, next); break; case REQUEST_TYPE.OOO: - await acknowledgeOooRequestValidator(req, res as OooRequestResponse, next); + await acknowledgeOooRequestValidator(req as AcknowledgeOooRequest, res as OooRequestResponse, next); break; default: return res.boom.badRequest("Invalid type"); diff --git a/services/oooRequest.ts b/services/oooRequest.ts index 86603db810..0e8aee6229 100644 --- a/services/oooRequest.ts +++ b/services/oooRequest.ts @@ -153,9 +153,10 @@ export const acknowledgeOooRequest = async ( throw new NotFound("Request not found"); } - const { type, status, from, until, requestedBy } = requestData as OooStatusRequest; - - await validateOooAcknowledgeRequest(type, status); + const { type, from, until, requestedBy } = requestData; + const status = 'status' in requestData ? requestData.status : (requestData as oldOooStatusRequest).state; + await validateOooAcknowledgeRequest(type, status); + const requestResult = await updateRequest(requestId, body, superUserId, REQUEST_TYPE.OOO); if (requestResult.error) { diff --git a/utils/requests.ts b/utils/requests.ts index f94e79f01a..23f80b4c8d 100644 --- a/utils/requests.ts +++ b/utils/requests.ts @@ -75,14 +75,14 @@ export const transformRequestResponse = (allRequests: (OooStatusRequest | oldOoo return request; } - if (dev) { - return 'status' in request - ? oldOOOSchema(request as OooStatusRequest) - : request; + if (dev && 'status' in request) { + return oldOOOSchema(request as OooStatusRequest); + } + + if (!dev && 'state' in request) { + return newOOOSchema(request as oldOooStatusRequest); } - return 'state' in request - ? newOOOSchema(request as oldOooStatusRequest) - : request; + return request; }); }; \ No newline at end of file From c3626469007a49ea60e948e081b1343fa6798157 Mon Sep 17 00:00:00 2001 From: Rishi Chaubey <155433512+RishiChaubey31@users.noreply.github.com> Date: Sun, 7 Sep 2025 00:14:22 +0530 Subject: [PATCH 23/23] feat: tests for OOO acknowledgement feature (#2474) * initial commit * added-more-test * fix-test-title * removed-try/catch-block * fix-try-catch-cases * added-try-catch * removed-functon * test: update assertions in oooRequest tests to use include for response validation --------- Co-authored-by: Mayank Bansal --- test/fixtures/oooRequest/oooRequest.ts | 2 +- test/integration/requests.test.ts | 24 +- test/unit/middlewares/oooRequests.test.ts | 30 +- test/unit/services/oooRequest.test.ts | 432 ++++++++++------------ 4 files changed, 232 insertions(+), 256 deletions(-) diff --git a/test/fixtures/oooRequest/oooRequest.ts b/test/fixtures/oooRequest/oooRequest.ts index 10dce241d4..36ba49cd8c 100644 --- a/test/fixtures/oooRequest/oooRequest.ts +++ b/test/fixtures/oooRequest/oooRequest.ts @@ -167,7 +167,7 @@ export const createOooRequests3 = { status: REQUEST_STATE.PENDING }; -export const acknowledgeOooRequest = { +export const testAcknowledgeOooRequest = { type: REQUEST_TYPE.OOO, status: REQUEST_STATE.APPROVED, comment: "OOO request approved as it's emergency." diff --git a/test/integration/requests.test.ts b/test/integration/requests.test.ts index 94b0d371c3..d5560138bc 100644 --- a/test/integration/requests.test.ts +++ b/test/integration/requests.test.ts @@ -15,7 +15,7 @@ import { validOooStatusRequests, validOooStatusUpdate, createOooRequests2, - acknowledgeOooRequest, + testAcknowledgeOooRequest, createOooRequests3, } from "../fixtures/oooRequest/oooRequest"; import { createRequest, updateRequest } from "../../models/requests"; @@ -351,7 +351,7 @@ describe("/requests OOO", function () { chai .request(app) .patch(`/requests/${testOooRequest.id}?dev=true`) - .send(acknowledgeOooRequest) + .send(testAcknowledgeOooRequest) .end(function (err, res) { expect(res).to.have.status(401); expect(res.body.error).to.equal("Unauthorized"); @@ -365,7 +365,7 @@ describe("/requests OOO", function () { .request(app) .patch(`/requests/${testOooRequest.id}?dev=false`) .set("cookie", `${cookieName}=${superUserToken}`) - .send(acknowledgeOooRequest) + .send(testAcknowledgeOooRequest) .end(function (err, res) { if (err) { return done(err); @@ -381,7 +381,7 @@ describe("/requests OOO", function () { .request(app) .patch(`/requests/11111111111111?dev=true`) .set("cookie", `${cookieName}=${superUserToken}`) - .send(acknowledgeOooRequest) + .send(testAcknowledgeOooRequest) .end(function (err, res) { if (err) { return done(err); @@ -397,13 +397,13 @@ describe("/requests OOO", function () { .request(app) .patch(`/requests/${testOooRequest.id}?dev=true`) .set("cookie", `${cookieName}=${authToken}`) - .send(acknowledgeOooRequest) + .send(testAcknowledgeOooRequest) .end(function (err, res) { if (err) { return done(err); } expect(res.statusCode).to.equal(403); - // expect(res.body.message).to.equal(UNAUTHORIZED_TO_ACKNOWLEDGE_OOO_REQUEST); + expect(res.body.message).to.equal(UNAUTHORIZED_TO_CREATE_OOO_REQUEST); done(); }); }); @@ -413,7 +413,7 @@ describe("/requests OOO", function () { .request(app) .patch(`/requests/${approvedOooRequest.id}?dev=true`) .set("cookie", `${cookieName}=${superUserToken}`) - .send(acknowledgeOooRequest) + .send(testAcknowledgeOooRequest) .end(function (err, res) { if (err) { return done(err); @@ -429,7 +429,7 @@ describe("/requests OOO", function () { .request(app) .patch(`/requests/${rejectedOooRequest.id}?dev=true`) .set("cookie", `${cookieName}=${superUserToken}`) - .send(acknowledgeOooRequest) + .send(testAcknowledgeOooRequest) .end(function (err, res) { if (err) { return done(err); @@ -445,7 +445,7 @@ describe("/requests OOO", function () { .request(app) .patch(`/requests/${onboardingRequest.id}?dev=true`) .set("cookie", `${cookieName}=${superUserToken}`) - .send(acknowledgeOooRequest) + .send(testAcknowledgeOooRequest) .end(function (err, res) { if (err) { return done(err); @@ -461,7 +461,7 @@ describe("/requests OOO", function () { .request(app) .patch(`/requests/${testOooRequest.id}?dev=true`) .set("cookie", `${cookieName}=${superUserToken}`) - .send(acknowledgeOooRequest) + .send(testAcknowledgeOooRequest) .end(function (err, res) { if (err) { return done(err); @@ -477,7 +477,7 @@ describe("/requests OOO", function () { .request(app) .patch(`/requests/${testOooRequest.id}?dev=true`) .set("cookie", `${cookieName}=${superUserToken}`) - .send({...acknowledgeOooRequest, status: REQUEST_STATE.REJECTED}) + .send({...testAcknowledgeOooRequest, status: REQUEST_STATE.REJECTED}) .end(function (err, res) { if (err) { return done(err); @@ -494,7 +494,7 @@ describe("/requests OOO", function () { .request(app) .patch(`/requests/${testOooRequest.id}?dev=true`) .set("cookie", `${cookieName}=${superUserToken}`) - .send(acknowledgeOooRequest) + .send(testAcknowledgeOooRequest) .end(function (err, res) { if (err) return done(err); expect(res.statusCode).to.equal(500); diff --git a/test/unit/middlewares/oooRequests.test.ts b/test/unit/middlewares/oooRequests.test.ts index 11272e8600..f8b4c103c8 100644 --- a/test/unit/middlewares/oooRequests.test.ts +++ b/test/unit/middlewares/oooRequests.test.ts @@ -4,15 +4,17 @@ const { expect } = chai; import { createOooStatusRequestValidator, - // acknowledgeOOORequestsValidator, + acknowledgeOooRequestValidator, } from "./../../../middlewares/validators/oooRequests"; -import { acknowledgeOooRequest, validOooStatusRequests, validOooStatusUpdate } from "../../fixtures/oooRequest/oooRequest"; +import { testAcknowledgeOooRequest, validOooStatusRequests, validOooStatusUpdate } from "../../fixtures/oooRequest/oooRequest"; import _ from "lodash"; +import { AcknowledgeOooRequest, OooRequestResponse } from "../../../types/oooRequest"; describe("OOO Status Request Validators", function () { let req: any; let res: any; let nextSpy; + beforeEach(function () { res = { boom: { @@ -91,40 +93,46 @@ describe("OOO Status Request Validators", function () { }); }); - describe.skip("acknowledgeOOORequestsValidator", function () { + describe("acknowledgeOOORequestsValidator", function () { it("should not validate for an invalid request for invalid request type", async function () { req = { - body: { ...acknowledgeOooRequest, type: "XYZ"} + body: { ...testAcknowledgeOooRequest, type: "XYZ"}, + params: { id: "test-id" } }; - // await acknowledgeOOORequestsValidator(req, res, nextSpy); + await acknowledgeOooRequestValidator(req as AcknowledgeOooRequest, res as OooRequestResponse, nextSpy); + expect(res.boom.badRequest.calledOnce).to.be.true; expect(nextSpy.notCalled).to.be.true; }); it("should not validate for an invalid request if status is incorrect", async function () { req = { - body: { ...acknowledgeOooRequest, status: "PENDING"} + body: { ...testAcknowledgeOooRequest, status: "PENDING"}, + params: { id: "test-id" } }; - // await acknowledgeOOORequestsValidator(req, res, nextSpy); + await acknowledgeOooRequestValidator(req as AcknowledgeOooRequest, res as OooRequestResponse, nextSpy); + expect(res.boom.badRequest.calledOnce).to.be.true; expect(nextSpy.notCalled).to.be.true; }); it("should validate for a valid acknowledge OOO request if comment not provided by superusers", async function() { req = { - body: _.omit(acknowledgeOooRequest, "comment") + body: _.omit(testAcknowledgeOooRequest, "comment"), + params: { id: "test-id" } }; res = {}; - // await acknowledgeOOORequestsValidator(req, res, nextSpy); + await acknowledgeOooRequestValidator(req as AcknowledgeOooRequest, res as OooRequestResponse, nextSpy); expect(nextSpy.calledOnce).to.be.true; }); it("should validate for a valid acknowledge OOO request", async function() { req = { - body: acknowledgeOooRequest + body: testAcknowledgeOooRequest, + params: { id: "test-id" } }; res = {}; - // await acknowledgeOOORequestsValidator(req, res, nextSpy); + await acknowledgeOooRequestValidator(req as AcknowledgeOooRequest, res as OooRequestResponse, nextSpy); expect(nextSpy.calledOnce).to.be.true; }); }); diff --git a/test/unit/services/oooRequest.test.ts b/test/unit/services/oooRequest.test.ts index c71580b1a6..f66d67fca6 100644 --- a/test/unit/services/oooRequest.test.ts +++ b/test/unit/services/oooRequest.test.ts @@ -1,245 +1,213 @@ import sinon from "sinon"; import cleanDb from "../../utils/cleanDb"; import { - INVALID_REQUEST_TYPE, - REQUEST_ALREADY_APPROVED, - REQUEST_ALREADY_REJECTED, - REQUEST_APPROVED_SUCCESSFULLY, - REQUEST_DOES_NOT_EXIST, - REQUEST_REJECTED_SUCCESSFULLY, - REQUEST_STATE, - REQUEST_TYPE, - OOO_STATUS_ALREADY_EXIST, - USER_STATUS_NOT_FOUND, + INVALID_REQUEST_TYPE, + REQUEST_ALREADY_APPROVED, + REQUEST_ALREADY_REJECTED, + REQUEST_APPROVED_SUCCESSFULLY, + REQUEST_DOES_NOT_EXIST, + REQUEST_REJECTED_SUCCESSFULLY, + REQUEST_STATE, + REQUEST_TYPE, + OOO_STATUS_ALREADY_EXIST, + USER_STATUS_NOT_FOUND, } from "../../../constants/requests"; -import { - createOooRequest, - validateUserStatus, - // acknowledgeOOORequest, - // validateOOOAcknowledgeRequest +import { + createOooRequest, + validateUserStatus, + acknowledgeOooRequest, + validateOooAcknowledgeRequest, } from "../../../services/oooRequest"; import { expect } from "chai"; -import { testUserStatus, validOooStatusRequests, validUserCurrentStatus, createdOOORequest } from "../../fixtures/oooRequest/oooRequest"; +import { + testUserStatus, + validOooStatusRequests, + validUserCurrentStatus, + createdOOORequest, +} from "../../fixtures/oooRequest/oooRequest"; import { updateUserStatus } from "../../../models/userStatus"; import { userState } from "../../../constants/userStatus"; import addUser from "../../utils/addUser"; import userDataFixture from "../../fixtures/user/user"; import * as logService from "../../../services/logService"; -import { createOooRequests3 } from "../../fixtures/oooRequest/oooRequest"; +import { createOooRequests3, testAcknowledgeOooRequest } from "../../fixtures/oooRequest/oooRequest"; import { createRequest } from "../../../models/requests"; -describe("Test OOO Request Service", function() { - - let testUserName: string; - let testUserId: string; - const errorMessage = "Unexpected error occured"; - - beforeEach(async function() { - const users = userDataFixture(); - testUserId = await addUser(users[8]); - testUserName = users[8].username; - }); - - afterEach(async function() { - sinon.restore(); - await cleanDb(); - }); - - describe("validateUserStatus", function() { - - it("should return USER_STATUS_NOT_FOUND if user status not found", async function() { - const validationResponse = await validateUserStatus( - testUserId, - { ...testUserStatus, userStatusExists: false } - ); - expect(validationResponse).to.be.not.undefined; - expect(validationResponse.error).to.equal(USER_STATUS_NOT_FOUND); - }); - - it("should return OOO_STATUS_ALREADY_EXIST if user status is already OOO", async function() { - const validationResponse = await validateUserStatus( - testUserId, - { - ...testUserStatus, - data: { - ...testUserStatus.data, - currentStatus: { - ...testUserStatus.data.currentStatus, - state: userState.OOO - } - } - } - ); - expect(validationResponse).to.be.not.undefined; - expect(validationResponse.error).to.equal(OOO_STATUS_ALREADY_EXIST); - }); - - it("should return undefined when all validation checks passes", async function() { - const response = await validateUserStatus(testUserId, testUserStatus); - expect(response).to.not.exist; - }); - }); - - describe("createOooRequest", function() { - - beforeEach(async function() { - await updateUserStatus(testUserId, testUserStatus.data); - }); - - afterEach(async function () { - sinon.restore(); - }); - - it("should create OOO request", async function() { - const response = await createOooRequest(validOooStatusRequests, testUserId); - expect(response).to.deep.include({ - ...createdOOORequest, - id: response.id, - requestedBy: testUserId - }); - }); - - it("should throw error", async function () { - sinon.stub(logService, "addLog").throws(new Error(errorMessage)); - - try { - await createOooRequest(validOooStatusRequests, testUserId); - } catch (error) { - expect(error.message).to.equal(errorMessage); - } - }); - }); - - describe.skip("validateOOOAcknowledgeRequest", function() { - - let testOooRequest; - - beforeEach(async function () { - testOooRequest = await createRequest({ - ...createOooRequests3, - userId: testUserId, - comment: null, - lastModifiedBy: null, - }); - }); - - it("should return INVALID_REQUEST_TYPE if request type is not OOO", async function() { - // const validationResponse = await validateOOOAcknowledgeRequest( - // testOooRequest.id, - // REQUEST_TYPE.ONBOARDING, - // testOooRequest.status - // ); - // expect(validationResponse.error).to.be.not.undefined; - // expect(validationResponse.error).to.equal(INVALID_REQUEST_TYPE); - }); - - it("should return REQUEST_ALREADY_APPROVED if request is already approved", async function() { - // const validationResponse = await validateOOOAcknowledgeRequest( - // testOooRequest.id, - // testOooRequest.type, - // REQUEST_STATE.APPROVED - // ); - // expect(validationResponse.error).to.be.not.undefined; - // expect(validationResponse.error).to.equal(REQUEST_ALREADY_APPROVED); - }); - - it("should return REQUEST_ALREADY_REJECTED if request is already rejected", async function() { - // const validationResponse = await validateOOOAcknowledgeRequest( - // testOooRequest.id, - // testOooRequest.type, - // REQUEST_STATE.REJECTED - // ); - // expect(validationResponse.error).to.be.not.undefined; - // expect(validationResponse.error).to.equal(REQUEST_ALREADY_REJECTED); - }); - - it("should return undefined when all validation checks passes", async function() { - // const response = await validateOOOAcknowledgeRequest( - // testOooRequest.id, - // testOooRequest.type, - // testOooRequest.status - // ); - // expect(response).to.not.exist; - }); - }); - - describe.skip("acknowledgeOOORequest", function() { - - let testSuperUserId; - let testOooRequest; - - beforeEach(async function () { - const users = userDataFixture(); - const superUserId = await addUser(users[4]); - testSuperUserId = superUserId; - - testOooRequest = await createRequest({ - ...createOooRequests3, - userId: testUserId, - comment: null, - lastModifiedBy: null, - }); - }); - - it("should return REQUEST_DOES_NOT_EXIST if invalid request id is passed", async function () { - // const invalidOOORequestId = "11111111111111111111"; - // const response = await acknowledgeOOORequest( - // invalidOOORequestId, - // acknowledgeOooRequest, - // testSuperUserId - // ); - // expect(response.error).to.equal(REQUEST_DOES_NOT_EXIST); - }); - - it("should approve OOO request", async function() { - // const response = await acknowledgeOOORequest( - // testOooRequest.id, - // acknowledgeOooRequest, - // testSuperUserId - // ); - // expect(response).to.deep.include({ - // message: REQUEST_APPROVED_SUCCESSFULLY, - // data: { - // ...acknowledgeOooRequest, - // id: testOooRequest.id, - // lastModifiedBy: testSuperUserId, - // updatedAt: response.data.updatedAt - // } - // }); - }); - - it("should reject OOO request", async function() { - // const response = await acknowledgeOOORequest( - // testOooRequest.id, - // { ...acknowledgeOooRequest, status: REQUEST_STATE.REJECTED }, - // testSuperUserId - // ); - // expect(response).to.deep.include({ - // message: REQUEST_REJECTED_SUCCESSFULLY, - // data: { - // ...acknowledgeOooRequest, - // id: testOooRequest.id, - // status: REQUEST_STATE.REJECTED, - // lastModifiedBy: testSuperUserId, - // updatedAt: response.data.updatedAt - // } - // }); - }); - - it("should throw error", async function() { - // sinon.stub(logService, "addLog").throws(new Error(errorMessage)); - // const createSpy = sinon.spy(require("../../../services/oooRequest"), "acknowledgeOOORequest"); - - // try { - // await acknowledgeOOORequest( - // testOooRequest.id, - // acknowledgeOooRequest, - // testSuperUserId - // ); - // } catch (error) { - // expect(error.message).to.equal(errorMessage); - // expect(createSpy.calledOnce).to.be.true; - // } - }); - }); -}); \ No newline at end of file +describe("Test OOO Request Service", function () { + let testUserName: string; + let testUserId: string; + const errorMessage = "Unexpected error occured"; + + + + beforeEach(async function () { + const users = userDataFixture(); + testUserId = await addUser(users[8]); + testUserName = users[8].username; + }); + + afterEach(async function () { + sinon.restore(); + await cleanDb(); + }); + + describe("validateUserStatus", function () { + it("should return USER_STATUS_NOT_FOUND if user status not found", async function () { + const validationResponse = await validateUserStatus(testUserId, { ...testUserStatus, userStatusExists: false }); + expect(validationResponse).to.be.not.undefined; + expect(validationResponse.error).to.equal(USER_STATUS_NOT_FOUND); + }); + + it("should return OOO_STATUS_ALREADY_EXIST if user status is already OOO", async function () { + const validationResponse = await validateUserStatus(testUserId, { + ...testUserStatus, + data: { + ...testUserStatus.data, + currentStatus: { + ...testUserStatus.data.currentStatus, + state: userState.OOO, + }, + }, + }); + expect(validationResponse).to.be.not.undefined; + expect(validationResponse.error).to.equal(OOO_STATUS_ALREADY_EXIST); + }); + + it("should return undefined when all validation checks passes", async function () { + const response = await validateUserStatus(testUserId, testUserStatus); + expect(response).to.not.exist; + }); + }); + + describe("createOooRequest", function () { + beforeEach(async function () { + await updateUserStatus(testUserId, testUserStatus.data); + }); + + afterEach(async function () { + sinon.restore(); + }); + + it("should create OOO request", async function () { + const response = await createOooRequest(validOooStatusRequests, testUserId); + expect(response).to.deep.include({ + ...createdOOORequest, + id: response.id, + requestedBy: testUserId, + }); + }); + + it("should throw error", async function () { + sinon.stub(logService, "addLog").throws(new Error(errorMessage)); + + try { + await createOooRequest(validOooStatusRequests, testUserId); + expect.fail("Should have thrown an error"); + } catch (error) { + expect(error.message).to.equal(errorMessage); + } + }); + }); + + describe("validateOOOAcknowledgeRequest", function () { + let testOooRequest; + + beforeEach(async function () { + testOooRequest = await createRequest({ + ...createOooRequests3, + userId: testUserId, + comment: null, + lastModifiedBy: null, + }); + }); + + it("should return INVALID_REQUEST_TYPE if request type is not OOO", async function () { + try { + await validateOooAcknowledgeRequest(REQUEST_TYPE.ONBOARDING, testOooRequest.status); + expect.fail("Should have thrown an error"); + } catch (error) { + expect(error.message).to.equal(INVALID_REQUEST_TYPE); + } + }); + + it("should return REQUEST_ALREADY_APPROVED if request is already approved", async function () { + try { + await validateOooAcknowledgeRequest(REQUEST_TYPE.OOO, REQUEST_STATE.APPROVED); + expect.fail("Should have thrown an error"); + } catch (error) { + expect(error.message).to.equal(REQUEST_ALREADY_APPROVED); + } + }); + + it("should return REQUEST_ALREADY_REJECTED if request is already rejected", async function () { + try { + await validateOooAcknowledgeRequest(REQUEST_TYPE.OOO, REQUEST_STATE.REJECTED); + expect.fail("Should have thrown an error"); + } catch (error) { + expect(error.message).to.equal(REQUEST_ALREADY_REJECTED); + } + }); + + it("should return undefined when all validation checks passes", async function () { + const response = await validateOooAcknowledgeRequest(REQUEST_TYPE.OOO, REQUEST_STATE.PENDING); + expect(response).to.not.exist; + }); + }); + + describe("acknowledgeOOORequest", function () { + let testSuperUserId; + let testOooRequest; + + beforeEach(async function () { + const users = userDataFixture(); + const superUserId = await addUser(users[4]); + testSuperUserId = superUserId; + + testOooRequest = await createRequest({ + ...createOooRequests3, + userId: testUserId, + comment: null, + lastModifiedBy: null, + }); + }); + + it("should return 'Request not found' if invalid request id is passed", async function () { + const invalidOOORequestId = "11111111111111111111"; + try { + await acknowledgeOooRequest(invalidOOORequestId, testAcknowledgeOooRequest, testSuperUserId); + expect.fail("Should have thrown an error"); + } catch (error) { + expect(error.message).to.equal("Request not found"); + } + }); + + it("should approve OOO request", async function () { + const response = await acknowledgeOooRequest(testOooRequest.id, testAcknowledgeOooRequest, testSuperUserId); + expect(response).to.include({ + message: REQUEST_APPROVED_SUCCESSFULLY, + }); + }); + + it("should reject OOO request", async function () { + const response = await acknowledgeOooRequest( + testOooRequest.id, + { ...testAcknowledgeOooRequest, status: REQUEST_STATE.REJECTED }, + testSuperUserId + ); + expect(response).to.include({ + message: REQUEST_REJECTED_SUCCESSFULLY, + }); + }); + + it("should propagate error when logging fails", async function () { + sinon.stub(logService, "addLog").throws(new Error(errorMessage)); + + try { + await acknowledgeOooRequest(testOooRequest.id, testAcknowledgeOooRequest, testSuperUserId); + expect.fail("Should have thrown an error"); + } catch (error) { + expect(error.message).to.equal(errorMessage); + } + }); + }); +});