Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REFACTOR: request controller and model functions #2017

Merged
merged 4 commits into from
May 4, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Prev Previous commit
Added middleware for extension request for /request route (#2028)
* added middleware for extension request

* test: fix failing tests

* test: add types for res and req and remove consoles
  • Loading branch information
sahsisunny authored May 4, 2024
commit e5de75c858477c96ffec96e48b985e1ccf712298
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
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 });
};
12 changes: 9 additions & 3 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
11 changes: 11 additions & 0 deletions test/fixtures/extension-requests/extensionRequests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { REQUEST_STATE, REQUEST_TYPE } from "../../../constants/requests";

export const extensionCreateObject = {
taskId: "4XlEQ64H8puuLTrwIi93",
title: "Extension Request",
oldEndsOn: 1708674980000,
newEndsOn: 1709674980000,
message: "request message",
type: REQUEST_TYPE.EXTENSION,
state: REQUEST_STATE.PENDING,
};
64 changes: 64 additions & 0 deletions test/unit/middlewares/extensionRequests.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import chai from "chai";
import sinon from "sinon";
const { expect } = chai;

import { createExtensionRequestValidator } from "../../../middlewares/validators/extensionRequestsv2";
import { extensionCreateObject } from "../../fixtures/extension-requests/extensionRequests";
import { REQUEST_STATE } from "../../../constants/requests";
import { ExtensionRequestRequest, ExtensionRequestResponse } from "../../../types/extensionRequests";

describe("Extension Request Validators", function () {
describe("createExtensionRequestValidator", function () {
it("should validate for a valid create extension request", async function () {
const req = {
body: extensionCreateObject,
};
const res = {};
const nextSpy = sinon.spy();
const response = await createExtensionRequestValidator(req as ExtensionRequestRequest, res as ExtensionRequestResponse, nextSpy);
expect(nextSpy.calledOnce);
});

it("should not validate for an invalid extension request on wrong type", async function () {
const req = {
body: { ...extensionCreateObject, type: "ACTIVE" },
};
const res = {};
const nextSpy = sinon.spy();
try {
await createExtensionRequestValidator(req as ExtensionRequestRequest, res as ExtensionRequestResponse, nextSpy);
} catch (error) {
expect(error).to.be.an.instanceOf(Error);
expect(error.details[0].message).to.equal(`"type" must be [EXTENSION]`);
}
});

it("should not validate for an invalid extension request on wrong status", async function () {
const req = {
body: { ...extensionCreateObject, state: REQUEST_STATE.APPROVED },
};
const res = {};
const nextSpy = sinon.spy();
try {
await createExtensionRequestValidator(req as ExtensionRequestRequest, res as ExtensionRequestResponse, nextSpy);
} catch (error) {
expect(error).to.be.an.instanceOf(Error);
expect(error.details[0].message).to.equal(`"state" must be [PENDING]`);
}
});

it("should not validate for an invalid extension request on missing taskId", async function () {
const req = {
body: { ...extensionCreateObject, taskId: "" },
};
const res = {};
const nextSpy = sinon.spy();
try {
await createExtensionRequestValidator(req as ExtensionRequestRequest, res as ExtensionRequestResponse, nextSpy);
} catch (error) {
expect(error).to.be.an.instanceOf(Error);
expect(error.details[0].message).to.equal(`taskId cannot be empty`);
}
});
});
});
1 change: 1 addition & 0 deletions typeDefinitions/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export type UserData = {
archived: boolean;
in_discord: boolean;
member: boolean;
super_user: boolean;
};
profileStatus: string;
created_at: number;
Expand Down
65 changes: 65 additions & 0 deletions types/extensionRequests.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Request, Response } from "express";
import { Boom } from "express-boom";
import { REQUEST_STATE, REQUEST_TYPE } from "../constants/requests";

export type ExtensionRequest = {
id: string;
type: REQUEST_TYPE.EXTENSION;
taskId: string;
title: string;
oldEndsOn: number;
newEndsOn: number;
message?: string;
requestedBy?: string;
state?: REQUEST_STATE;
lastModifiedBy?: string;
reason?: string;
createdAt?: Timestamp;
updatedAt?: Timestamp;
requestNumber?: number;
};

export type ExtensionRequestCreateBody = {
type: REQUEST_TYPE.EXTENSION;
taskId: string;
title: string;
message?: string;
requestedBy?: string;
oldEndsOn: number;
newEndsOn: number;
state: REQUEST_STATE.PENDING;
requestNumber?: number;
assignee?: string;
};

export type ExtensionRequestUpdateBody = {
lastModifiedBy?: string;
type?: REQUEST_TYPE.EXTENSION;
id?: string;
reason?: string;
state: REQUEST_STATE.APPROVED | REQUEST_STATE.REJECTED;
};

export type userData = {
id: string;
};

export type RequestQuery = {
dev?: string;
type?: string;
requestedBy?: string;
state?: REQUEST_STATE.APPROVED | REQUEST_STATE.PENDING | REQUEST_STATE.REJECTED;
id?: string;
prev?: string;
next?: string;
page?: number;
size?: number;
};

export type ExtensionRequestResponse = Response & { Boom: Boom };
export type ExtensionRequestRequest = Request & {
ExtensionRequestCreateBody: ExtensionRequestCreateBody;
userData: userData;
query: RequestQuery;
Boom: Boom;
};
Loading