Skip to content

Commit

Permalink
Merge pull request #2034 from Real-Dev-Squad/develop
Browse files Browse the repository at this point in the history
Dev to main sync
  • Loading branch information
iamitprakash authored May 6, 2024
2 parents e68d742 + ba68f7d commit 4116605
Show file tree
Hide file tree
Showing 19 changed files with 646 additions and 86 deletions.
1 change: 1 addition & 0 deletions constants/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const LOG_ACTION = {

export const REQUEST_TYPE = {
OOO: "OOO",
EXTENSION: "EXTENSION",
ALL: "ALL",
};

Expand Down
93 changes: 93 additions & 0 deletions controllers/extensionRequestsv2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { getRequestByKeyValues } from "../models/requests";
import { LOG_ACTION, REQUEST_LOG_TYPE, REQUEST_STATE, REQUEST_TYPE } from "../constants/requests";
import { addLog } from "../models/logs";
import { createRequest } from "../models/requests";
import { fetchTask } from "../models/tasks";
import { CustomResponse } from "../typeDefinitions/global";
import { ExtensionRequest, ExtensionRequestCreateBody, ExtensionRequestRequest } from "../types/extensionRequests";
import { getUsernameElseUndefined } from "../utils/users";

export const createTaskExtensionRequest = async (req: ExtensionRequestRequest, res: CustomResponse) => {
try {
const { userData } = req;
const { id: requestedBy, roles } = userData || {};
const { body } = req;

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

const { taskId } = body;
let extensionBody: ExtensionRequestCreateBody = { ...body, requestedBy };
let assignee: string | undefined = undefined;

const { taskData: task } = await fetchTask(taskId);
if (!task) {
return res.boom.badRequest("Task Not Found");
}

const { assigneeId, endsOn } = task;
if (!assigneeId) {
return res.boom.badRequest("Assignee is not present for this task");
}

assignee = await getUsernameElseUndefined(assigneeId);
if (!assignee) {
return res.boom.badRequest("Assignee is not present for this task");
} else {
extensionBody = { ...extensionBody, assignee };
}

if (requestedBy !== assigneeId && !roles?.super_user) {
return res.boom.forbidden("Only assigned user and super user can create an extension request for this task.");
}

if (endsOn >= body.newEndsOn) {
return res.boom.badRequest("New ETA must be greater than Old ETA");
}

if (body.oldEndsOn != endsOn) {
return res.boom.badRequest("Old ETA does not match the task's ETA");
}

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;
extensionBody = { ...extensionBody, requestNumber };

const extensionRequest = await createRequest(extensionBody);
if ("error" in extensionRequest) {
return res.boom.badRequest(extensionRequest.error);
}

const extensionLog = {
type: REQUEST_LOG_TYPE.REQUEST_CREATED,
meta: {
taskId,
requestId: extensionRequest.id,
action: LOG_ACTION.CREATE,
createdBy: requestedBy,
createdAt: Date.now(),
},
body: extensionBody,
};

await addLog(extensionLog.type, extensionLog.meta, extensionLog.body);

return res.status(201).json({
message: "Extension Request created successfully!",
extensionRequest: { ...extensionBody, id: extensionRequest.id },
});
} catch (err) {
logger.error(`Error while creating new extension request: ${err}`);
return res.boom.badImplementation('Internal Server Error');
}
};
55 changes: 55 additions & 0 deletions controllers/oooRequests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {
REQUEST_LOG_TYPE,
LOG_ACTION,
REQUEST_CREATED_SUCCESSFULLY,
ERROR_WHILE_CREATING_REQUEST,
REQUEST_ALREADY_PENDING,
REQUEST_STATE,
REQUEST_TYPE,
} from "../constants/requests";
import { addLog } from "../models/logs";
import { createRequest, getRequestByKeyValues } from "../models/requests";
import { CustomResponse } from "../typeDefinitions/global";
import { OooRequestCreateRequest, OooStatusRequest } from "../types/oooRequest";

export const createOooRequestController = async (req: OooRequestCreateRequest, res: CustomResponse) => {
const requestBody = req.body;
const userId = req?.userData?.id;

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

try {
const latestOooRequest:OooStatusRequest = await getRequestByKeyValues({ requestedBy: userId, type: REQUEST_TYPE.OOO , state: REQUEST_STATE.PENDING });

if (latestOooRequest && latestOooRequest.state === REQUEST_STATE.PENDING) {
return res.boom.badRequest(REQUEST_ALREADY_PENDING);
}

const requestResult = await createRequest({ requestedBy: userId, ...requestBody });

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

return res.status(201).json({
message: REQUEST_CREATED_SUCCESSFULLY,
data: {
id: requestResult.id,
...requestResult,
},
});
} catch (err) {
logger.error(ERROR_WHILE_CREATING_REQUEST, err);
return res.boom.badImplementation(ERROR_WHILE_CREATING_REQUEST);
}
};
4 changes: 2 additions & 2 deletions controllers/progresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ const createProgress = async (req, res) => {
body: { type, completed, planned, blockers, taskId },
} = req;
try {
const data = await createProgressDocument({ ...req.body, userId: req.userData.id });
await sendTaskUpdate(completed, blockers, planned, req.userData.username, taskId);
const { data, taskTitle } = await createProgressDocument({ ...req.body, userId: req.userData.id });
await sendTaskUpdate(completed, blockers, planned, req.userData.username, taskId, taskTitle);
return res.status(201).json({
data,
message: `${type.charAt(0).toUpperCase() + type.slice(1)} ${PROGRESS_DOCUMENT_CREATED_SUCCEEDED}`,
Expand Down
71 changes: 19 additions & 52 deletions controllers/requests.ts
Original file line number Diff line number Diff line change
@@ -1,71 +1,38 @@
import {
ERROR_WHILE_FETCHING_REQUEST,
ERROR_WHILE_CREATING_REQUEST,
ERROR_WHILE_UPDATING_REQUEST,
REQUEST_REJECTED_SUCCESSFULLY,
REQUEST_APPROVED_SUCCESSFULLY,
REQUEST_FETCHED_SUCCESSFULLY,
REQUEST_CREATED_SUCCESSFULLY,
REQUEST_STATE,
LOG_ACTION,
REQUEST_LOG_TYPE,
REQUEST_TYPE,
} from "../constants/requests";
import { statusState } from "../constants/userStatus";
import {addFutureStatus} from "../models/userStatus";
import { addFutureStatus } from "../models/userStatus";
import { createUserFutureStatus } from "../models/userFutureStatus";
import { createRequest, getRequests, updateRequest } from "../models/requests";
import { getRequests, updateRequest } from "../models/requests";
import { addLog } from "../models/logs";
import { getPaginatedLink } from "../utils/helper";
import { createOooRequestController } from "./oooRequests";
import { OooRequestCreateRequest, OooRequestResponse } from "../types/oooRequest";
import { CustomResponse } from "../typeDefinitions/global";
import { ExtensionRequestRequest, ExtensionRequestResponse } from "../types/extensionRequests";
import { createTaskExtensionRequest } from "./extensionRequestsv2";

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

try {
const requestResult = await createRequest({ requestedBy: userId, ...requestBody });
if ("error" in requestResult) {
const requestLog = {
type: REQUEST_LOG_TYPE.REQUEST_BLOCKED,
meta: {
action: LOG_ACTION.ERRORS,
createdBy: userId,
createdAt: Date.now(),
},
body: {
error: requestResult.error,
...requestBody,
},
};
await addLog(requestLog.type, requestLog.meta, requestLog.body);
export const createRequestController = async (
req: OooRequestCreateRequest | ExtensionRequestRequest,
res: CustomResponse) => {

return res.boom.badRequest(requestResult.error);
} else {
const requestLog = {
type: REQUEST_LOG_TYPE.REQUEST_CREATED,
meta: {
requestId: requestResult.id,
action: LOG_ACTION.CREATE,
createdBy: userId,
createdAt: Date.now(),
},
body: requestResult,
};
await addLog(requestLog.type, requestLog.meta, requestLog.body);
return res.status(201).json({
message: REQUEST_CREATED_SUCCESSFULLY,
data: {
id: requestResult.id,
...requestResult,
},
});
}
} catch (err) {
logger.error(ERROR_WHILE_CREATING_REQUEST, err);
return res.boom.badImplementation(ERROR_WHILE_CREATING_REQUEST);
const type = req.body.type;
switch (type) {
case REQUEST_TYPE.OOO:
return await createOooRequestController(req as OooRequestCreateRequest, res as OooRequestResponse);
case REQUEST_TYPE.EXTENSION:
return await createTaskExtensionRequest(req as ExtensionRequestRequest, res as ExtensionRequestResponse);
default:
return res.boom.badRequest("Invalid request type");
}
};

Expand Down Expand Up @@ -142,7 +109,7 @@ export const getRequestsController = async (req: any, res: any) => {
return res.status(204).send();
}

if(page) {
if (page) {
const pageLink = `/requests?page=${page}&dev=${query.dev}`;
return res.status(200).json({
message: REQUEST_FETCHED_SUCCESSFULLY,
Expand Down
46 changes: 46 additions & 0 deletions middlewares/validators/extensionRequestsv2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import joi from "joi";
import { ExtensionRequestRequest, ExtensionRequestResponse } from "../../types/extensionRequests";
import { NextFunction } from "express";
import { REQUEST_TYPE,REQUEST_STATE } from "../../constants/requests";

export const createExtensionRequestValidator = async (
req: ExtensionRequestRequest,
res: ExtensionRequestResponse,
next: NextFunction
) => {

const schema = joi
.object()
.strict()
.keys({
taskId: joi.string().required().messages({
"string.empty": "taskId cannot be empty",
"any.required": "taskId is required",
}),
title: joi.string().required().messages({
"string.empty": "title cannot be empty",
"any.required": "title is required",
}),
oldEndsOn: joi.number().required().messages({
"number.base": "oldEndsOn must be a number",
"any.required": "oldEndsOn is required",
}),
newEndsOn: joi.number().required().min(joi.ref("oldEndsOn")).messages({
"number.base": "newEndsOn must be a number",
"any.required": "newEndsOn is required",
}),
message: joi.string().required().messages({
"string.empty": "message cannot be empty",
}),
state: joi.string().valid(REQUEST_STATE.PENDING).required().messages({
"string.empty": "state cannot be empty",
"any.required": "state is required",
}),
type: joi.string().valid(REQUEST_TYPE.EXTENSION).required().messages({
"string.empty": "type cannot be empty",
"any.required": "type is required",
}),
});

await schema.validateAsync(req.body, { abortEarly: false });
};
14 changes: 10 additions & 4 deletions middlewares/validators/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ 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 { createExtensionRequestValidator } from "./extensionRequestsv2";
import { ExtensionRequestRequest, ExtensionRequestResponse } from "../../types/extensionRequests";
import { CustomResponse } from "../../typeDefinitions/global";

export const createRequestsMiddleware = async (
req: OooRequestCreateRequest,
res: OooRequestResponse,
req: OooRequestCreateRequest|ExtensionRequestRequest,
res: CustomResponse,
next: NextFunction
) => {
const type = req.body.type;
Expand All @@ -20,13 +23,16 @@ export const createRequestsMiddleware = async (
case REQUEST_TYPE.OOO:
await createOooStatusRequestValidator(req as OooRequestCreateRequest, res as OooRequestResponse, next);
break;
case REQUEST_TYPE.EXTENSION:
await createExtensionRequestValidator(req as ExtensionRequestRequest, res as ExtensionRequestResponse, next);
break;
default:
res.boom.badRequest(`Invalid request type: ${type}`);
}

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 Expand Up @@ -67,7 +73,7 @@ export const getRequestsMiddleware = async (req: OooRequestCreateRequest, res: O
id: joi.string().optional(),
type: joi
.string()
.valid(REQUEST_TYPE.OOO, REQUEST_TYPE.ALL)
.valid(REQUEST_TYPE.OOO, REQUEST_TYPE.EXTENSION, REQUEST_TYPE.ALL)
.optional(),
requestedBy: joi.string().insensitive().optional(),
state: joi
Expand Down
6 changes: 4 additions & 2 deletions models/progresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ const createProgressDocument = async (progressData) => {
const { type, taskId } = progressData;
const createdAtTimestamp = new Date().getTime();
const progressDateTimestamp = getProgressDateTimestamp();
let taskTitle;
if (taskId) {
await assertTaskExists(taskId);
taskTitle = await assertTaskExists(taskId);
}
const query = buildQueryForPostingProgress(progressData);
const existingDocumentSnapshot = await query.where("date", "==", progressDateTimestamp).get();
Expand All @@ -35,7 +36,8 @@ const createProgressDocument = async (progressData) => {
}
const progressDocumentData = { ...progressData, createdAt: createdAtTimestamp, date: progressDateTimestamp };
const { id } = await progressesCollection.add(progressDocumentData);
return { id, ...progressDocumentData };
const data = { id, ...progressDocumentData };
return { data, taskTitle };
};

/**
Expand Down
Loading

0 comments on commit 4116605

Please sign in to comment.