Skip to content

Commit

Permalink
Implement PUT Method for Extension Requests in /request Endpoint (#…
Browse files Browse the repository at this point in the history
…2037)

* refactor: middleware and added types for update extension request

* fix: made some changes in models function

* controller: updated cotroller for approved or reject all types of request

* test: added test for approve or reject extension request
  • Loading branch information
sahsisunny committed May 11, 2024
1 parent ba68f7d commit 4906fbb
Show file tree
Hide file tree
Showing 11 changed files with 379 additions and 171 deletions.
68 changes: 61 additions & 7 deletions controllers/extensionRequestsv2.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { getRequestByKeyValues } from "../models/requests";
import { LOG_ACTION, REQUEST_LOG_TYPE, REQUEST_STATE, REQUEST_TYPE } from "../constants/requests";
import { getRequestByKeyValues, updateRequest } from "../models/requests";
import {
ERROR_WHILE_CREATING_REQUEST,
ERROR_WHILE_UPDATING_REQUEST,
LOG_ACTION,
REQUEST_APPROVED_SUCCESSFULLY,
REQUEST_LOG_TYPE,
REQUEST_REJECTED_SUCCESSFULLY,
REQUEST_STATE,
REQUEST_TYPE,
} from "../constants/requests";
import { addLog } from "../models/logs";
import { createRequest } from "../models/requests";
import { fetchTask } from "../models/tasks";
Expand Down Expand Up @@ -52,15 +61,16 @@ export const createTaskExtensionRequest = async (req: ExtensionRequestRequest, r

const latestExtensionRequest: ExtensionRequest | undefined = await getRequestByKeyValues({
taskId,
state: REQUEST_STATE.PENDING,
type: REQUEST_TYPE.EXTENSION,
});

if (latestExtensionRequest && latestExtensionRequest.state === REQUEST_STATE.PENDING) {
return res.boom.badRequest("An extension request for this task already exists.");
}

let requestNumber: number = latestExtensionRequest?.requestedBy === requestedBy && latestExtensionRequest.requestNumber ? latestExtensionRequest.requestNumber + 1 : 1;
const requestNumber: number =
latestExtensionRequest?.requestedBy === requestedBy && latestExtensionRequest.requestNumber
? latestExtensionRequest.requestNumber + 1
: 1;
extensionBody = { ...extensionBody, requestNumber };

const extensionRequest = await createRequest(extensionBody);
Expand All @@ -87,7 +97,51 @@ export const createTaskExtensionRequest = async (req: ExtensionRequestRequest, r
extensionRequest: { ...extensionBody, id: extensionRequest.id },
});
} catch (err) {
logger.error(`Error while creating new extension request: ${err}`);
return res.boom.badImplementation('Internal Server Error');
logger.error(ERROR_WHILE_CREATING_REQUEST, err);
return res.boom.badImplementation(ERROR_WHILE_CREATING_REQUEST);
}
};

export const updateTaskExtensionRequest = async (req: any, res: any) => {
const requestBody = req.body;
const userId = req?.userData?.id;
const requestId = req.params.id;

if (!userId) {
return res.boom.unauthorized();
}

try {
const requestResult = await updateRequest(requestId, requestBody, userId,REQUEST_TYPE.EXTENSION)
if ("error" in requestResult) {
return res.boom.badRequest(requestResult.error);
}
const [logType, returnMessage] =
requestResult.state === REQUEST_STATE.APPROVED
? [REQUEST_LOG_TYPE.REQUEST_APPROVED, REQUEST_APPROVED_SUCCESSFULLY]
: [REQUEST_LOG_TYPE.REQUEST_REJECTED, REQUEST_REJECTED_SUCCESSFULLY];

const requestLog = {
type: logType,
meta: {
requestId: requestId,
action: LOG_ACTION.UPDATE,
createdBy: userId,
createdAt: Date.now(),
},
body: requestResult,
};
await addLog(requestLog.type, requestLog.meta, requestLog.body);

return res.status(201).json({
message: returnMessage,
data: {
id: requestResult.id,
...requestResult,
},
});
} catch (err) {
logger.error(ERROR_WHILE_UPDATING_REQUEST, err);
return res.boom.badImplementation(ERROR_WHILE_UPDATING_REQUEST);
}
};
69 changes: 68 additions & 1 deletion controllers/oooRequests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@ import {
REQUEST_ALREADY_PENDING,
REQUEST_STATE,
REQUEST_TYPE,
ERROR_WHILE_UPDATING_REQUEST,
REQUEST_APPROVED_SUCCESSFULLY,
REQUEST_REJECTED_SUCCESSFULLY,
} from "../constants/requests";
import { statusState } from "../constants/userStatus";
import { addLog } from "../models/logs";
import { createRequest, getRequestByKeyValues } from "../models/requests";
import { createRequest, getRequestByKeyValues, getRequests, updateRequest } from "../models/requests";
import { createUserFutureStatus } from "../models/userFutureStatus";
import { addFutureStatus } from "../models/userStatus";
import { CustomResponse } from "../typeDefinitions/global";
import { OooRequestCreateRequest, OooStatusRequest } from "../types/oooRequest";
import { UpdateRequest } from "../types/requests";

export const createOooRequestController = async (req: OooRequestCreateRequest, res: CustomResponse) => {
const requestBody = req.body;
Expand Down Expand Up @@ -53,3 +60,63 @@ export const createOooRequestController = async (req: OooRequestCreateRequest, r
return res.boom.badImplementation(ERROR_WHILE_CREATING_REQUEST);
}
};

export const updateOooRequestController = async (req: UpdateRequest, res: CustomResponse) => {
const requestBody = req.body;
const userId = req?.userData?.id;
const requestId = req.params.id;
if (!userId) {
return res.boom.unauthorized();
}

try {
const requestResult = await updateRequest(requestId, requestBody, userId, REQUEST_TYPE.OOO);
if ("error" in requestResult) {
return res.boom.badRequest(requestResult.error);
}
const [logType, returnMessage] =
requestResult.state === REQUEST_STATE.APPROVED
? [REQUEST_LOG_TYPE.REQUEST_APPROVED, REQUEST_APPROVED_SUCCESSFULLY]
: [REQUEST_LOG_TYPE.REQUEST_REJECTED, REQUEST_REJECTED_SUCCESSFULLY];

const requestLog = {
type: logType,
meta: {
requestId: requestId,
action: LOG_ACTION.UPDATE,
createdBy: userId,
createdAt: Date.now(),
},
body: requestResult,
};
await addLog(requestLog.type, requestLog.meta, requestLog.body);
if (requestResult.state === REQUEST_STATE.APPROVED) {
const requestData = await getRequests({ id: requestId });

if (requestData) {
const { from, until, requestedBy, message } = requestData as any;
const userFutureStatusData = {
requestId,
status: REQUEST_TYPE.OOO,
state: statusState.UPCOMING,
from,
endsOn: until,
userId: requestedBy,
message,
};
await createUserFutureStatus(userFutureStatusData);
await addFutureStatus(userFutureStatusData);
}
}
return res.status(201).json({
message: returnMessage,
data: {
id: requestResult.id,
...requestResult,
},
});
} catch (err) {
logger.error(ERROR_WHILE_UPDATING_REQUEST, err);
return res.boom.badImplementation(ERROR_WHILE_UPDATING_REQUEST);
}
};
87 changes: 15 additions & 72 deletions controllers/requests.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,21 @@
import {
ERROR_WHILE_FETCHING_REQUEST,
ERROR_WHILE_UPDATING_REQUEST,
REQUEST_REJECTED_SUCCESSFULLY,
REQUEST_APPROVED_SUCCESSFULLY,
REQUEST_FETCHED_SUCCESSFULLY,
REQUEST_STATE,
LOG_ACTION,
REQUEST_LOG_TYPE,
REQUEST_TYPE,
} from "../constants/requests";
import { statusState } from "../constants/userStatus";
import { addFutureStatus } from "../models/userStatus";
import { createUserFutureStatus } from "../models/userFutureStatus";
import { getRequests, updateRequest } from "../models/requests";
import { addLog } from "../models/logs";
import { getRequests } from "../models/requests";
import { getPaginatedLink } from "../utils/helper";
import { createOooRequestController } from "./oooRequests";
import { createOooRequestController, updateOooRequestController } from "./oooRequests";
import { OooRequestCreateRequest, OooRequestResponse } from "../types/oooRequest";
import { CustomResponse } from "../typeDefinitions/global";
import { ExtensionRequestRequest, ExtensionRequestResponse } from "../types/extensionRequests";
import { createTaskExtensionRequest } from "./extensionRequestsv2";
import { createTaskExtensionRequest, updateTaskExtensionRequest } from "./extensionRequestsv2";
import { UpdateRequest } from "../types/requests";

export const createRequestController = async (
req: OooRequestCreateRequest | ExtensionRequestRequest,
res: CustomResponse) => {

res: CustomResponse
) => {
const type = req.body.type;
switch (type) {
case REQUEST_TYPE.OOO:
Expand All @@ -36,63 +27,15 @@ export const createRequestController = async (
}
};

export const updateRequestController = async (req: any, res: any) => {
const requestBody = req.body;
const userId = req?.userData?.id;
const requestId = req.params.id;
if (!userId) {
return res.boom.unauthorized();
}

try {
const requestResult = await updateRequest(requestId, requestBody, userId);
if ("error" in requestResult) {
return res.boom.badRequest(requestResult.error);
}
const [logType, returnMessage] =
requestResult.state === REQUEST_STATE.APPROVED
? [REQUEST_LOG_TYPE.REQUEST_APPROVED, REQUEST_APPROVED_SUCCESSFULLY]
: [REQUEST_LOG_TYPE.REQUEST_REJECTED, REQUEST_REJECTED_SUCCESSFULLY];

const requestLog = {
type: logType,
meta: {
requestId: requestId,
action: LOG_ACTION.UPDATE,
createdBy: userId,
createdAt: Date.now(),
},
body: requestResult,
};
await addLog(requestLog.type, requestLog.meta, requestLog.body);
if (requestResult.state === REQUEST_STATE.APPROVED) {
const requestData = await getRequests({ id: requestId });

if (requestData) {
const { from, until, requestedBy, message } = requestData as any;
const userFutureStatusData = {
requestId,
status: REQUEST_TYPE.OOO,
state: statusState.UPCOMING,
from,
endsOn: until,
userId: requestedBy,
message,
};
await createUserFutureStatus(userFutureStatusData);
await addFutureStatus(userFutureStatusData);
}
}
return res.status(201).json({
message: returnMessage,
data: {
id: requestResult.id,
...requestResult,
},
});
} catch (err) {
logger.error(ERROR_WHILE_UPDATING_REQUEST, err);
return res.boom.badImplementation(ERROR_WHILE_UPDATING_REQUEST);
export const updateRequestController = async (req: UpdateRequest, res: CustomResponse) => {
const type = req.body.type;
switch (type) {
case REQUEST_TYPE.OOO:
return await updateOooRequestController(req as UpdateRequest, res as ExtensionRequestResponse);
case REQUEST_TYPE.EXTENSION:
return await updateTaskExtensionRequest(req as UpdateRequest, res as ExtensionRequestResponse);
default:
return res.boom.badRequest("Invalid request type");
}
};

Expand Down
25 changes: 1 addition & 24 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,OooRequestUpdateRequest } from "../../types/oooRequest";
import { OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest";

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

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


export const updateOooStatusRequestValidator = async (
req: OooRequestUpdateRequest,

) => {
const schema = joi
.object()
.strict()
.keys({
reason: joi.string().optional(),
state: joi
.string()
.valid(REQUEST_STATE.APPROVED, REQUEST_STATE.REJECTED)
.required()
.messages({
"any.only": "state must be APPROVED or REJECTED",
}),
type: joi.string().valid(REQUEST_TYPE.OOO).required(),
});

await schema.validateAsync(req.body, { abortEarly: false });
};
39 changes: 24 additions & 15 deletions middlewares/validators/requests.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import joi from "joi";
import { NextFunction } from "express";
import { REQUEST_STATE, REQUEST_TYPE } from "../../constants/requests";
import { OooRequestCreateRequest, OooRequestResponse, OooRequestUpdateRequest } from "../../types/oooRequest";
import { createOooStatusRequestValidator, updateOooStatusRequestValidator } from "./oooRequests";
import { OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest";
import { createOooStatusRequestValidator } from "./oooRequests";
import { createExtensionRequestValidator } from "./extensionRequestsv2";
import { ExtensionRequestRequest, ExtensionRequestResponse } from "../../types/extensionRequests";
import { CustomResponse } from "../../typeDefinitions/global";
import { UpdateRequest } from "../../types/requests";

export const createRequestsMiddleware = async (
req: OooRequestCreateRequest|ExtensionRequestRequest,
Expand Down Expand Up @@ -39,29 +40,37 @@ export const createRequestsMiddleware = async (
};

export const updateRequestsMiddleware = async (
req: OooRequestUpdateRequest,
res: OooRequestResponse,
req: UpdateRequest,
res: CustomResponse,
next: NextFunction
) => {
const type = req.body.type;

// TODO: Remove this check once feature is tested and ready to be used
if ( req.query.dev !== "true") {
return res.boom.badRequest("Please use feature flag to make this requests");
}
const schema = joi
.object()
.strict()
.keys({
reason: joi.string().optional()
.messages({
"string.empty": "reason cannot be empty",
}),
state: joi
.string()
.valid(REQUEST_STATE.APPROVED, REQUEST_STATE.REJECTED)
.required()
.messages({
"any.only": "state must be APPROVED or REJECTED",
}),
type: joi.string().valid(REQUEST_TYPE.OOO, REQUEST_TYPE.EXTENSION).required(),
});

try {
switch (type) {
case REQUEST_TYPE.OOO:
await updateOooStatusRequestValidator(req as OooRequestUpdateRequest);
break;
default:
res.boom.badRequest(`Invalid request type: ${type}`);
}

await schema.validateAsync(req.body, { abortEarly: false });
next();
} catch (error) {
const errorMessages = error.details.map((detail) => detail.message);
const errorMessages = error.details.map((detail:any) => detail.message);
logger.error(`Error while validating request payload : ${errorMessages}`);
res.boom.badRequest(errorMessages);
}
Expand Down
Loading

0 comments on commit 4906fbb

Please sign in to comment.