Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 4 additions & 0 deletions constants/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated
};
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated

export const REQUEST_CREATED_SUCCESSFULLY = "Request created successfully";
Expand All @@ -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";
Comment thread
RishiChaubey31 marked this conversation as resolved.

export const REQUEST_ID_REQUIRED = "Request id is required";
Comment thread
RishiChaubey31 marked this conversation as resolved.
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
51 changes: 49 additions & 2 deletions controllers/oooRequests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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";
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated
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 +83,7 @@ export const createOooRequestController = async (
return res.boom.conflict(REQUEST_ALREADY_PENDING);
}

await createOooRequest(requestBody, username, userId);
await createOooRequest(requestBody, userId);
Comment thread
RishiChaubey31 marked this conversation as resolved.

return res.status(201).json({
message: REQUEST_CREATED_SUCCESSFULLY,
Expand Down Expand Up @@ -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<OooRequestResponse>} Resolves with success or failure.
*/
Comment thread
RishiChaubey31 marked this conversation as resolved.
export const acknowledgeOooRequest = async (
req: AcknowledgeOooRequest,
res: OooRequestResponse,
next: NextFunction
)
: Promise<OooRequestResponse> => {
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);
}
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated

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

if (!requestId) {
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated
return res.boom.badRequest(REQUEST_ID_REQUIRED);
}

const response = await oooRequestService.acknowledgeOooRequest(requestId, requestBody, superUserId);

return res.status(200).json({
message: response.message,
});
}
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated
catch(error){
logger.error(ERROR_WHILE_ACKNOWLEDGING_REQUEST, error);
next(error);
return res;
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated
}
Comment thread
RishiChaubey31 marked this conversation as resolved.
Comment thread
RishiChaubey31 marked this conversation as resolved.
};
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 { 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";
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 acknowledgeOooRequest(req as AcknowledgeOooRequest, res as OooRequestResponse, next);
break;
case REQUEST_TYPE.ONBOARDING:
await updateOnboardingExtensionRequestController(req as UpdateOnboardingExtensionRequest, res as OnboardingExtensionResponse);
break;
Expand Down
32 changes: 32 additions & 0 deletions middlewares/conditionalOooChecks.ts
Original file line number Diff line number Diff line change
@@ -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");
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated

/**
* 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);
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated


Comment thread
MayankBansal12 marked this conversation as resolved.
Outdated
authorizeRoles([SUPERUSER])(req, res, next);
});
} else {

next();
}
};
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated
46 changes: 45 additions & 1 deletion 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 { AcknowledgeOooRequest, OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest";

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

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

const schema = joi
Comment thread
MayankBansal12 marked this conversation as resolved.
Outdated
.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"
})
});
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated

/**
* 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 acknowledgeOooRequest = async (
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated
req: AcknowledgeOooRequest,
res: OooRequestResponse,
next: NextFunction
Comment thread
iamitprakash marked this conversation as resolved.
): Promise<void> => {
try {
await schema.validateAsync(req.body, { abortEarly: false });
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated
next();
} catch (error) {
const errorMessages = error.details.map((detail) => detail.message);
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated
Comment thread
iamitprakash marked this conversation as resolved.
Outdated
logger.error(`Error while validating request payload : ${errorMessages}`);
Comment thread
MayankBansal12 marked this conversation as resolved.
Outdated
return res.boom.badRequest(errorMessages);
}
Comment thread
MayankBansal12 marked this conversation as resolved.
};
15 changes: 11 additions & 4 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 { acknowledgeOooRequest, createOooStatusRequestValidator } from "./oooRequests";
import { createExtensionRequestValidator } from "./extensionRequestsv2";
import {createTaskRequestValidator} from "./taskRequests";
import { ExtensionRequestRequest, ExtensionRequestResponse } from "../../types/extensionRequests";
Expand Down Expand Up @@ -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(),
Comment thread
RishiChaubey31 marked this conversation as resolved.
page: joi.number().integer().min(0).when("next", {
Comment thread
RishiChaubey31 marked this conversation as resolved.
is: joi.exist(),
then: joi.forbidden().messages({
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 acknowledgeOooRequest(req, res as OooRequestResponse, next);
break;
default:
return res.boom.badRequest("Invalid type");
}
Expand Down
17 changes: 16 additions & 1 deletion models/requests.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
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";
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated
import {
ERROR_WHILE_FETCHING_REQUEST,
ERROR_WHILE_CREATING_REQUEST,
ERROR_WHILE_UPDATING_REQUEST,
REQUEST_DOES_NOT_EXIST,
} from "../constants/requests";
import { getUserId } from "../utils/users";
import {NotFound} from "http-errors"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • where we are using this ?

import {fetchUser} from "./users"
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated
const SIZE = 5;

export const createRequest = async (body: any) => {
Expand Down Expand Up @@ -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;
}
}
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated

export const getRequests = async (query: any) => {
let { id, type, requestedBy, state, prev, next, page, size = SIZE } = query;
const dev = query.dev === "true";
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 { conditionalOooChecks } from "../middlewares/conditionalOooChecks";
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, conditionalOooChecks, updateRequestValidator, updateRequestBeforeAcknowledgedController);
Comment thread
RishiChaubey31 marked this conversation as resolved.
Outdated
Comment thread
MayankBansal12 marked this conversation as resolved.
Outdated
module.exports = router;

Loading
Loading