Skip to content

Commit 1e0668c

Browse files
committed
feat: acknowledge OOO request
1 parent a7812ee commit 1e0668c

File tree

7 files changed

+263
-28
lines changed

7 files changed

+263
-28
lines changed

constants/requests.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ export const REQUEST_LOG_TYPE = {
2525
REQUEST_BLOCKED: "REQUEST_BLOCKED",
2626
REQUEST_CANCELLED: "REQUEST_CANCELLED",
2727
REQUEST_UPDATED: "REQUEST_UPDATED",
28+
REQUEST_ALREADY_APPROVED: "REQUEST_ALREADY_APPROVED",
29+
REQUEST_ALREADY_REJECTED: "REQUEST_ALREADY_REJECTED",
2830
};
2931

3032
export const REQUEST_CREATED_SUCCESSFULLY = "Request created successfully";
@@ -41,6 +43,7 @@ export const ERROR_WHILE_UPDATING_REQUEST = "Error while updating request";
4143

4244
export const REQUEST_DOES_NOT_EXIST = "Request does not exist";
4345
export const REQUEST_ALREADY_PENDING = "Request already exists please wait for approval or rejection";
46+
export const UNAUTHORIZED_TO_ACKNOWLEDGE_OOO_REQUEST = "Only super users are allowed to acknowledge OOO requests";
4447

4548
export const TASK_REQUEST_MESSAGES = {
4649
NOT_AUTHORIZED_TO_CREATE_REQUEST: "Not authorized to create the request",

controllers/oooRequests.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { NextFunction } from "express";
12
import {
23
REQUEST_LOG_TYPE,
34
LOG_ACTION,
@@ -9,15 +10,19 @@ import {
910
ERROR_WHILE_UPDATING_REQUEST,
1011
REQUEST_APPROVED_SUCCESSFULLY,
1112
REQUEST_REJECTED_SUCCESSFULLY,
13+
UNAUTHORIZED_TO_ACKNOWLEDGE_OOO_REQUEST,
1214
} from "../constants/requests";
1315
import { statusState } from "../constants/userStatus";
1416
import { addLog } from "../models/logs";
1517
import { createRequest, getRequestByKeyValues, getRequests, updateRequest } from "../models/requests";
1618
import { createUserFutureStatus } from "../models/userFutureStatus";
1719
import { addFutureStatus } from "../models/userStatus";
20+
import { acknowledgeOOORequest } from "../services/oooRequest";
1821
import { CustomResponse } from "../typeDefinitions/global";
19-
import { OooRequestCreateRequest, OooStatusRequest } from "../types/oooRequest";
22+
import { AcknowledgeOOORequest, OooRequestCreateRequest, OooRequestResponse, OooStatusRequest } from "../types/oooRequest";
2023
import { UpdateRequest } from "../types/requests";
24+
import firestore from "../utils/firestore";
25+
const requestModel = firestore.collection("requests");
2126

2227
export const createOooRequestController = async (req: OooRequestCreateRequest, res: CustomResponse) => {
2328
const requestBody = req.body;
@@ -120,3 +125,37 @@ export const updateOooRequestController = async (req: UpdateRequest, res: Custom
120125
return res.boom.badImplementation(ERROR_WHILE_UPDATING_REQUEST);
121126
}
122127
};
128+
129+
export const acknowledgeOOORequestController = async (
130+
req: AcknowledgeOOORequest,
131+
res: OooRequestResponse,
132+
next: NextFunction,
133+
)
134+
: Promise<OooRequestResponse> => {
135+
136+
const dev = req.query.dev === "true";
137+
138+
if(!dev) return res.boom.notImplemented("Feature not implemented");
139+
140+
const requestBody = req.body;
141+
const userId = req?.userData?.id;
142+
const requestId = req.params.id;
143+
const isSuperuser = req?.userData?.roles?.super_user === true;
144+
145+
if (isSuperuser === false) {
146+
return res.boom.unauthorized(UNAUTHORIZED_TO_ACKNOWLEDGE_OOO_REQUEST);
147+
}
148+
149+
try {
150+
151+
const response = acknowledgeOOORequest(requestId, requestBody, userId);
152+
153+
return res.status(201).json({
154+
message: (await response).message,
155+
});
156+
}
157+
catch(error){
158+
logger.error(ERROR_WHILE_UPDATING_REQUEST, error);
159+
next(error);
160+
}
161+
};

controllers/requests.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import {
55
} from "../constants/requests";
66
import { getRequests } from "../models/requests";
77
import { getPaginatedLink } from "../utils/helper";
8-
import { createOooRequestController, updateOooRequestController } from "./oooRequests";
9-
import { OooRequestCreateRequest, OooRequestResponse } from "../types/oooRequest";
8+
import { acknowledgeOOORequestController, createOooRequestController, updateOooRequestController } from "./oooRequests";
9+
import { AcknowledgeOOORequest, OooRequestCreateRequest, OooRequestResponse } from "../types/oooRequest";
1010
import { CustomResponse } from "../typeDefinitions/global";
1111
import { ExtensionRequestRequest, ExtensionRequestResponse } from "../types/extensionRequests";
1212
import { createTaskExtensionRequest, updateTaskExtensionRequest } from "./extensionRequestsv2";
@@ -16,8 +16,7 @@ import { createTaskRequestController } from "./taskRequestsv2";
1616
import { OnboardingExtensionCreateRequest, OnboardingExtensionResponse, UpdateOnboardingExtensionStateRequest } from "../types/onboardingExtension";
1717
import { createOnboardingExtensionRequestController, updateOnboardingExtensionRequestController, updateOnboardingExtensionRequestState } from "./onboardingExtension";
1818
import { UpdateOnboardingExtensionRequest } from "../types/onboardingExtension";
19-
20-
import { Request } from "express";
19+
import { NextFunction, Request } from "express";
2120

2221
export const createRequestController = async (
2322
req: OooRequestCreateRequest | ExtensionRequestRequest | TaskRequestRequest | OnboardingExtensionCreateRequest,
@@ -121,8 +120,14 @@ export const getRequestsController = async (req: any, res: any) => {
121120
* @param {CustomResponse} res - The response object.
122121
* @returns {Promise<void>} Resolves or sends an error for invalid types.
123122
*/
124-
export const updateRequestBeforeAcknowledgedController = async (req: Request, res: CustomResponse) => {
123+
export const updateRequestBeforeAcknowledgedController = async (req: Request, res: CustomResponse, next: NextFunction) => {
125124
const type = req.body.type;
125+
126+
if (type === undefined) {
127+
await acknowledgeOOORequestController(req as AcknowledgeOOORequest, res as OooRequestResponse, next);
128+
return;
129+
}
130+
126131
switch(type){
127132
case REQUEST_TYPE.ONBOARDING:
128133
await updateOnboardingExtensionRequestController(req as UpdateOnboardingExtensionRequest, res as OnboardingExtensionResponse);

middlewares/validators/oooRequests.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import joi from "joi";
22
import { NextFunction } from "express";
33
import { REQUEST_STATE, REQUEST_TYPE } from "../../constants/requests";
4-
import { OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest";
4+
import { AcknowledgeOOORequest, OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest";
55

66
export const createOooStatusRequestValidator = async (
77
req: OooRequestCreateRequest,
@@ -38,3 +38,35 @@ export const createOooStatusRequestValidator = async (
3838

3939
await schema.validateAsync(req.body, { abortEarly: false });
4040
};
41+
42+
export const acknowledgeOOORequestsValidator = async (
43+
req: AcknowledgeOOORequest,
44+
res: OooRequestResponse,
45+
next: NextFunction
46+
) => {
47+
const schema = joi
48+
.object()
49+
.strict()
50+
.keys({
51+
comment: joi.string().optional()
52+
.messages({
53+
"string.empty": "comment cannot be empty",
54+
}),
55+
status: joi
56+
.string()
57+
.valid(REQUEST_STATE.APPROVED, REQUEST_STATE.REJECTED)
58+
.required()
59+
.messages({
60+
"any.only": "status must be APPROVED or REJECTED",
61+
})
62+
});
63+
64+
try {
65+
await schema.validateAsync(req.body, { abortEarly: false });
66+
next();
67+
} catch (error) {
68+
const errorMessages = error.details.map((detail:any) => detail.message);
69+
logger.error(`Error while validating request payload : ${errorMessages}`);
70+
res.boom.badRequest(errorMessages);
71+
}
72+
};

middlewares/validators/requests.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import joi from "joi";
22
import { NextFunction } from "express";
33
import { REQUEST_STATE, REQUEST_TYPE } from "../../constants/requests";
4-
import { OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest";
5-
import { createOooStatusRequestValidator } from "./oooRequests";
4+
import { AcknowledgeOOORequest, OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest";
5+
import { acknowledgeOOORequestsValidator, createOooStatusRequestValidator } from "./oooRequests";
66
import { createExtensionRequestValidator } from "./extensionRequestsv2";
77
import {createTaskRequestValidator} from "./taskRequests";
88
import { ExtensionRequestRequest, ExtensionRequestResponse } from "../../types/extensionRequests";
@@ -131,11 +131,17 @@ export const getRequestsMiddleware = async (req: OooRequestCreateRequest, res: O
131131
* @returns {Promise<void>} Resolves or sends errors.
132132
*/
133133
export const updateRequestValidator = async (
134-
req: UpdateOnboardingExtensionRequest,
134+
req: UpdateOnboardingExtensionRequest | AcknowledgeOOORequest,
135135
res: CustomResponse,
136136
next: NextFunction
137137
): Promise<void> => {
138138
const type = req.body.type;
139+
140+
if (type === undefined) {
141+
await acknowledgeOOORequestsValidator(req, res as OooRequestResponse, next);
142+
return;
143+
}
144+
139145
switch (type) {
140146
case REQUEST_TYPE.ONBOARDING:
141147
await updateOnboardingExtensionRequestValidator(
@@ -145,4 +151,4 @@ export const updateRequestValidator = async (
145151
default:
146152
return res.boom.badRequest("Invalid type");
147153
}
148-
};
154+
};

services/oooRequest.ts

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import { addLog } from "./logService";
2+
import firestore from "../utils/firestore";
3+
import { NotFound, BadRequest } from "http-errors";
4+
import { logType } from "../constants/logs";
5+
import {
6+
REQUEST_STATE,
7+
REQUEST_TYPE,
8+
REQUEST_DOES_NOT_EXIST,
9+
REQUEST_ALREADY_APPROVED,
10+
REQUEST_ALREADY_REJECTED,
11+
REQUEST_LOG_TYPE,
12+
REQUEST_APPROVED_SUCCESSFULLY,
13+
REQUEST_REJECTED_SUCCESSFULLY,
14+
LOG_ACTION,
15+
INVALID_REQUEST_TYPE,
16+
} from "../constants/requests";
17+
import { getRequests, updateRequest } from "../models/requests";
18+
import { AcknowledgeOOORequestBody } from "../types/oooRequest";
19+
import { statusState } from "../constants/userStatus";
20+
import { createUserFutureStatus } from "../models/userFutureStatus";
21+
import { addFutureStatus } from "../models/userStatus";
22+
const requestModel = firestore.collection("requests");
23+
24+
export const validateOOOAcknowledgeRequest = async (
25+
requestId: string,
26+
requestType: string,
27+
requestStatus: string,
28+
) => {
29+
30+
try {
31+
32+
if (requestType !== REQUEST_TYPE.OOO) {
33+
await addLog(logType.INVALID_REQUEST_TYPE,
34+
{ requestId, type: requestType },
35+
{ message: INVALID_REQUEST_TYPE }
36+
);
37+
throw BadRequest(INVALID_REQUEST_TYPE);
38+
}
39+
40+
if (requestStatus === REQUEST_STATE.APPROVED) {
41+
await addLog(logType.REQUEST_ALREADY_APPROVED,
42+
{ oooRequestId: requestId },
43+
{ message: REQUEST_ALREADY_APPROVED }
44+
);
45+
throw BadRequest(REQUEST_ALREADY_APPROVED);
46+
}
47+
48+
if (requestStatus === REQUEST_STATE.REJECTED) {
49+
await addLog(logType.REQUEST_ALREADY_REJECTED,
50+
{ oooRequestId: requestId },
51+
{ message: REQUEST_ALREADY_REJECTED }
52+
);
53+
throw BadRequest(REQUEST_ALREADY_REJECTED);
54+
}
55+
} catch (error) {
56+
logger.error("Error while validating OOO acknowledge request", error);
57+
throw error;
58+
}
59+
}
60+
61+
export const acknowledgeOOORequest = async (
62+
requestId: string,
63+
body: AcknowledgeOOORequestBody,
64+
userId: string,
65+
) => {
66+
try {
67+
const request = await requestModel.doc(requestId).get();
68+
69+
if (!request.exists) {
70+
await addLog(logType.REQUEST_DOES_NOT_EXIST,
71+
{ oooRequestId: requestId },
72+
{ message: REQUEST_DOES_NOT_EXIST }
73+
);
74+
throw NotFound(REQUEST_DOES_NOT_EXIST);
75+
}
76+
77+
const requestData = request.data();
78+
79+
await validateOOOAcknowledgeRequest(requestId, requestData.type, requestData.status);
80+
81+
const requestResult = await updateRequest(requestId, body, userId, REQUEST_TYPE.OOO);
82+
83+
if ("error" in requestResult) {
84+
throw BadRequest(requestResult.error);
85+
}
86+
87+
const [acknowledgeLogType, returnMessage] =
88+
requestResult.status === REQUEST_STATE.APPROVED
89+
? [REQUEST_LOG_TYPE.REQUEST_APPROVED, REQUEST_APPROVED_SUCCESSFULLY]
90+
: [REQUEST_LOG_TYPE.REQUEST_REJECTED, REQUEST_REJECTED_SUCCESSFULLY];
91+
92+
const requestLog = {
93+
type: acknowledgeLogType,
94+
meta: {
95+
requestId: requestId,
96+
action: LOG_ACTION.UPDATE,
97+
userId: userId,
98+
createdAt: Date.now(),
99+
},
100+
body: requestResult,
101+
};
102+
103+
await addLog(requestLog.type, requestLog.meta, requestLog.body);
104+
105+
if (requestResult.status === REQUEST_STATE.APPROVED) {
106+
const requestData = await getRequests({ id: requestId });
107+
108+
if (requestData) {
109+
const { from, until, requestedBy, comment } = requestData as any;
110+
const userFutureStatusData = {
111+
requestId,
112+
status: REQUEST_TYPE.OOO,
113+
state: statusState.UPCOMING,
114+
from,
115+
endsOn: until,
116+
userId: requestedBy,
117+
message: comment,
118+
};
119+
await createUserFutureStatus(userFutureStatusData);
120+
await addFutureStatus(userFutureStatusData);
121+
}
122+
}
123+
124+
return {
125+
message: returnMessage,
126+
data: {
127+
id: requestResult.id,
128+
...requestResult,
129+
},
130+
};
131+
} catch (error) {
132+
logger.error("Error while acknowledging OOO request", error);
133+
throw error;
134+
}
135+
}

0 commit comments

Comments
 (0)