diff --git a/constants/requests.ts b/constants/requests.ts index 498cab9d6..06e3ef6a8 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 36d2baeab..99089bcfb 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. @@ -148,3 +153,46 @@ 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; + } +}; \ No newline at end of file diff --git a/controllers/requests.ts b/controllers/requests.ts index fd8974ea0..5e8bb7a2c 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"; @@ -16,8 +16,7 @@ import { createTaskRequestController } from "./taskRequestsv2"; import { OnboardingExtensionCreateRequest, OnboardingExtensionResponse, UpdateOnboardingExtensionStateRequest } from "../types/onboardingExtension"; import { createOnboardingExtensionRequestController, updateOnboardingExtensionRequestController, updateOnboardingExtensionRequestState } from "./onboardingExtension"; import { UpdateOnboardingExtensionRequest } from "../types/onboardingExtension"; - -import { Request } from "express"; +import { NextFunction, Request } from "express"; export const createRequestController = async ( req: OooRequestCreateRequest | ExtensionRequestRequest | TaskRequestRequest | OnboardingExtensionCreateRequest, @@ -121,9 +120,13 @@ 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/validators/oooRequests.ts b/middlewares/validators/oooRequests.ts index ab73929f1..2836d9b3f 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,46 @@ 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({ + "type.any": "type is required", + }) + }); + +/** + * 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:{message: string}) => 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 80ff0478b..d53fadc96 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"; @@ -125,18 +125,24 @@ 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 => { const type = req.body.type; + switch (type) { + case REQUEST_TYPE.OOO: + await acknowledgeOooRequest( + req, + res as OooRequestResponse, next); + break; case REQUEST_TYPE.ONBOARDING: await updateOnboardingExtensionRequestValidator( req, @@ -145,4 +151,4 @@ export const updateRequestValidator = async ( default: return res.boom.badRequest("Invalid type"); } -}; \ No newline at end of file +}; diff --git a/models/requests.ts b/models/requests.ts index 064eebd8c..3c8272a48 100644 --- a/models/requests.ts +++ b/models/requests.ts @@ -8,6 +8,7 @@ import { REQUEST_DOES_NOT_EXIST, } from "../constants/requests"; import { getUserId } from "../utils/users"; +import { NotFound } from "http-errors"; const SIZE = 5; export const createRequest = async (body: any) => { @@ -69,6 +70,21 @@ 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"; diff --git a/services/oooRequest.ts b/services/oooRequest.ts index 2e2f35ab7..9a3c24110 100644 --- a/services/oooRequest.ts +++ b/services/oooRequest.ts @@ -5,12 +5,22 @@ import { REQUEST_LOG_TYPE, REQUEST_STATE, USER_STATUS_NOT_FOUND, + REQUEST_TYPE, + REQUEST_ALREADY_APPROVED, + REQUEST_ALREADY_REJECTED, + REQUEST_APPROVED_SUCCESSFULLY, + REQUEST_REJECTED_SUCCESSFULLY, + INVALID_REQUEST_TYPE, } from "../constants/requests"; import { userState } from "../constants/userStatus"; -import { createRequest } from "../models/requests"; +import { createRequest, getRequestById } from "../models/requests"; import { OooStatusRequest, OooStatusRequestBody } from "../types/oooRequest"; import { UserStatus } from "../types/userStatus"; import { addLog } from "./logService"; +import { BadRequest, Conflict } from "http-errors"; +import { updateRequest } from "../models/requests"; +import { AcknowledgeOooRequestBody } from "../types/oooRequest"; +import { addFutureStatus } from "../models/userStatus"; /** * Validates the user status. @@ -93,3 +103,101 @@ export const createOooRequest = async ( throw error; } } + +/** + * Validates an Out-Of-Office (OOO) acknowledge request + * + * @param {string} requestId - The unique identifier of the request. + * @param {string} requestType - The type of the request (expected to be 'OOO'). + * @param {string} requestStatus - The current status of the request. + * @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 an Out-of-Office (OOO) request + * + * @param {string} requestId - The ID of the OOO request to acknowledge. + * @param {AcknowledgeOooRequestBody} body - The acknowledgement body containing acknowledging details. + * @param {string} superUserId - The unique identifier of the superuser user. + * @returns {Promise} The acknowledged OOO request. + * @throws {Error} Throws an error if an issue occurs during acknowledgment process. + */ +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, + 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.userId, + message: body.comment, + }); + } + + return { + message: returnMessage, + data: { + id: requestResult.id, + ...requestResult, + }, + }; + } catch (error) { + logger.error("Error while acknowledging OOO request", error); + throw error; + } +} \ No newline at end of file diff --git a/test/fixtures/oooRequest/oooRequest.ts b/test/fixtures/oooRequest/oooRequest.ts index 30b72d2a0..83a587684 100644 --- a/test/fixtures/oooRequest/oooRequest.ts +++ b/test/fixtures/oooRequest/oooRequest.ts @@ -168,7 +168,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 2e83acf18..955027524 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"; @@ -30,7 +30,7 @@ import { REQUEST_REJECTED_SUCCESSFULLY, REQUEST_ALREADY_REJECTED, INVALID_REQUEST_TYPE, - // UNAUTHORIZED_TO_ACKNOWLEDGE_OOO_REQUEST, + UNAUTHORIZED_TO_UPDATE_REQUEST, UNAUTHORIZED_TO_CREATE_OOO_REQUEST, USER_STATUS_NOT_FOUND, OOO_STATUS_ALREADY_EXIST, @@ -323,7 +323,7 @@ describe("/requests OOO", function () { }); }); - describe.skip("PATCH /requests/:id", function () { + describe("PATCH /requests/:id", function () { let testOooRequest; let onboardingRequest; let approvedOooRequest; @@ -352,7 +352,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"); @@ -366,7 +366,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); @@ -382,7 +382,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); @@ -398,13 +398,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_UPDATE_REQUEST); done(); }); }); @@ -414,7 +414,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); @@ -430,7 +430,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); @@ -446,7 +446,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); @@ -462,7 +462,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); @@ -478,7 +478,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); @@ -495,7 +495,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 11272e860..e369859c9 100644 --- a/test/unit/middlewares/oooRequests.test.ts +++ b/test/unit/middlewares/oooRequests.test.ts @@ -4,9 +4,9 @@ const { expect } = chai; import { createOooStatusRequestValidator, - // acknowledgeOOORequestsValidator, + acknowledgeOooRequest, } from "./../../../middlewares/validators/oooRequests"; -import { acknowledgeOooRequest, validOooStatusRequests, validOooStatusUpdate } from "../../fixtures/oooRequest/oooRequest"; +import { testAcknowledgeOooRequest, validOooStatusRequests, validOooStatusUpdate } from "../../fixtures/oooRequest/oooRequest"; import _ from "lodash"; describe("OOO Status Request Validators", function () { @@ -91,40 +91,40 @@ 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"} }; - // await acknowledgeOOORequestsValidator(req, res, nextSpy); + await acknowledgeOooRequest(req, res, nextSpy); 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"} }; - // await acknowledgeOOORequestsValidator(req, res, nextSpy); + await acknowledgeOooRequest(req, res, nextSpy); 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") }; res = {}; - // await acknowledgeOOORequestsValidator(req, res, nextSpy); + await acknowledgeOooRequest(req, res, nextSpy); expect(nextSpy.calledOnce).to.be.true; }); it("should validate for a valid acknowledge OOO request", async function() { req = { - body: acknowledgeOooRequest + body: testAcknowledgeOooRequest }; res = {}; - // await acknowledgeOOORequestsValidator(req, res, nextSpy); + await acknowledgeOooRequest(req, res, nextSpy); expect(nextSpy.calledOnce).to.be.true; }); }); diff --git a/test/unit/models/requests.test.ts b/test/unit/models/requests.test.ts index 954024c86..c719c8afe 100644 --- a/test/unit/models/requests.test.ts +++ b/test/unit/models/requests.test.ts @@ -1,14 +1,15 @@ import { expect } from "chai"; import cleanDb from "../../utils/cleanDb"; -import { createRequest, getRequests, updateRequest, getRequestByKeyValues } from "../../../models/requests"; +import { createRequest, getRequests, updateRequest, getRequestByKeyValues, getRequestById } from "../../../models/requests"; import { createOooRequests, createOooRequests2, + createOooRequests3, createOooStatusRequests, updateOooApprovedRequests, updateOooRejectedRequests, } from "./../../fixtures/oooRequest/oooRequest"; -import { REQUEST_STATE, REQUEST_TYPE } from "../../../constants/requests"; +import { REQUEST_DOES_NOT_EXIST, REQUEST_STATE, REQUEST_TYPE } from "../../../constants/requests"; import userDataFixture from "./../../fixtures/user/user"; import addUser from "../../utils/addUser"; const userData = userDataFixture(); @@ -179,4 +180,20 @@ describe("models/oooRequests", () => { expect(oooRequestData).to.be.equal(null); }); }); + + describe("getRequestById", () => { + + it("should return request using request id", async () => { + const oooRequest = await createRequest(createOooRequests3); + const response = await getRequestById(oooRequest.id); + expect(response).to.deep.include(createOooRequests3); + }); + + it("should return REQUEST_DOES_NOT_EXIST for invalid request id", async () => { + await getRequestById("111111111111").catch((error) => { + expect(error).to.be.not.undefined; + expect(error.message).to.equal(REQUEST_DOES_NOT_EXIST); + }); + }); + }); }); diff --git a/test/unit/services/oooRequest.test.ts b/test/unit/services/oooRequest.test.ts index eb63242b6..a38db65d2 100644 --- a/test/unit/services/oooRequest.test.ts +++ b/test/unit/services/oooRequest.test.ts @@ -15,8 +15,8 @@ import { import { createOooRequest, validateUserStatus, - // acknowledgeOOORequest, - // validateOOOAcknowledgeRequest + acknowledgeOooRequest, + validateOooAcknowledgeRequest } from "../../../services/oooRequest"; import { expect } from "chai"; import { testUserStatus, validOooStatusRequests, validUserCurrentStatus, createdOOORequest } from "../../fixtures/oooRequest/oooRequest"; @@ -25,8 +25,11 @@ 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 { testAcknowledgeOooRequest, createOooRequests3 } from "../../fixtures/oooRequest/oooRequest"; import { createRequest } from "../../../models/requests"; +import * as requestModel from "../../../models/requests"; +import * as oooRequestService from "../../../services/oooRequest"; +import { NotFound, Conflict, BadRequest } from "http-errors"; describe("Test OOO Request Service", function() { @@ -111,7 +114,7 @@ describe("Test OOO Request Service", function() { }); }); - describe.skip("validateOOOAcknowledgeRequest", function() { + describe("validateOooAcknowledgeRequest", function() { let testOooRequest; @@ -125,46 +128,45 @@ describe("Test OOO Request Service", function() { }); 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); + await validateOooAcknowledgeRequest( + REQUEST_TYPE.ONBOARDING, + testOooRequest.status + ).catch((error) => { + expect(error).to.be.not.undefined; + expect(error.message).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); + await validateOooAcknowledgeRequest( + testOooRequest.type, + REQUEST_STATE.APPROVED + ).catch((error) => { + expect(error).to.be.not.undefined; + expect(error.message).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); + await validateOooAcknowledgeRequest( + testOooRequest.type, + REQUEST_STATE.REJECTED + ).catch((error) => { + expect(error).to.be.not.undefined; + expect(error.message).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; + const response = await validateOooAcknowledgeRequest( + testOooRequest.type, + testOooRequest.status + ); + expect(response).to.not.exist; }); }); - describe.skip("acknowledgeOOORequest", function() { + describe("acknowledgeOooRequest", function() { let testSuperUserId; let testOooRequest; @@ -183,64 +185,81 @@ describe("Test OOO Request Service", function() { }); 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); + sinon.stub(requestModel, "getRequestById").throws(new NotFound(REQUEST_DOES_NOT_EXIST)); + await acknowledgeOooRequest( + "11111111111111111111", + testAcknowledgeOooRequest, + testSuperUserId + ).catch((error) => { + expect(error).to.be.not.undefined; + expect(error.message).to.equal(REQUEST_DOES_NOT_EXIST); + }); + }); + + it("should return REQUEST_ALREADY_APPROVED when status is approved", async function () { + sinon.stub(requestModel, "getRequestById").returns(testOooRequest); + sinon.stub(oooRequestService, "validateOooAcknowledgeRequest").throws(new Conflict(REQUEST_ALREADY_APPROVED)); + await acknowledgeOooRequest( + testOooRequest.id, + testAcknowledgeOooRequest, + testSuperUserId + ).catch((error) => { + expect(error).to.be.not.undefined; + expect(error.message).to.equal(REQUEST_ALREADY_APPROVED); + }); + }); + + it("should throw error when approve or rejection fails", async function () { + sinon.stub(requestModel, "getRequestById").returns(testOooRequest); + sinon.stub(oooRequestService, "validateOooAcknowledgeRequest"); + sinon.stub(requestModel, "updateRequest").throws(new BadRequest(errorMessage)); + await acknowledgeOooRequest( + testOooRequest.id, + testAcknowledgeOooRequest, + testSuperUserId + ).catch((error) => { + expect(error).to.be.not.undefined; + expect(error.message).to.equal(errorMessage); + }); }); 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 - // } - // }); + sinon.stub(requestModel, "getRequestById").returns(testOooRequest); + sinon.stub(oooRequestService, "validateOooAcknowledgeRequest"); + sinon.stub(requestModel, "updateRequest").returns({ ...testOooRequest, status: REQUEST_STATE.APPROVED}); + const response = await acknowledgeOooRequest( + testOooRequest.id, + testAcknowledgeOooRequest, + testSuperUserId + ); + expect(response.message).to.equal(REQUEST_APPROVED_SUCCESSFULLY); + expect(response.data.status).to.equal(REQUEST_STATE.APPROVED); }); 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 - // } - // }); + sinon.stub(requestModel, "getRequestById").returns(testOooRequest); + sinon.stub(oooRequestService, "validateOooAcknowledgeRequest"); + sinon.stub(requestModel, "updateRequest").returns({ ...testOooRequest, status: REQUEST_STATE.REJECTED}); + const response = await acknowledgeOooRequest( + testOooRequest.id, + { ...testAcknowledgeOooRequest, status: REQUEST_STATE.REJECTED }, + testSuperUserId + ); + expect(response.message).to.equal(REQUEST_REJECTED_SUCCESSFULLY); + expect(response.data.status).to.equal(REQUEST_STATE.REJECTED); }); 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; - // } + sinon.stub(requestModel, "getRequestById").throws(new Error(errorMessage)); + try { + await acknowledgeOooRequest( + testOooRequest.id, + testAcknowledgeOooRequest, + testSuperUserId + ); + } catch (error) { + expect(error.message).to.equal(errorMessage); + } }); }); }); \ No newline at end of file diff --git a/types/oooRequest.d.ts b/types/oooRequest.d.ts index 6b1c282a8..865daeb94 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; +}