Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
402cc8b
initial commit
RishiChaubey31 Aug 23, 2025
3666b9d
fix-services-ooorequest-test
RishiChaubey31 Aug 23, 2025
9a9926e
Merge remote-tracking branch 'origin/develop' into feature/ooo-req-ac…
RishiChaubey31 Aug 26, 2025
303a1d7
fix-module-imports
RishiChaubey31 Aug 26, 2025
e046d74
refactor: streamline OOO request handling and enhance validation
RishiChaubey31 Aug 29, 2025
dcffdca
feat: enhance OOO request creation with user role validation
RishiChaubey31 Aug 30, 2025
1f8b831
fix-getRequestByID-function
RishiChaubey31 Aug 30, 2025
03d3198
fix-validator-name
RishiChaubey31 Aug 30, 2025
16b90ba
refactor: remove redundant request constants from REQUEST_LOG_TYPE
RishiChaubey31 Aug 30, 2025
d94bbda
fix-ooo-models-condtions
RishiChaubey31 Sep 1, 2025
602a0bb
refactor: enhance OOO request handling and validation
RishiChaubey31 Sep 2, 2025
d608d9c
refactor: update error handling in OOO request service
RishiChaubey31 Sep 2, 2025
9ad3048
fix/function-name
RishiChaubey31 Sep 3, 2025
577cef8
fix/spaces
RishiChaubey31 Sep 3, 2025
f4ff103
refactor: simplify variable usage in acknowledgeOooRequest
RishiChaubey31 Sep 3, 2025
f1bfe6a
refactor: rename acknowledgeOooRequest for consistency
RishiChaubey31 Sep 3, 2025
b2fb635
refactor: update acknowledgeOooRequest to improve clarity and maintai…
RishiChaubey31 Sep 3, 2025
f47848a
refactor: enhance type handling in acknowledgeOooRequest
RishiChaubey31 Sep 3, 2025
ed7e062
Merge branch 'develop' into feature/ooo-req-acknowledgement
MayankBansal12 Sep 3, 2025
baad820
fix-nested-loop-refactor
RishiChaubey31 Sep 3, 2025
ea2222c
fix/change-conflict-to-badrequest
RishiChaubey31 Sep 4, 2025
6e63df6
refactor: improve oooRoleCheckMiddleware and oooRequests validation
RishiChaubey31 Sep 4, 2025
ac47140
fix-jsdoc
RishiChaubey31 Sep 4, 2025
4a6ccb3
refactor: improve error handling and type safety in oooRequests
RishiChaubey31 Sep 5, 2025
c362646
feat: tests for OOO acknowledgement feature (#2474)
RishiChaubey31 Sep 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions constants/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,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";
Expand Down
43 changes: 39 additions & 4 deletions controllers/oooRequests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,24 @@ 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_CREATE_OOO_REQUEST,
ERROR_WHILE_ACKNOWLEDGING_REQUEST,
REQUEST_ID_REQUIRED,
} from "../constants/requests";
import { statusState } from "../constants/userStatus";
import { logType } from "../constants/logs";
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 { createOooRequest, validateUserStatus, acknowledgeOooRequest as acknowledgeOooRequestService } 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.
Expand Down Expand Up @@ -78,7 +81,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,
Expand Down Expand Up @@ -148,3 +151,35 @@ export const updateOooRequestController = async (req: UpdateRequest, res: Custom
return res.boom.badImplementation(ERROR_WHILE_UPDATING_REQUEST);
}
};
/**
* Acknowledges an Out-of-Office (OOO) request by updating its status to approved or rejected
*
* @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<OooRequestResponse>} Resolves with success message or passes error to next middleware
*/
export const acknowledgeOooRequestController = async (
req: AcknowledgeOooRequest,
res: OooRequestResponse,
next: NextFunction
)
: Promise<OooRequestResponse> => {
try {

const requestBody = req.body;
const superUserId = req.userData.id;
const requestId = req.params.id;

const response = await acknowledgeOooRequestService(requestId, requestBody, superUserId);

return res.status(200).json({
message: response.message,
});
}
catch(error){
logger.error(ERROR_WHILE_ACKNOWLEDGING_REQUEST, error);
next(error);
return;
}
};
11 changes: 7 additions & 4 deletions controllers/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 { acknowledgeOooRequestController, 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";
Expand All @@ -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,
Expand Down Expand Up @@ -121,9 +121,12 @@ export const getRequestsController = async (req: any, res: any) => {
* @param {CustomResponse} res - The response object.
* @returns {Promise<void>} 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 acknowledgeOooRequestController(req as AcknowledgeOooRequest, res as OooRequestResponse, next);
break;
case REQUEST_TYPE.ONBOARDING:
await updateOnboardingExtensionRequestController(req as UpdateOnboardingExtensionRequest, res as OnboardingExtensionResponse);
break;
Expand Down
30 changes: 30 additions & 0 deletions middlewares/oooRoleCheckMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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");

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
return devFlagMiddleware(req, res, (err) => {
if (err) return next(err);

try {
return authorizeRoles([SUPERUSER])(req, res, next);
} catch (authErr) {
return next(authErr);
}
});
}

return next();
};
66 changes: 64 additions & 2 deletions middlewares/validators/oooRequests.ts
Original file line number Diff line number Diff line change
@@ -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 { REQUEST_STATE, REQUEST_TYPE, ERROR_WHILE_ACKNOWLEDGING_REQUEST } from "../../constants/requests";
import { AcknowledgeOooRequest, OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest";

export const createOooStatusRequestValidator = async (
req: OooRequestCreateRequest,
Expand Down Expand Up @@ -38,3 +38,65 @@ export const createOooStatusRequestValidator = async (

await schema.validateAsync(req.body, { abortEarly: false });
};

const acknowledgeOooRequestSchema = 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"
})
});

const paramsSchema = joi
.object()
.strict()
.keys({
id: joi.string().trim().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.
*
* @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<void>} Resolves or sends errors.
*/
export const acknowledgeOooRequestValidator = async (
req: AcknowledgeOooRequest,
res: OooRequestResponse,
next: NextFunction
): Promise<void> => {
try {
await acknowledgeOooRequestSchema.validateAsync(req.body, { abortEarly: false });
await paramsSchema.validateAsync(req.params, { abortEarly: false });
return next();
} catch (error: unknown) {

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 errorMessage = error instanceof Error ? error.message : String(error);
logger.error(`${ERROR_WHILE_ACKNOWLEDGING_REQUEST}: ${errorMessage}`);
return res.boom.badRequest([ERROR_WHILE_ACKNOWLEDGING_REQUEST]);
}
};
17 changes: 12 additions & 5 deletions middlewares/validators/requests.ts
Original file line number Diff line number Diff line change
@@ -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 { acknowledgeOooRequestValidator, createOooStatusRequestValidator } from "./oooRequests";
import { createExtensionRequestValidator } from "./extensionRequestsv2";
import {createTaskRequestValidator} from "./taskRequests";
import { ExtensionRequestRequest, ExtensionRequestResponse } from "../../types/extensionRequests";
Expand Down Expand Up @@ -88,7 +88,11 @@ 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
.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(),
Expand Down Expand Up @@ -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<void>} Resolves or sends errors.
*/
export const updateRequestValidator = async (
req: UpdateOnboardingExtensionRequest,
req: UpdateOnboardingExtensionRequest | AcknowledgeOooRequest,
res: CustomResponse,
next: NextFunction
): Promise<void> => {
Expand All @@ -142,6 +146,9 @@ export const updateRequestValidator = async (
req,
res as OnboardingExtensionResponse, next);
break;
case REQUEST_TYPE.OOO:
await acknowledgeOooRequestValidator(req as AcknowledgeOooRequest, res as OooRequestResponse, next);
break;
default:
return res.boom.badRequest("Invalid type");
}
Expand Down
3 changes: 1 addition & 2 deletions models/requests.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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,
Expand Down
3 changes: 2 additions & 1 deletion routes/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const router = express.Router();
const authorizeRoles = require("../middlewares/authorizeRoles");
const { SUPERUSER } = require("../constants/roles");
import authenticate from "../middlewares/authenticate";
import { oooRoleCheckMiddleware } from "../middlewares/oooRoleCheckMiddleware";
import {
createRequestsMiddleware,
updateRequestsMiddleware,
Expand All @@ -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, oooRoleCheckMiddleware, updateRequestValidator, updateRequestBeforeAcknowledgedController);
module.exports = router;

Loading
Loading