From df7cee55d2ae9daba9eca995b64748ef2ece0e0d Mon Sep 17 00:00:00 2001 From: ZendeAditya Date: Tue, 29 Apr 2025 20:57:56 +0530 Subject: [PATCH 1/5] fix: change state to status in onboarding files --- .gitignore | 1 - config/default.js | 1 - constants/requests.ts | 2 +- controllers/auth.js | 28 +++++++------- controllers/extensionRequestsv2.ts | 6 +-- controllers/onboardingExtension.ts | 10 ++--- controllers/oooRequests.ts | 12 +++--- controllers/taskRequestsv2.ts | 12 +++--- middlewares/validators/extensionRequestsv2.ts | 4 +- middlewares/validators/oooRequests.ts | 4 +- middlewares/validators/requests.ts | 6 +-- middlewares/validators/taskRequests.ts | 4 +- models/discordactions.js | 4 +- models/requests.ts | 6 +-- services/onboardingExtension.ts | 4 +- .../extension-requests/extensionRequests.ts | 4 +- test/fixtures/oooRequest/oooRequest.ts | 26 ++++++------- test/fixtures/taskRequests/taskRequests.ts | 6 +-- test/integration/discordactions.test.js | 4 +- test/integration/onboardingExtension.test.ts | 38 +++++++++---------- test/integration/requests.test.ts | 12 +++--- .../middlewares/extensionRequests.test.ts | 4 +- test/unit/models/discordactions.test.js | 10 ++--- test/unit/models/requests.test.ts | 12 +++--- .../unit/services/onboardingExtension.test.ts | 8 ++-- types/extensionRequests.d.ts | 10 ++--- types/onboardingExtension.d.ts | 6 +-- types/oooRequest.d.ts | 12 +++--- types/requests.d.ts | 6 +-- types/taskRequests.d.ts | 12 +++--- 30 files changed, 136 insertions(+), 138 deletions(-) diff --git a/.gitignore b/.gitignore index 90018a6eb..c38e59bbf 100644 --- a/.gitignore +++ b/.gitignore @@ -124,5 +124,4 @@ dist # Local config file config/local.js - package-lock.json \ No newline at end of file diff --git a/config/default.js b/config/default.js index 4b7dc2b83..c3874f10e 100644 --- a/config/default.js +++ b/config/default.js @@ -5,7 +5,6 @@ * * Documentation: https://github.com/lorenwest/node-config/wiki/Configuration-Files */ - const NODE_ENV = process.env.NODE_ENV; module.exports = { port: 3000, diff --git a/constants/requests.ts b/constants/requests.ts index 8a9635d2d..bd07e9c21 100644 --- a/constants/requests.ts +++ b/constants/requests.ts @@ -1,4 +1,4 @@ -export const REQUEST_STATE = { +export const REQUEST_STATUS = { APPROVED: "APPROVED", PENDING: "PENDING", REJECTED: "REJECTED", diff --git a/controllers/auth.js b/controllers/auth.js index 6b1bf4cd6..42168665b 100644 --- a/controllers/auth.js +++ b/controllers/auth.js @@ -172,20 +172,20 @@ const githubAuthCallback = (req, res, next) => { updated_at: null, }; - if (!userData.email) { - const githubBaseUrl = config.get("githubApi.baseUrl"); - const res = await fetch(`${githubBaseUrl}/user/emails`, { - headers: { - Authorization: `token ${accessToken}`, - }, - }); - const emails = await res.json(); - const primaryEmails = emails.filter((item) => item.primary); - - if (primaryEmails.length > 0) { - userData.email = primaryEmails[0].email; - } - } + // if (!userData.email) { + // const githubBaseUrl = config.get("githubApi.baseUrl"); + // const res = await fetch(`${githubBaseUrl}/user/emails`, { + // headers: { + // Authorization: `token ${accessToken}`, + // }, + // }); + // const emails = await res.json(); + // const primaryEmails = emails.filter((item) => item.primary); + + // if (primaryEmails.length > 0) { + // userData.email = primaryEmails[0].email; + // } + // } const { userId, incompleteUserDetails, role } = await users.addOrUpdate(userData); diff --git a/controllers/extensionRequestsv2.ts b/controllers/extensionRequestsv2.ts index edde8482e..8e15ca802 100644 --- a/controllers/extensionRequestsv2.ts +++ b/controllers/extensionRequestsv2.ts @@ -6,7 +6,7 @@ import { REQUEST_APPROVED_SUCCESSFULLY, REQUEST_LOG_TYPE, REQUEST_REJECTED_SUCCESSFULLY, - REQUEST_STATE, + REQUEST_STATUS, REQUEST_TYPE, } from "../constants/requests"; import { addLog } from "../models/logs"; @@ -64,7 +64,7 @@ export const createTaskExtensionRequest = async (req: ExtensionRequestRequest, r type: REQUEST_TYPE.EXTENSION, }); - if (latestExtensionRequest && latestExtensionRequest.state === REQUEST_STATE.PENDING) { + if (latestExtensionRequest && latestExtensionRequest.state === REQUEST_STATUS.PENDING) { return res.boom.badRequest("An extension request for this task already exists."); } const requestNumber: number = @@ -117,7 +117,7 @@ export const updateTaskExtensionRequest = async (req: any, res: any) => { return res.boom.badRequest(requestResult.error); } const [logType, returnMessage] = - requestResult.state === REQUEST_STATE.APPROVED + requestResult.state === REQUEST_STATUS.APPROVED ? [REQUEST_LOG_TYPE.REQUEST_APPROVED, REQUEST_APPROVED_SUCCESSFULLY] : [REQUEST_LOG_TYPE.REQUEST_REJECTED, REQUEST_REJECTED_SUCCESSFULLY]; diff --git a/controllers/onboardingExtension.ts b/controllers/onboardingExtension.ts index 356bd20e9..606424606 100644 --- a/controllers/onboardingExtension.ts +++ b/controllers/onboardingExtension.ts @@ -8,7 +8,7 @@ import { REQUEST_DOES_NOT_EXIST, REQUEST_LOG_TYPE, REQUEST_REJECTED_SUCCESSFULLY, - REQUEST_STATE, + REQUEST_STATUS, REQUEST_TYPE, REQUEST_UPDATED_SUCCESSFULLY, UNAUTHORIZED_TO_CREATE_ONBOARDING_EXTENSION_REQUEST, @@ -73,7 +73,7 @@ export const createOnboardingExtensionRequestController = async ( type: REQUEST_TYPE.ONBOARDING }); - if(latestExtensionRequest && latestExtensionRequest.state === REQUEST_STATE.PENDING){ + if(latestExtensionRequest && latestExtensionRequest.state === REQUEST_STATUS.PENDING){ return res.boom.conflict(REQUEST_ALREADY_PENDING); } @@ -93,7 +93,7 @@ export const createOnboardingExtensionRequestController = async ( if(!latestExtensionRequest){ requestNumber = 1; oldEndsOn = discordJoinedDateInMillisecond + millisecondsInThirtyOneDays; - }else if(latestExtensionRequest.state === REQUEST_STATE.REJECTED) { + }else if(latestExtensionRequest.state === REQUEST_STATUS.REJECTED) { requestNumber = latestExtensionRequest.requestNumber + 1; oldEndsOn = latestExtensionRequest.oldEndsOn; }else{ @@ -105,7 +105,7 @@ export const createOnboardingExtensionRequestController = async ( const onboardingExtension = await createRequest({ type: REQUEST_TYPE.ONBOARDING, - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, userId: userId, requestedBy: username, oldEndsOn: oldEndsOn, @@ -179,7 +179,7 @@ export const updateOnboardingExtensionRequestState = async ( return res.boom.badRequest(response.error); } - const [logType, returnMessage] = response.state === REQUEST_STATE.APPROVED + const [logType, returnMessage] = response.state === REQUEST_STATUS.APPROVED ? [REQUEST_LOG_TYPE.REQUEST_APPROVED, REQUEST_APPROVED_SUCCESSFULLY] : [REQUEST_LOG_TYPE.REQUEST_REJECTED, REQUEST_REJECTED_SUCCESSFULLY]; diff --git a/controllers/oooRequests.ts b/controllers/oooRequests.ts index a3dea406b..26c357551 100644 --- a/controllers/oooRequests.ts +++ b/controllers/oooRequests.ts @@ -4,7 +4,7 @@ import { REQUEST_CREATED_SUCCESSFULLY, ERROR_WHILE_CREATING_REQUEST, REQUEST_ALREADY_PENDING, - REQUEST_STATE, + REQUEST_STATUS, REQUEST_TYPE, ERROR_WHILE_UPDATING_REQUEST, REQUEST_APPROVED_SUCCESSFULLY, @@ -28,9 +28,9 @@ export const createOooRequestController = async (req: OooRequestCreateRequest, r } try { - const latestOooRequest:OooStatusRequest = await getRequestByKeyValues({ requestedBy: userId, type: REQUEST_TYPE.OOO , state: REQUEST_STATE.PENDING }); + const latestOooRequest:OooStatusRequest = await getRequestByKeyValues({ requestedBy: userId, type: REQUEST_TYPE.OOO , status: REQUEST_STATUS.PENDING }); - if (latestOooRequest && latestOooRequest.state === REQUEST_STATE.PENDING) { + if (latestOooRequest && latestOooRequest.status === REQUEST_STATUS.PENDING) { return res.boom.badRequest(REQUEST_ALREADY_PENDING); } @@ -75,7 +75,7 @@ export const updateOooRequestController = async (req: UpdateRequest, res: Custom return res.boom.badRequest(requestResult.error); } const [logType, returnMessage] = - requestResult.state === REQUEST_STATE.APPROVED + requestResult.status === REQUEST_STATUS.APPROVED ? [REQUEST_LOG_TYPE.REQUEST_APPROVED, REQUEST_APPROVED_SUCCESSFULLY] : [REQUEST_LOG_TYPE.REQUEST_REJECTED, REQUEST_REJECTED_SUCCESSFULLY]; @@ -90,7 +90,7 @@ export const updateOooRequestController = async (req: UpdateRequest, res: Custom body: requestResult, }; await addLog(requestLog.type, requestLog.meta, requestLog.body); - if (requestResult.state === REQUEST_STATE.APPROVED) { + if (requestResult.status === REQUEST_STATUS.APPROVED) { const requestData = await getRequests({ id: requestId }); if (requestData) { @@ -98,7 +98,7 @@ export const updateOooRequestController = async (req: UpdateRequest, res: Custom const userFutureStatusData = { requestId, status: REQUEST_TYPE.OOO, - state: statusState.UPCOMING, + status: statusState.UPCOMING, from, endsOn: until, userId: requestedBy, diff --git a/controllers/taskRequestsv2.ts b/controllers/taskRequestsv2.ts index 327c56bc1..5909c101d 100644 --- a/controllers/taskRequestsv2.ts +++ b/controllers/taskRequestsv2.ts @@ -1,4 +1,4 @@ -import { REQUEST_STATE, TASK_REQUEST_MESSAGES } from "../constants/requests"; +import { REQUEST_STATUS, TASK_REQUEST_MESSAGES } from "../constants/requests"; import { TASK_REQUEST_TYPE } from "../constants/taskRequests"; import { addLog } from "../models/logs"; import { createRequest, getRequestByKeyValues } from "../models/requests"; @@ -64,13 +64,13 @@ export const createTaskRequestController = async (req: TaskRequestRequest, res: if ( existingRequest && - existingRequest.state === REQUEST_STATE.PENDING && + existingRequest.state === REQUEST_STATUS.PENDING && existingRequest.requestors.includes(requestedBy) ) { return res.boom.badRequest(TASK_REQUEST_MESSAGES.TASK_REQUEST_EXISTS); } else if ( existingRequest && - existingRequest.state === REQUEST_STATE.PENDING && + existingRequest.state === REQUEST_STATUS.PENDING && !existingRequest.requestors.includes(requestedBy) ) { existingRequest.requestors.push(requestedBy); @@ -83,7 +83,7 @@ export const createTaskRequestController = async (req: TaskRequestRequest, res: markdownEnabled: taskRequestData.markdownEnabled, firstName: userData.first_name, lastName: userData.last_name, - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, requestedAt: Date.now(), }); const updatedRequest = await createRequest(existingRequest); @@ -116,7 +116,7 @@ export const createTaskRequestController = async (req: TaskRequestRequest, res: externalIssueHtmlUrl: taskRequestData.externalIssueHtmlUrl, requestType: taskRequestData.requestType, type: taskRequestData.type, - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, requestedBy: requestedBy, taskTitle: taskRequestData.taskTitle, users: [ @@ -129,7 +129,7 @@ export const createTaskRequestController = async (req: TaskRequestRequest, res: markdownEnabled: taskRequestData.markdownEnabled, firstName: userData.first_name, lastName: userData.last_name, - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, requestedAt: Date.now(), }, ], diff --git a/middlewares/validators/extensionRequestsv2.ts b/middlewares/validators/extensionRequestsv2.ts index 40dee4eac..6b37110a5 100644 --- a/middlewares/validators/extensionRequestsv2.ts +++ b/middlewares/validators/extensionRequestsv2.ts @@ -1,7 +1,7 @@ import joi from "joi"; import { ExtensionRequestRequest, ExtensionRequestResponse } from "../../types/extensionRequests"; import { NextFunction } from "express"; -import { REQUEST_TYPE,REQUEST_STATE } from "../../constants/requests"; +import { REQUEST_TYPE,REQUEST_STATUS } from "../../constants/requests"; export const createExtensionRequestValidator = async ( req: ExtensionRequestRequest, @@ -32,7 +32,7 @@ export const createExtensionRequestValidator = async ( message: joi.string().required().messages({ "string.empty": "message cannot be empty", }), - state: joi.string().valid(REQUEST_STATE.PENDING).required().messages({ + state: joi.string().valid(REQUEST_STATUS.PENDING).required().messages({ "string.empty": "state cannot be empty", "any.required": "state is required", }), diff --git a/middlewares/validators/oooRequests.ts b/middlewares/validators/oooRequests.ts index 1a90aea6e..56f2d78ba 100644 --- a/middlewares/validators/oooRequests.ts +++ b/middlewares/validators/oooRequests.ts @@ -1,6 +1,6 @@ import joi from "joi"; import { NextFunction } from "express"; -import { REQUEST_STATE, REQUEST_TYPE } from "../../constants/requests"; +import { REQUEST_STATUS, REQUEST_TYPE } from "../../constants/requests"; import { OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest"; export const createOooStatusRequestValidator = async ( @@ -30,7 +30,7 @@ export const createOooStatusRequestValidator = async ( "any.required": "message is required", "string.empty": "message cannot be empty", }), - state: joi.string().valid(REQUEST_STATE.PENDING).required().messages({ + state: joi.string().valid(REQUEST_STATUS.PENDING).required().messages({ "any.only": "state must be PENDING", }), type: joi.string().valid(REQUEST_TYPE.OOO).required(), diff --git a/middlewares/validators/requests.ts b/middlewares/validators/requests.ts index 80ff0478b..f8a98abba 100644 --- a/middlewares/validators/requests.ts +++ b/middlewares/validators/requests.ts @@ -1,6 +1,6 @@ import joi from "joi"; import { NextFunction } from "express"; -import { REQUEST_STATE, REQUEST_TYPE } from "../../constants/requests"; +import { REQUEST_STATUS, REQUEST_TYPE } from "../../constants/requests"; import { OooRequestCreateRequest, OooRequestResponse } from "../../types/oooRequest"; import { createOooStatusRequestValidator } from "./oooRequests"; import { createExtensionRequestValidator } from "./extensionRequestsv2"; @@ -60,7 +60,7 @@ export const updateRequestsMiddleware = async ( }), state: joi .string() - .valid(REQUEST_STATE.APPROVED, REQUEST_STATE.REJECTED) + .valid(REQUEST_STATUS.APPROVED, REQUEST_STATUS.REJECTED) .required() .messages({ "any.only": "state must be APPROVED or REJECTED", @@ -90,7 +90,7 @@ export const getRequestsMiddleware = async (req: OooRequestCreateRequest, res: O requestedBy: joi.string().insensitive().optional(), state: joi .string() - .valid(REQUEST_STATE.APPROVED, REQUEST_STATE.PENDING, REQUEST_STATE.REJECTED) + .valid(REQUEST_STATUS.APPROVED, REQUEST_STATUS.PENDING, REQUEST_STATUS.REJECTED) .optional(), page: joi.number().integer().min(0).when("next", { is: joi.exist(), diff --git a/middlewares/validators/taskRequests.ts b/middlewares/validators/taskRequests.ts index 383ee3e6b..f1527f1ed 100644 --- a/middlewares/validators/taskRequests.ts +++ b/middlewares/validators/taskRequests.ts @@ -1,7 +1,7 @@ import joi from "joi"; import { TaskRequestResponse, TaskRequestRequest } from "../../types/taskRequests"; import { NextFunction } from "express"; -import { REQUEST_TYPE, REQUEST_STATE } from "../../constants/requests"; +import { REQUEST_TYPE, REQUEST_STATUS } from "../../constants/requests"; import { GITHUB_URL } from "../../constants/urls"; import config from "config"; @@ -36,7 +36,7 @@ export const createTaskRequestValidator = async ( "string.empty": "type cannot be empty", "any.required": "type is required", }), - state: joi.string().valid(REQUEST_STATE.PENDING).required().messages({ + state: joi.string().valid(REQUEST_STATUS.PENDING).required().messages({ "string.empty": "state cannot be empty", "any.required": "state is required", }), diff --git a/models/discordactions.js b/models/discordactions.js index 3ba9ee658..1ba91d5f7 100644 --- a/models/discordactions.js +++ b/models/discordactions.js @@ -29,7 +29,7 @@ const discordService = require("../services/discordService"); const { buildTasksQueryForMissedUpdates } = require("../utils/tasks"); const { buildProgressQueryForMissedUpdates } = require("../utils/progresses"); const { getRequestByKeyValues } = require("./requests"); -const { REQUEST_TYPE, REQUEST_STATE } = require("../constants/requests"); +const { REQUEST_TYPE, REQUEST_STATUS } = require("../constants/requests"); const allMavens = []; /** @@ -778,7 +778,7 @@ const skipOnboardingUsersHavingApprovedExtensionRequest = async (users = []) => try { const latestApprovedExtension = await getRequestByKeyValues({ type: REQUEST_TYPE.ONBOARDING, - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, userId: user.id, }); diff --git a/models/requests.ts b/models/requests.ts index 064eebd8c..f40f9102f 100644 --- a/models/requests.ts +++ b/models/requests.ts @@ -1,6 +1,6 @@ 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_STATUS } from "../constants/requests"; import { ERROR_WHILE_FETCHING_REQUEST, ERROR_WHILE_CREATING_REQUEST, @@ -37,12 +37,12 @@ export const updateRequest = async (id: string, body: any, lastModifiedBy: strin error: REQUEST_DOES_NOT_EXIST, }; } - if (existingRequestDoc.data().state === REQUEST_STATE.APPROVED) { + if (existingRequestDoc.data().state === REQUEST_STATUS.APPROVED) { return { error: REQUEST_ALREADY_APPROVED, }; } - if (existingRequestDoc.data().state === REQUEST_STATE.REJECTED) { + if (existingRequestDoc.data().state === REQUEST_STATUS.REJECTED) { return { error: REQUEST_ALREADY_REJECTED, }; diff --git a/services/onboardingExtension.ts b/services/onboardingExtension.ts index f2b21fea9..f633475b9 100644 --- a/services/onboardingExtension.ts +++ b/services/onboardingExtension.ts @@ -6,7 +6,7 @@ import { PENDING_REQUEST_UPDATED, REQUEST_DOES_NOT_EXIST, REQUEST_LOG_TYPE, - REQUEST_STATE, + REQUEST_STATUS, REQUEST_TYPE, UNAUTHORIZED_TO_UPDATE_REQUEST } from "../constants/requests"; @@ -63,7 +63,7 @@ export const validateOnboardingExtensionUpdateRequest = async ( }; } - if(extensionRequest.state !== REQUEST_STATE.PENDING){ + if(extensionRequest.state !== REQUEST_STATUS.PENDING){ await addLog(logType.PENDING_REQUEST_CAN_BE_UPDATED, { state: extensionRequest.state }, { message:PENDING_REQUEST_UPDATED } diff --git a/test/fixtures/extension-requests/extensionRequests.ts b/test/fixtures/extension-requests/extensionRequests.ts index f1d38fa63..bedac40f9 100644 --- a/test/fixtures/extension-requests/extensionRequests.ts +++ b/test/fixtures/extension-requests/extensionRequests.ts @@ -1,4 +1,4 @@ -import { REQUEST_STATE, REQUEST_TYPE } from "../../../constants/requests"; +import { REQUEST_STATUS, REQUEST_TYPE } from "../../../constants/requests"; export const extensionCreateObject = { taskId: "4XlEQ64H8puuLTrwIi93", @@ -7,5 +7,5 @@ export const extensionCreateObject = { newEndsOn: 1709674980000, message: "request message", type: REQUEST_TYPE.EXTENSION, - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, }; diff --git a/test/fixtures/oooRequest/oooRequest.ts b/test/fixtures/oooRequest/oooRequest.ts index c1b99a2c8..4f7e49a59 100644 --- a/test/fixtures/oooRequest/oooRequest.ts +++ b/test/fixtures/oooRequest/oooRequest.ts @@ -1,4 +1,4 @@ -import { REQUEST_STATE, REQUEST_TYPE } from "../../../constants/requests"; +import { REQUEST_STATUS, REQUEST_TYPE } from "../../../constants/requests"; export const createOooStatusRequests = { type: "OOO", @@ -6,7 +6,7 @@ export const createOooStatusRequests = { from: Date.now() + 100000, until: Date.now() + 200000, message: "Out of office for personal reasons.", - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, createdAt: 1234567890, updatedAt: 1234567890, }; @@ -16,7 +16,7 @@ export const validOooStatusRequests = { from: Date.now() + 100000, until: Date.now() + 200000, message: "Out of office for personal reasons.", - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, }; export const invalidOooStatusRequests = { @@ -27,21 +27,21 @@ export const invalidOooStatusRequests = { }; export const updateOooApprovedRequests = { - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, lastModifiedBy: "admin123", updatedAt: 1234567890, reason: "Approval granted.", }; export const updateOooRejectedRequests = { - state: REQUEST_STATE.REJECTED, + state: REQUEST_STATUS.REJECTED, lastModifiedBy: "admin123", updatedAt: 1234567890, reason: "Sorry, we can't approve additional leave at this time.", }; export const validOooStatusUpdate ={ - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, reason: "Welcome back! Enjoy the conference.", type:REQUEST_TYPE.OOO } @@ -58,7 +58,7 @@ export const createOooRequests = { from: Date.now() + 100000, until: Date.now() + 200000, message: "Out of office for personal reasons.", - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, }; export const createOooRequests2 = { requestedBy: "testUser2", @@ -66,7 +66,7 @@ export const createOooRequests2 = { from: Date.now() + 100000, until: Date.now() + 200000, message: "Out of office for personal reasons.", - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, }; @@ -74,7 +74,7 @@ export const oooStatusRequests = [ { id: "MpykhM8sT1Tlid4Y6Y0d", requestedBy: "user456", - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, from: 1709525300000, until: 1709870800000, message: "Attending a work conference.", @@ -86,7 +86,7 @@ export const oooStatusRequests = [ { id: "Me8sT1Tlid4Y6Y0d", requestedBy: "user789", - state: REQUEST_STATE.REJECTED, + state: REQUEST_STATUS.REJECTED, from: 1709603700000, until: 1709785600000, message: "Out of office for personal reasons.", @@ -99,7 +99,7 @@ export const oooStatusRequests = [ { id: "abc123", requestedBy: "user101", - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, from: 1710000000000, until: 1711000000000, message: "Family vacation.", @@ -110,7 +110,7 @@ export const oooStatusRequests = [ { id: "def456", requestedBy: "user202", - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, from: 1712000000000, until: 1713000000000, message: "Remote work due to personal reasons.", @@ -123,7 +123,7 @@ export const oooStatusRequests = [ export const updateOooStatusRequest = [ { - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, lastModifiedBy: "admin123", updatedAt: 1234567890, reason: "Approval granted.", diff --git a/test/fixtures/taskRequests/taskRequests.ts b/test/fixtures/taskRequests/taskRequests.ts index fcfe2e685..7dd1bcebe 100644 --- a/test/fixtures/taskRequests/taskRequests.ts +++ b/test/fixtures/taskRequests/taskRequests.ts @@ -1,4 +1,4 @@ -import { REQUEST_STATE, REQUEST_TYPE } from "../../../constants/requests"; +import { REQUEST_STATUS, REQUEST_TYPE } from "../../../constants/requests"; import { TASK_REQUEST_TYPE } from "../../../constants/taskRequests"; export const validTaskCreqtionRequest = { @@ -10,7 +10,7 @@ export const validTaskCreqtionRequest = { proposedDeadline: 1719450351203, description: "Task Create Description", markdownEnabled: true, - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, type: REQUEST_TYPE.TASK, }; @@ -23,6 +23,6 @@ export const validTaskAssignmentRequest = { proposedDeadline: 1719450351203, description: "Task Create Description", markdownEnabled: true, - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, type: REQUEST_TYPE.TASK, }; diff --git a/test/integration/discordactions.test.js b/test/integration/discordactions.test.js index d5604897f..d029c7d05 100644 --- a/test/integration/discordactions.test.js +++ b/test/integration/discordactions.test.js @@ -49,7 +49,7 @@ const { userStatusDataForOooState } = require("../fixtures/userStatus/userStatus const { generateCronJobToken } = require("../utils/generateBotToken"); const { CRON_JOB_HANDLER } = require("../../constants/bot"); const { createRequest } = require("../../models/requests"); -const { REQUEST_TYPE, REQUEST_STATE } = require("../../constants/requests"); +const { REQUEST_TYPE, REQUEST_STATUS } = require("../../constants/requests"); const { convertDaysToMilliseconds } = require("../../utils/time"); describe("Discord actions", function () { @@ -914,7 +914,7 @@ describe("Discord actions", function () { it("should filter users who have approved extension request and update groupOnboarding31d+ role", function (done) { createRequest({ type: REQUEST_TYPE.ONBOARDING, - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, userId: userId, newEndsOn: Date.now() + convertDaysToMilliseconds(2), }); diff --git a/test/integration/onboardingExtension.test.ts b/test/integration/onboardingExtension.test.ts index 4c81e98b7..34ed1e5e6 100644 --- a/test/integration/onboardingExtension.test.ts +++ b/test/integration/onboardingExtension.test.ts @@ -8,7 +8,7 @@ import cleanDb from "../utils/cleanDb"; import { CreateOnboardingExtensionBody, OnboardingExtension } from "../../types/onboardingExtension"; import { REQUEST_ALREADY_PENDING, - REQUEST_STATE, REQUEST_TYPE, + REQUEST_STATUS, REQUEST_TYPE, ONBOARDING_REQUEST_CREATED_SUCCESSFULLY, UNAUTHORIZED_TO_CREATE_ONBOARDING_EXTENSION_REQUEST, REQUEST_FETCHED_SUCCESSFULLY, @@ -42,7 +42,7 @@ describe("/requests Onboarding Extension", () => { const testUserDiscordId: string = "654321"; const extensionRequest = { - state: REQUEST_STATE.APPROVED, + status: REQUEST_STATUS.APPROVED, type: REQUEST_TYPE.ONBOARDING, requestNumber: 1 }; @@ -228,7 +228,7 @@ describe("/requests Onboarding Extension", () => { it("should return 409 response when a user already has a pending request", (done)=> { createUserStatusWithState(testUserId, userStatusModel, userState.ONBOARDING); - requestsQuery.createRequest({...extensionRequest, state: REQUEST_STATE.PENDING, userId: testUserId}); + requestsQuery.createRequest({...extensionRequest, state: REQUEST_STATUS.PENDING, userId: testUserId}); chai.request(app) .post(`${postEndpoint}?dev=true`) @@ -255,7 +255,7 @@ describe("/requests Onboarding Extension", () => { expect(res.body.message).to.equal(ONBOARDING_REQUEST_CREATED_SUCCESSFULLY); expect(res.body.data.requestNumber).to.equal(1); expect(res.body.data.reason).to.equal(body.reason); - expect(res.body.data.state).to.equal(REQUEST_STATE.PENDING); + expect(res.body.data.state).to.equal(REQUEST_STATUS.PENDING); done(); }) }) @@ -265,7 +265,7 @@ describe("/requests Onboarding Extension", () => { const latestApprovedExtension = await requestsQuery.createRequest({ ...extensionRequest, userId: testUserId, - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, newEndsOn: Date.now() + 2*24*60*60*1000, oldEndsOn: Date.now() - 24*60*60*1000, }); @@ -279,7 +279,7 @@ describe("/requests Onboarding Extension", () => { expect(res.body.message).to.equal(ONBOARDING_REQUEST_CREATED_SUCCESSFULLY); expect(res.body.data.requestNumber).to.equal(2); expect(res.body.data.reason).to.equal(body.reason); - expect(res.body.data.state).to.equal(REQUEST_STATE.PENDING); + expect(res.body.data.state).to.equal(REQUEST_STATUS.PENDING); expect(res.body.data.oldEndsOn).to.equal(latestApprovedExtension.newEndsOn); expect(res.body.data.newEndsOn).to.equal(latestApprovedExtension.newEndsOn + (body.numberOfDays*24*60*60*1000)); }) @@ -289,7 +289,7 @@ describe("/requests Onboarding Extension", () => { const currentDate = Date.now(); const latestRejectedExtension = await requestsQuery.createRequest({ ...extensionRequest, - state: REQUEST_STATE.REJECTED, + state: REQUEST_STATUS.REJECTED, userId: testUserId, newEndsOn: currentDate, oldEndsOn: currentDate - 24*60*60*1000, @@ -304,7 +304,7 @@ describe("/requests Onboarding Extension", () => { expect(res.body.message).to.equal(ONBOARDING_REQUEST_CREATED_SUCCESSFULLY); expect(res.body.data.requestNumber).to.equal(2); expect(res.body.data.reason).to.equal(body.reason);; - expect(res.body.data.state).to.equal(REQUEST_STATE.PENDING); + expect(res.body.data.state).to.equal(REQUEST_STATUS.PENDING); expect(res.body.data.oldEndsOn).to.equal(latestRejectedExtension.oldEndsOn); expect(new Date(res.body.data.newEndsOn).toDateString()) .to.equal(new Date(currentDate + (body.numberOfDays*24*60*60*1000)).toDateString()); @@ -374,7 +374,7 @@ describe("/requests Onboarding Extension", () => { }); it("should fetch onboarding extension request by state field", (done) => { - requestsQuery.createRequest({ type: REQUEST_TYPE.ONBOARDING, state: REQUEST_STATE.APPROVED }); + requestsQuery.createRequest({ type: REQUEST_TYPE.ONBOARDING, state: REQUEST_STATUS.APPROVED }); chai.request(app) .get(`${getEndpoint}?state=APPROVED`) .end((err, res) => { @@ -383,7 +383,7 @@ describe("/requests Onboarding Extension", () => { expect(res.body.message).to.equal(REQUEST_FETCHED_SUCCESSFULLY); expect(res.body.data.length).to.equal(1); expect(res.body.data[0].type).to.equal(REQUEST_TYPE.ONBOARDING); - expect(res.body.data[0].state).to.equal(REQUEST_STATE.APPROVED); + expect(res.body.data[0].state).to.equal(REQUEST_STATUS.APPROVED); return done(); }); }); @@ -392,7 +392,7 @@ describe("/requests Onboarding Extension", () => { describe("PUT /requests", () => { const body = { type: REQUEST_TYPE.ONBOARDING, - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, message: "test-message" }; let latestExtension: OnboardingExtension; @@ -405,16 +405,16 @@ describe("/requests Onboarding Extension", () => { beforeEach(async () => { userId = await addUser(userData[4]); latestExtension = await requestsQuery.createRequest({ - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, type: REQUEST_TYPE.ONBOARDING, requestNumber: 1 }); latestApprovedExtension = await requestsQuery.createRequest({ - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, type: REQUEST_TYPE.ONBOARDING, requestNumber: 2 }); latestRejectedExtension = await requestsQuery.createRequest({ - state: REQUEST_STATE.REJECTED, + state: REQUEST_STATUS.REJECTED, type: REQUEST_TYPE.ONBOARDING, requestNumber: 2 }); @@ -498,7 +498,7 @@ describe("/requests Onboarding Extension", () => { chai.request(app) .put(putEndpoint) .set("authorization", `Bearer ${authToken}`) - .send({...body, state: REQUEST_STATE.PENDING}) + .send({...body, state: REQUEST_STATUS.PENDING}) .end((err, res) => { if (err) return done(err); expect(res.statusCode).to.equal(400); @@ -581,7 +581,7 @@ describe("/requests Onboarding Extension", () => { chai.request(app) .put(putEndpoint) .set("authorization", `Bearer ${authToken}`) - .send({...body, state: REQUEST_STATE.REJECTED}) + .send({...body, state: REQUEST_STATUS.REJECTED}) .end((err, res) => { if (err) return done(err); expect(res.statusCode).to.equal(200); @@ -628,19 +628,19 @@ describe("/requests Onboarding Extension", () => { invalidUserId = await addUser(userData[0]); superUserId = await addUser(userData[4]); latestInvalidExtension = await requestsQuery.createRequest({ - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, type: REQUEST_TYPE.ONBOARDING, oldEndsOn: Date.now() + convertDaysToMilliseconds(5), userId: userId, }); latestValidExtension = await requestsQuery.createRequest({ - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, type: REQUEST_TYPE.ONBOARDING, oldEndsOn: Date.now() - convertDaysToMilliseconds(3), userId: userId }); latestApprovedExtension = await requestsQuery.createRequest({ - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, type: REQUEST_TYPE.ONBOARDING, oldEndsOn: Date.now(), userId: userId diff --git a/test/integration/requests.test.ts b/test/integration/requests.test.ts index 0d6fe74c2..870cca3b6 100644 --- a/test/integration/requests.test.ts +++ b/test/integration/requests.test.ts @@ -18,7 +18,7 @@ import { import { createRequest, updateRequest } from "../../models/requests"; import { REQUEST_ALREADY_APPROVED, - REQUEST_STATE, + REQUEST_STATUS, REQUEST_TYPE, REQUEST_APPROVED_SUCCESSFULLY, REQUEST_CREATED_SUCCESSFULLY, @@ -56,7 +56,7 @@ describe("/requests OOO", function () { const { id: pendingOooId }: any = await createRequest(oooRequestData2); pendingOooRequestId = pendingOooId; - const { id: approveOooId }: any = await updateRequest(oooRequestId, { state: REQUEST_STATE.APPROVED }, superUserId, REQUEST_TYPE.OOO); + const { id: approveOooId }: any = await updateRequest(oooRequestId, { state: REQUEST_STATUS.APPROVED }, superUserId, REQUEST_TYPE.OOO); approvedOooRequestId = approveOooId; authToken = authService.generateAuthToken({ userId }); @@ -122,7 +122,7 @@ describe("/requests OOO", function () { expect(res.body.data.until).to.be.above(res.body.data.from); expect(res.body.data).to.have.property("requestedBy"); expect(res.body.data.type).to.equal(REQUEST_TYPE.OOO); - expect(res.body.data.state).to.equal(REQUEST_STATE.PENDING); + expect(res.body.data.state).to.equal(REQUEST_STATUS.PENDING); expect(res.body.message).to.equal(REQUEST_CREATED_SUCCESSFULLY); done(); }); @@ -176,7 +176,7 @@ describe("/requests OOO", function () { .request(app) .post("/requests") .set("cookie", `${cookieName}=${authToken}`) - .send({ ...validOooStatusRequests, state: REQUEST_STATE.APPROVED }) + .send({ ...validOooStatusRequests, state: REQUEST_STATUS.APPROVED }) .end(function (err, res) { expect(res).to.have.status(400); expect(res.body).to.have.property("message"); @@ -592,12 +592,12 @@ describe("/requests Extension", function () { let pendingExtensionRequestId: string; const approvedExtensionRequest = { - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, type: REQUEST_TYPE.EXTENSION, }; const rejectedExtensionRequest = { - state: REQUEST_STATE.REJECTED, + state: REQUEST_STATUS.REJECTED, type: REQUEST_TYPE.EXTENSION, }; diff --git a/test/unit/middlewares/extensionRequests.test.ts b/test/unit/middlewares/extensionRequests.test.ts index 4631542d6..ec1c37582 100644 --- a/test/unit/middlewares/extensionRequests.test.ts +++ b/test/unit/middlewares/extensionRequests.test.ts @@ -4,7 +4,7 @@ const { expect } = chai; import { createExtensionRequestValidator } from "../../../middlewares/validators/extensionRequestsv2"; import { extensionCreateObject } from "../../fixtures/extension-requests/extensionRequests"; -import { REQUEST_STATE } from "../../../constants/requests"; +import { REQUEST_STATUS } from "../../../constants/requests"; import { ExtensionRequestRequest, ExtensionRequestResponse } from "../../../types/extensionRequests"; describe("Extension Request Validators", function () { @@ -35,7 +35,7 @@ describe("Extension Request Validators", function () { it("should not validate for an invalid extension request on wrong status", async function () { const req = { - body: { ...extensionCreateObject, state: REQUEST_STATE.APPROVED }, + body: { ...extensionCreateObject, state: REQUEST_STATUS.APPROVED }, }; const res = {}; const nextSpy = sinon.spy(); diff --git a/test/unit/models/discordactions.test.js b/test/unit/models/discordactions.test.js index 1a21c6f61..296865bd3 100644 --- a/test/unit/models/discordactions.test.js +++ b/test/unit/models/discordactions.test.js @@ -64,7 +64,7 @@ const { stubbedModelTaskProgressData } = require("../../fixtures/progress/progre const { convertDaysToMilliseconds } = require("../../../utils/time"); const { generateUserStatusData } = require("../../fixtures/userStatus/userStatus"); const { userState } = require("../../../constants/userStatus"); -const { REQUEST_TYPE, REQUEST_STATE } = require("../../../constants/requests"); +const { REQUEST_TYPE, REQUEST_STATUS } = require("../../../constants/requests"); const { createRequest } = require("../../../models/requests"); chai.should(); @@ -1140,7 +1140,7 @@ describe("discordactions", function () { it("should add grouponboarding31D when user has an approved extension request but dealine has been passed", async function () { await createRequest({ type: REQUEST_TYPE.ONBOARDING, - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, newEndsOn: Date.now() - convertDaysToMilliseconds(2), userId: userId0, }); @@ -1154,7 +1154,7 @@ describe("discordactions", function () { it("should add grouponboarding31D when user does not have approved extension request", async function () { await createRequest({ type: REQUEST_TYPE.ONBOARDING, - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, newEndsOn: Date.now() + convertDaysToMilliseconds(2), userId: userId0, }); @@ -1168,7 +1168,7 @@ describe("discordactions", function () { it("should not add grouponboarding31D when user has approved extension request", async function () { await createRequest({ type: REQUEST_TYPE.ONBOARDING, - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, newEndsOn: Date.now() + convertDaysToMilliseconds(2), userId: userId0, }); @@ -1401,7 +1401,7 @@ describe("discordactions", function () { it("should return filtered users", async function () { await createRequest({ - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, type: REQUEST_TYPE.ONBOARDING, newEndsOn: Date.now() + convertDaysToMilliseconds(2), userId: userId0, diff --git a/test/unit/models/requests.test.ts b/test/unit/models/requests.test.ts index 954024c86..0ddb52636 100644 --- a/test/unit/models/requests.test.ts +++ b/test/unit/models/requests.test.ts @@ -8,7 +8,7 @@ import { updateOooApprovedRequests, updateOooRejectedRequests, } from "./../../fixtures/oooRequest/oooRequest"; -import { REQUEST_STATE, REQUEST_TYPE } from "../../../constants/requests"; +import { REQUEST_STATUS, REQUEST_TYPE } from "../../../constants/requests"; import userDataFixture from "./../../fixtures/user/user"; import addUser from "../../utils/addUser"; const userData = userDataFixture(); @@ -115,16 +115,16 @@ describe("models/oooRequests", () => { it("Should return a list of all the requests with specified state - APPROVED", async () => { const oooRequest: any = await createRequest(createOooStatusRequests); await updateRequest(oooRequest.id, updateOooApprovedRequests, updateOooApprovedRequests.lastModifiedBy, REQUEST_TYPE.OOO) - const query = { dev: "true", state: REQUEST_STATE.APPROVED }; + const query = { dev: "true", state: REQUEST_STATUS.APPROVED }; const oooRequestData = await getRequests(query); - expect(oooRequestData.allRequests[0].state).to.be.equal(REQUEST_STATE.APPROVED); + expect(oooRequestData.allRequests[0].state).to.be.equal(REQUEST_STATUS.APPROVED); }); it("Should return a list of all the requests with specified state - PENDING", async () => { await createRequest(createOooStatusRequests); - const query = { dev: "true", state: REQUEST_STATE.PENDING }; + const query = { dev: "true", state: REQUEST_STATUS.PENDING }; const oooRequestData = await getRequests(query); - expect(oooRequestData.allRequests[0].state).to.be.equal(REQUEST_STATE.PENDING); + expect(oooRequestData.allRequests[0].state).to.be.equal(REQUEST_STATUS.PENDING); }); it("Should return a list of all the requests by specific user ", async () => { @@ -144,7 +144,7 @@ describe("models/oooRequests", () => { }); it("Should return empty array if no data is found", async () => { - const query = { dev: "true", state: REQUEST_STATE.PENDING }; + const query = { dev: "true", state: REQUEST_STATUS.PENDING }; const oooRequestData = await getRequests(query); expect(oooRequestData).to.be.equal(null); }); diff --git a/test/unit/services/onboardingExtension.test.ts b/test/unit/services/onboardingExtension.test.ts index 35f85e64a..aabfda6b2 100644 --- a/test/unit/services/onboardingExtension.test.ts +++ b/test/unit/services/onboardingExtension.test.ts @@ -3,7 +3,7 @@ import { INVALID_REQUEST_TYPE, PENDING_REQUEST_UPDATED, REQUEST_DOES_NOT_EXIST, - REQUEST_STATE, + REQUEST_STATUS, REQUEST_TYPE, UNAUTHORIZED_TO_UPDATE_REQUEST } from "../../../constants/requests" @@ -29,7 +29,7 @@ describe("Test Onboarding Extension Service", () => { validExtensionRequest = await requestModel.add({ type: REQUEST_TYPE.ONBOARDING, oldEndsOn: Date.now() - convertDaysToMilliseconds(2), - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, userId, }) validExtensionRequestDoc = await requestModel.doc(validExtensionRequest.id).get(); @@ -55,14 +55,14 @@ describe("Test Onboarding Extension Service", () => { }); invalidTypeRequestDoc = await requestModel.doc(invalidTypeRequest.id).get(); invalidStateRequest = await requestModel.add({ - state: REQUEST_STATE.APPROVED, + state: REQUEST_STATUS.APPROVED, userId, type: REQUEST_TYPE.ONBOARDING, }) invalidStateRequestDoc = await requestModel.doc(invalidStateRequest.id).get(); invalidDeadlineRequest = await requestModel.add({ type: REQUEST_TYPE.ONBOARDING, - state: REQUEST_STATE.PENDING, + state: REQUEST_STATUS.PENDING, oldEndsOn: Date.now() + convertDaysToMilliseconds(2), userId, }) diff --git a/types/extensionRequests.d.ts b/types/extensionRequests.d.ts index fdbab7553..41b1b904b 100644 --- a/types/extensionRequests.d.ts +++ b/types/extensionRequests.d.ts @@ -1,6 +1,6 @@ import { Request, Response } from "express"; import { Boom } from "express-boom"; -import { REQUEST_STATE, REQUEST_TYPE } from "../constants/requests"; +import { REQUEST_STATUS, REQUEST_TYPE } from "../constants/requests"; import { userData } from "./global"; export type ExtensionRequest = { @@ -12,7 +12,7 @@ export type ExtensionRequest = { newEndsOn: number; message?: string; requestedBy?: string; - state?: REQUEST_STATE; + state?: REQUEST_STATUS; lastModifiedBy?: string; reason?: string; createdAt?: Timestamp; @@ -28,7 +28,7 @@ export type ExtensionRequestCreateBody = { requestedBy?: string; oldEndsOn: number; newEndsOn: number; - state: REQUEST_STATE.PENDING; + state: REQUEST_STATUS.PENDING; requestNumber?: number; assignee?: string; }; @@ -38,14 +38,14 @@ export type ExtensionRequestUpdateBody = { type?: REQUEST_TYPE.EXTENSION; id?: string; reason?: string; - state: REQUEST_STATE.APPROVED | REQUEST_STATE.REJECTED; + state: REQUEST_STATUS.APPROVED | REQUEST_STATUS.REJECTED; }; export type RequestQuery = { dev?: string; type?: string; requestedBy?: string; - state?: REQUEST_STATE.APPROVED | REQUEST_STATE.PENDING | REQUEST_STATE.REJECTED; + state?: REQUEST_STATUS.APPROVED | REQUEST_STATUS.PENDING | REQUEST_STATUS.REJECTED; id?: string; prev?: string; next?: string; diff --git a/types/onboardingExtension.d.ts b/types/onboardingExtension.d.ts index f2474a434..428a9ad3a 100644 --- a/types/onboardingExtension.d.ts +++ b/types/onboardingExtension.d.ts @@ -1,6 +1,6 @@ import { Request, Response } from "express"; import { Boom } from "express-boom"; -import { REQUEST_STATE, REQUEST_TYPE } from "../constants/requests"; +import { REQUEST_STATUS, REQUEST_TYPE } from "../constants/requests"; import { RequestQuery } from "./requests"; import { userData } from "./global"; @@ -12,7 +12,7 @@ export type OnboardingExtension = { message?: string; reason: string; requestedBy: string; - state: REQUEST_STATE; + status: REQUEST_STATUS; lastModifiedBy?: string; createdAt: Timestamp; updatedAt: Timestamp; @@ -43,7 +43,7 @@ export type OnboardingExtensionCreateRequest = Request & { export type UpdateOnboardingExtensionStateRequestBody = { type: REQUEST_TYPE.ONBOARDING; message?: string; - state: REQUEST_STATE.APPROVED | REQUEST_STATE.REJECTED; + state: REQUEST_STATUS.APPROVED | REQUEST_STATUS.REJECTED; }; export type RequestParams = { diff --git a/types/oooRequest.d.ts b/types/oooRequest.d.ts index 5655cee01..1a367ee21 100644 --- a/types/oooRequest.d.ts +++ b/types/oooRequest.d.ts @@ -1,5 +1,5 @@ import { Request, Response } from "express"; -import { REQUEST_STATE, REQUEST_TYPE } from "../constants/requests"; +import { REQUEST_STATUS, REQUEST_TYPE } from "../constants/requests"; import { userState } from "../constants/userStatus"; import { Boom } from "express-boom"; import { RequestParams, RequestQuery } from "./requests"; @@ -12,7 +12,7 @@ export type OooStatusRequest = { until?: number; message?: string; status: userState; - state?: REQUEST_STATE; + status?: REQUEST_STATUS; lastModifiedBy?: string; requestedBy?: string; createdAt?: Timestamp; @@ -25,7 +25,7 @@ export type OooStatusRequestBody = { from: number; until: number; message: string; - state: REQUEST_STATE.PENDING; + status: REQUEST_STATUS.PENDING; createdAt?: Timestamp; updatedAt?: Timestamp; }; @@ -35,11 +35,11 @@ export type OooRequestUpdateBody = { type?: REQUEST_TYPE.OOO; id?: string; reason?: string; - state: REQUEST_STATE.APPROVED | REQUEST_STATE.REJECTED; + status: REQUEST_STATUS.APPROVED | REQUEST_STATUS.REJECTED; updatedAt?: admin.firestore.Timestamp; }; export type OooRequestResponse = Response & { boom: Boom }; -export type OooRequestCreateRequest = Request & { OooStatusRequestBody , userData: userData , query: RequestQuery }; +export type OooRequestCreateRequest = Request & { OooStatusRequestBody: OooStatusRequestBody, userData: userData, query: RequestQuery }; -export type OooRequestUpdateRequest = Request & { oooRequestUpdateBody , userData: userData , query: RequestQuery , params: RequestParams }; +export type OooRequestUpdateRequest = Request & { oooRequestUpdateBody: OooRequestUpdateBody, userData: userData, query: RequestQuery, params: RequestParams }; diff --git a/types/requests.d.ts b/types/requests.d.ts index 6a903ff96..b975964b7 100644 --- a/types/requests.d.ts +++ b/types/requests.d.ts @@ -1,17 +1,17 @@ import { Request } from "express"; -import { REQUEST_STATE, REQUEST_TYPE } from "./../constants/requests"; +import { REQUEST_STATUS, REQUEST_TYPE } from "./../constants/requests"; import { userData } from "./global"; export type UpdateRequestBody = { type: REQUEST_TYPE.OOO | REQUEST_TYPE.EXTENSION; reason: string; - state: REQUEST_STATE.APPROVED | REQUEST_STATE.REJECTED; + state: REQUEST_STATUS.APPROVED | REQUEST_STATUS.REJECTED; }; export type RequestQuery = { type?: string; requestedBy?: string; - state?: REQUEST_STATE.APPROVED | REQUEST_STATE.PENDING | REQUEST_STATE.REJECTED; + state?: REQUEST_STATUS.APPROVED | REQUEST_STATUS.PENDING | REQUEST_STATUS.REJECTED; id?: string; prev?: string; next?: string; diff --git a/types/taskRequests.d.ts b/types/taskRequests.d.ts index fb9fd018b..26a1de24b 100644 --- a/types/taskRequests.d.ts +++ b/types/taskRequests.d.ts @@ -1,7 +1,7 @@ -import { REQUEST_STATE } from "./../constants/requests"; +import { REQUEST_STATUS } from "./../constants/requests"; import { Request, Response } from "express"; import { Boom } from "express-boom"; -import { REQUEST_STATE, REQUEST_TYPE } from "../constants/requests"; +import { REQUEST_STATUS, REQUEST_TYPE } from "../constants/requests"; import { TASK_REQUEST_STATUS, TASK_REQUEST_TYPE } from "../constants/taskRequests"; import { userData } from "./global"; @@ -13,7 +13,7 @@ export type TaskCreationRequest = { requestType: TASK_REQUEST_TYPE.CREATION | TASK_REQUEST_TYPE.ASSIGNMENT; userId?: string; taskId?: string; - state: REQUEST_STATE; + state: REQUEST_STATUS; requestedBy?: string; proposedStartDate: number; proposedDeadline: number; @@ -28,7 +28,7 @@ export type TaskCreationRequest = { export type TaskCreationRequestBody = { type: REQUEST_TYPE.TASK; - state: REQUEST_STATE.PENDING; + state: REQUEST_STATUS.PENDING; externalIssueUrl: string; externalIssueHtmlUrl: string; requestType: TASK_REQUEST_TYPE.CREATION; @@ -43,7 +43,7 @@ export type TaskCreationRequestUpdateBody = { lastModifiedBy?: string; type?: REQUEST_TYPE.TASK; id?: string; - state: REQUEST_STATE.APPROVED | REQUEST_STATE.REJECTED; + state: REQUEST_STATUS.APPROVED | REQUEST_STATUS.REJECTED; approvedTo?: string; }; @@ -51,7 +51,7 @@ export type RequestQuery = { dev?: string; type?: string; requestedBy?: string; - state?: REQUEST_STATE.APPROVED | REQUEST_STATE.PENDING | REQUEST_STATE.REJECTED; + state?: REQUEST_STATUS.APPROVED | REQUEST_STATUS.PENDING | REQUEST_STATUS.REJECTED; id?: string; prev?: string; next?: string; From 640635ca0b521def987097e1a8b0cb19426c91e9 Mon Sep 17 00:00:00 2001 From: ZendeAditya Date: Fri, 2 May 2025 20:11:27 +0530 Subject: [PATCH 2/5] state to status change done --- controllers/extensionRequestsv2.ts | 2 +- controllers/onboardingExtension.ts | 6 +-- controllers/oooRequests.ts | 4 +- controllers/requests.ts | 25 +++++++---- controllers/taskRequestsv2.ts | 6 +-- middlewares/validators/extensionRequestsv2.ts | 6 +-- models/requests.ts | 5 ++- routes/requests.ts | 1 + services/onboardingExtension.ts | 4 +- .../extension-requests/extensionRequests.ts | 2 +- test/fixtures/logs/requests.js | 4 +- test/fixtures/oooRequest/oooRequest.ts | 24 +++++------ test/integration/onboardingExtension.test.ts | 42 +++++++++---------- test/integration/requests.test.ts | 10 ++--- .../middlewares/extensionRequests.test.ts | 4 +- test/unit/middlewares/oooRequests.test.ts | 4 +- test/unit/middlewares/requests.test.ts | 2 +- test/unit/models/requests.test.ts | 10 ++--- .../unit/services/onboardingExtension.test.ts | 6 +-- 19 files changed, 89 insertions(+), 78 deletions(-) diff --git a/controllers/extensionRequestsv2.ts b/controllers/extensionRequestsv2.ts index 8e15ca802..fdccc9bde 100644 --- a/controllers/extensionRequestsv2.ts +++ b/controllers/extensionRequestsv2.ts @@ -117,7 +117,7 @@ export const updateTaskExtensionRequest = async (req: any, res: any) => { return res.boom.badRequest(requestResult.error); } const [logType, returnMessage] = - requestResult.state === REQUEST_STATUS.APPROVED + requestResult.status === REQUEST_STATUS.APPROVED ? [REQUEST_LOG_TYPE.REQUEST_APPROVED, REQUEST_APPROVED_SUCCESSFULLY] : [REQUEST_LOG_TYPE.REQUEST_REJECTED, REQUEST_REJECTED_SUCCESSFULLY]; diff --git a/controllers/onboardingExtension.ts b/controllers/onboardingExtension.ts index 606424606..b17aef01e 100644 --- a/controllers/onboardingExtension.ts +++ b/controllers/onboardingExtension.ts @@ -73,7 +73,7 @@ export const createOnboardingExtensionRequestController = async ( type: REQUEST_TYPE.ONBOARDING }); - if(latestExtensionRequest && latestExtensionRequest.state === REQUEST_STATUS.PENDING){ + if(latestExtensionRequest && latestExtensionRequest.status === REQUEST_STATUS.PENDING){ return res.boom.conflict(REQUEST_ALREADY_PENDING); } @@ -93,7 +93,7 @@ export const createOnboardingExtensionRequestController = async ( if(!latestExtensionRequest){ requestNumber = 1; oldEndsOn = discordJoinedDateInMillisecond + millisecondsInThirtyOneDays; - }else if(latestExtensionRequest.state === REQUEST_STATUS.REJECTED) { + }else if(latestExtensionRequest.status === REQUEST_STATUS.REJECTED) { requestNumber = latestExtensionRequest.requestNumber + 1; oldEndsOn = latestExtensionRequest.oldEndsOn; }else{ @@ -105,7 +105,7 @@ export const createOnboardingExtensionRequestController = async ( const onboardingExtension = await createRequest({ type: REQUEST_TYPE.ONBOARDING, - state: REQUEST_STATUS.PENDING, + status: REQUEST_STATUS.PENDING, userId: userId, requestedBy: username, oldEndsOn: oldEndsOn, diff --git a/controllers/oooRequests.ts b/controllers/oooRequests.ts index 26c357551..c2c6579d3 100644 --- a/controllers/oooRequests.ts +++ b/controllers/oooRequests.ts @@ -28,7 +28,7 @@ export const createOooRequestController = async (req: OooRequestCreateRequest, r } try { - const latestOooRequest:OooStatusRequest = await getRequestByKeyValues({ requestedBy: userId, type: REQUEST_TYPE.OOO , status: REQUEST_STATUS.PENDING }); + const latestOooRequest: OooStatusRequest = await getRequestByKeyValues({ requestedBy: userId, type: REQUEST_TYPE.OOO, status: REQUEST_STATUS.PENDING }); if (latestOooRequest && latestOooRequest.status === REQUEST_STATUS.PENDING) { return res.boom.badRequest(REQUEST_ALREADY_PENDING); @@ -98,7 +98,7 @@ export const updateOooRequestController = async (req: UpdateRequest, res: Custom const userFutureStatusData = { requestId, status: REQUEST_TYPE.OOO, - status: statusState.UPCOMING, + state: statusState.UPCOMING, from, endsOn: until, userId: requestedBy, diff --git a/controllers/requests.ts b/controllers/requests.ts index fd8974ea0..476e253d8 100644 --- a/controllers/requests.ts +++ b/controllers/requests.ts @@ -60,12 +60,12 @@ export const getRequestsController = async (req: any, res: any) => { return res.status(204).send(); } - if (query.id) { - return res.status(200).json({ - message: REQUEST_FETCHED_SUCCESSFULLY, - data: requests, - }); - } + if (query.id) { + return res.status(200).json({ + message: REQUEST_FETCHED_SUCCESSFULLY, + data: requests, + }); + } const { allRequests, next, prev, page } = requests; if (allRequests.length === 0) { @@ -123,11 +123,20 @@ export const getRequestsController = async (req: any, res: any) => { */ export const updateRequestBeforeAcknowledgedController = async (req: Request, res: CustomResponse) => { const type = req.body.type; - switch(type){ + switch (type) { case REQUEST_TYPE.ONBOARDING: await updateOnboardingExtensionRequestController(req as UpdateOnboardingExtensionRequest, res as OnboardingExtensionResponse); break; default: return res.boom.badRequest("Invalid request"); } -} \ No newline at end of file +} + +// Write a function here for 'state' to 'status' update. +export const updateRequestStateToStatus = async (req: Request, res: CustomResponse) => { + try { + + } catch (error) { + + } +}; \ No newline at end of file diff --git a/controllers/taskRequestsv2.ts b/controllers/taskRequestsv2.ts index 5909c101d..0c18db332 100644 --- a/controllers/taskRequestsv2.ts +++ b/controllers/taskRequestsv2.ts @@ -83,7 +83,7 @@ export const createTaskRequestController = async (req: TaskRequestRequest, res: markdownEnabled: taskRequestData.markdownEnabled, firstName: userData.first_name, lastName: userData.last_name, - state: REQUEST_STATUS.PENDING, + status: REQUEST_STATUS.PENDING, requestedAt: Date.now(), }); const updatedRequest = await createRequest(existingRequest); @@ -116,7 +116,7 @@ export const createTaskRequestController = async (req: TaskRequestRequest, res: externalIssueHtmlUrl: taskRequestData.externalIssueHtmlUrl, requestType: taskRequestData.requestType, type: taskRequestData.type, - state: REQUEST_STATUS.PENDING, + status: REQUEST_STATUS.PENDING, requestedBy: requestedBy, taskTitle: taskRequestData.taskTitle, users: [ @@ -129,7 +129,7 @@ export const createTaskRequestController = async (req: TaskRequestRequest, res: markdownEnabled: taskRequestData.markdownEnabled, firstName: userData.first_name, lastName: userData.last_name, - state: REQUEST_STATUS.PENDING, + status: REQUEST_STATUS.PENDING, requestedAt: Date.now(), }, ], diff --git a/middlewares/validators/extensionRequestsv2.ts b/middlewares/validators/extensionRequestsv2.ts index 6b37110a5..c5fa0c061 100644 --- a/middlewares/validators/extensionRequestsv2.ts +++ b/middlewares/validators/extensionRequestsv2.ts @@ -32,9 +32,9 @@ export const createExtensionRequestValidator = async ( message: joi.string().required().messages({ "string.empty": "message cannot be empty", }), - state: joi.string().valid(REQUEST_STATUS.PENDING).required().messages({ - "string.empty": "state cannot be empty", - "any.required": "state is required", + status: joi.string().valid(REQUEST_STATUS.PENDING).required().messages({ + "string.empty": "status cannot be empty", + "any.required": "status is required", }), type: joi.string().valid(REQUEST_TYPE.EXTENSION).required().messages({ "string.empty": "type cannot be empty", diff --git a/models/requests.ts b/models/requests.ts index f40f9102f..4c8bacbaa 100644 --- a/models/requests.ts +++ b/models/requests.ts @@ -88,8 +88,8 @@ export const getRequests = async (query: any) => { ...requestDoc.data(), }; } - - if(requestedBy && dev){ + + if (requestedBy && dev) { requestQuery = requestQuery.where("requestedBy", "==", requestedBy); } else if (requestedBy) { @@ -191,3 +191,4 @@ export const getRequestByKeyValues = async (keyValues: KeyValues) => { } }; +// Write a function for migration for state to status. \ No newline at end of file diff --git a/routes/requests.ts b/routes/requests.ts index 098e00a82..2e52394bf 100644 --- a/routes/requests.ts +++ b/routes/requests.ts @@ -23,5 +23,6 @@ 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); +// Make a route here -> / router.post("/migrations"); module.exports = router; diff --git a/services/onboardingExtension.ts b/services/onboardingExtension.ts index f633475b9..1c7835035 100644 --- a/services/onboardingExtension.ts +++ b/services/onboardingExtension.ts @@ -63,9 +63,9 @@ export const validateOnboardingExtensionUpdateRequest = async ( }; } - if(extensionRequest.state !== REQUEST_STATUS.PENDING){ + if(extensionRequest.status !== REQUEST_STATUS.PENDING){ await addLog(logType.PENDING_REQUEST_CAN_BE_UPDATED, - { state: extensionRequest.state }, + { status: extensionRequest.status }, { message:PENDING_REQUEST_UPDATED } ); return { diff --git a/test/fixtures/extension-requests/extensionRequests.ts b/test/fixtures/extension-requests/extensionRequests.ts index bedac40f9..5b47d28f7 100644 --- a/test/fixtures/extension-requests/extensionRequests.ts +++ b/test/fixtures/extension-requests/extensionRequests.ts @@ -7,5 +7,5 @@ export const extensionCreateObject = { newEndsOn: 1709674980000, message: "request message", type: REQUEST_TYPE.EXTENSION, - state: REQUEST_STATUS.PENDING, + status: REQUEST_STATUS.PENDING, }; diff --git a/test/fixtures/logs/requests.js b/test/fixtures/logs/requests.js index c02739524..df9d81ba5 100644 --- a/test/fixtures/logs/requests.js +++ b/test/fixtures/logs/requests.js @@ -13,7 +13,7 @@ export const requestsLogs = [ from: 1711676421000, until: 1714354821000, id: "CNExxpR1F4UPbtYIpxFP", - state: "PENDING", + status: "PENDING", type: "OOO", message: "Out of office for personal reasons.", updatedAt: 1709175282967, @@ -37,7 +37,7 @@ export const requestsLogs = [ from: 1711676421000, until: 1714354821000, id: "K7ioni8arDgRCBAWwYxp", - state: "PENDING", + status: "PENDING", type: "OOO", message: "Out of office for personal reasons.", updatedAt: 1709170848318, diff --git a/test/fixtures/oooRequest/oooRequest.ts b/test/fixtures/oooRequest/oooRequest.ts index 4f7e49a59..621e6837d 100644 --- a/test/fixtures/oooRequest/oooRequest.ts +++ b/test/fixtures/oooRequest/oooRequest.ts @@ -6,7 +6,7 @@ export const createOooStatusRequests = { from: Date.now() + 100000, until: Date.now() + 200000, message: "Out of office for personal reasons.", - state: REQUEST_STATUS.PENDING, + status: REQUEST_STATUS.PENDING, createdAt: 1234567890, updatedAt: 1234567890, }; @@ -16,7 +16,7 @@ export const validOooStatusRequests = { from: Date.now() + 100000, until: Date.now() + 200000, message: "Out of office for personal reasons.", - state: REQUEST_STATUS.PENDING, + status: REQUEST_STATUS.PENDING, }; export const invalidOooStatusRequests = { @@ -27,21 +27,21 @@ export const invalidOooStatusRequests = { }; export const updateOooApprovedRequests = { - state: REQUEST_STATUS.APPROVED, + status: REQUEST_STATUS.APPROVED, lastModifiedBy: "admin123", updatedAt: 1234567890, reason: "Approval granted.", }; export const updateOooRejectedRequests = { - state: REQUEST_STATUS.REJECTED, + status: REQUEST_STATUS.REJECTED, lastModifiedBy: "admin123", updatedAt: 1234567890, reason: "Sorry, we can't approve additional leave at this time.", }; export const validOooStatusUpdate ={ - state: REQUEST_STATUS.APPROVED, + status: REQUEST_STATUS.APPROVED, reason: "Welcome back! Enjoy the conference.", type:REQUEST_TYPE.OOO } @@ -58,7 +58,7 @@ export const createOooRequests = { from: Date.now() + 100000, until: Date.now() + 200000, message: "Out of office for personal reasons.", - state: REQUEST_STATUS.PENDING, + status: REQUEST_STATUS.PENDING, }; export const createOooRequests2 = { requestedBy: "testUser2", @@ -66,7 +66,7 @@ export const createOooRequests2 = { from: Date.now() + 100000, until: Date.now() + 200000, message: "Out of office for personal reasons.", - state: REQUEST_STATUS.PENDING, + status: REQUEST_STATUS.PENDING, }; @@ -74,7 +74,7 @@ export const oooStatusRequests = [ { id: "MpykhM8sT1Tlid4Y6Y0d", requestedBy: "user456", - state: REQUEST_STATUS.APPROVED, + status: REQUEST_STATUS.APPROVED, from: 1709525300000, until: 1709870800000, message: "Attending a work conference.", @@ -86,7 +86,7 @@ export const oooStatusRequests = [ { id: "Me8sT1Tlid4Y6Y0d", requestedBy: "user789", - state: REQUEST_STATUS.REJECTED, + status: REQUEST_STATUS.REJECTED, from: 1709603700000, until: 1709785600000, message: "Out of office for personal reasons.", @@ -99,7 +99,7 @@ export const oooStatusRequests = [ { id: "abc123", requestedBy: "user101", - state: REQUEST_STATUS.PENDING, + status: REQUEST_STATUS.PENDING, from: 1710000000000, until: 1711000000000, message: "Family vacation.", @@ -110,7 +110,7 @@ export const oooStatusRequests = [ { id: "def456", requestedBy: "user202", - state: REQUEST_STATUS.APPROVED, + status: REQUEST_STATUS.APPROVED, from: 1712000000000, until: 1713000000000, message: "Remote work due to personal reasons.", @@ -123,7 +123,7 @@ export const oooStatusRequests = [ export const updateOooStatusRequest = [ { - state: REQUEST_STATUS.APPROVED, + status: REQUEST_STATUS.APPROVED, lastModifiedBy: "admin123", updatedAt: 1234567890, reason: "Approval granted.", diff --git a/test/integration/onboardingExtension.test.ts b/test/integration/onboardingExtension.test.ts index 34ed1e5e6..0a0b5dbf3 100644 --- a/test/integration/onboardingExtension.test.ts +++ b/test/integration/onboardingExtension.test.ts @@ -228,7 +228,7 @@ describe("/requests Onboarding Extension", () => { it("should return 409 response when a user already has a pending request", (done)=> { createUserStatusWithState(testUserId, userStatusModel, userState.ONBOARDING); - requestsQuery.createRequest({...extensionRequest, state: REQUEST_STATUS.PENDING, userId: testUserId}); + requestsQuery.createRequest({...extensionRequest, status: REQUEST_STATUS.PENDING, userId: testUserId}); chai.request(app) .post(`${postEndpoint}?dev=true`) @@ -255,7 +255,7 @@ describe("/requests Onboarding Extension", () => { expect(res.body.message).to.equal(ONBOARDING_REQUEST_CREATED_SUCCESSFULLY); expect(res.body.data.requestNumber).to.equal(1); expect(res.body.data.reason).to.equal(body.reason); - expect(res.body.data.state).to.equal(REQUEST_STATUS.PENDING); + expect(res.body.data.status).to.equal(REQUEST_STATUS.PENDING); done(); }) }) @@ -265,7 +265,7 @@ describe("/requests Onboarding Extension", () => { const latestApprovedExtension = await requestsQuery.createRequest({ ...extensionRequest, userId: testUserId, - state: REQUEST_STATUS.APPROVED, + status: REQUEST_STATUS.APPROVED, newEndsOn: Date.now() + 2*24*60*60*1000, oldEndsOn: Date.now() - 24*60*60*1000, }); @@ -279,7 +279,7 @@ describe("/requests Onboarding Extension", () => { expect(res.body.message).to.equal(ONBOARDING_REQUEST_CREATED_SUCCESSFULLY); expect(res.body.data.requestNumber).to.equal(2); expect(res.body.data.reason).to.equal(body.reason); - expect(res.body.data.state).to.equal(REQUEST_STATUS.PENDING); + expect(res.body.data.status).to.equal(REQUEST_STATUS.PENDING); expect(res.body.data.oldEndsOn).to.equal(latestApprovedExtension.newEndsOn); expect(res.body.data.newEndsOn).to.equal(latestApprovedExtension.newEndsOn + (body.numberOfDays*24*60*60*1000)); }) @@ -289,7 +289,7 @@ describe("/requests Onboarding Extension", () => { const currentDate = Date.now(); const latestRejectedExtension = await requestsQuery.createRequest({ ...extensionRequest, - state: REQUEST_STATUS.REJECTED, + status: REQUEST_STATUS.REJECTED, userId: testUserId, newEndsOn: currentDate, oldEndsOn: currentDate - 24*60*60*1000, @@ -304,7 +304,7 @@ describe("/requests Onboarding Extension", () => { expect(res.body.message).to.equal(ONBOARDING_REQUEST_CREATED_SUCCESSFULLY); expect(res.body.data.requestNumber).to.equal(2); expect(res.body.data.reason).to.equal(body.reason);; - expect(res.body.data.state).to.equal(REQUEST_STATUS.PENDING); + expect(res.body.data.status).to.equal(REQUEST_STATUS.PENDING); expect(res.body.data.oldEndsOn).to.equal(latestRejectedExtension.oldEndsOn); expect(new Date(res.body.data.newEndsOn).toDateString()) .to.equal(new Date(currentDate + (body.numberOfDays*24*60*60*1000)).toDateString()); @@ -373,17 +373,17 @@ describe("/requests Onboarding Extension", () => { }); }); - it("should fetch onboarding extension request by state field", (done) => { - requestsQuery.createRequest({ type: REQUEST_TYPE.ONBOARDING, state: REQUEST_STATUS.APPROVED }); + it("should fetch onboarding extension request by status field", (done) => { + requestsQuery.createRequest({ type: REQUEST_TYPE.ONBOARDING, status: REQUEST_STATUS.APPROVED }); chai.request(app) - .get(`${getEndpoint}?state=APPROVED`) + .get(`${getEndpoint}?status=APPROVED`) .end((err, res) => { if (err) return done(err); expect(res.statusCode).to.equal(200); expect(res.body.message).to.equal(REQUEST_FETCHED_SUCCESSFULLY); expect(res.body.data.length).to.equal(1); expect(res.body.data[0].type).to.equal(REQUEST_TYPE.ONBOARDING); - expect(res.body.data[0].state).to.equal(REQUEST_STATUS.APPROVED); + expect(res.body.data[0].status).to.equal(REQUEST_STATUS.APPROVED); return done(); }); }); @@ -392,7 +392,7 @@ describe("/requests Onboarding Extension", () => { describe("PUT /requests", () => { const body = { type: REQUEST_TYPE.ONBOARDING, - state: REQUEST_STATUS.APPROVED, + status: REQUEST_STATUS.APPROVED, message: "test-message" }; let latestExtension: OnboardingExtension; @@ -405,18 +405,18 @@ describe("/requests Onboarding Extension", () => { beforeEach(async () => { userId = await addUser(userData[4]); latestExtension = await requestsQuery.createRequest({ - state: REQUEST_STATUS.PENDING, + status: REQUEST_STATUS.PENDING, type: REQUEST_TYPE.ONBOARDING, requestNumber: 1 }); latestApprovedExtension = await requestsQuery.createRequest({ - state: REQUEST_STATUS.APPROVED, + status: REQUEST_STATUS.APPROVED, type: REQUEST_TYPE.ONBOARDING, requestNumber: 2 }); latestRejectedExtension = await requestsQuery.createRequest({ - state: REQUEST_STATUS.REJECTED, + status: REQUEST_STATUS.REJECTED, type: REQUEST_TYPE.ONBOARDING, - requestNumber: 2 + requestNumber: 3 }); putEndpoint = `/requests/${latestExtension.id}?dev=true`; authToken = generateAuthToken({userId}); @@ -498,11 +498,11 @@ describe("/requests Onboarding Extension", () => { chai.request(app) .put(putEndpoint) .set("authorization", `Bearer ${authToken}`) - .send({...body, state: REQUEST_STATUS.PENDING}) + .send({...body, status: REQUEST_STATUS.PENDING}) .end((err, res) => { if (err) return done(err); expect(res.statusCode).to.equal(400); - expect(res.body.message).to.equal("state must be APPROVED or REJECTED"); + expect(res.body.message).to.equal("status must be APPROVED or REJECTED"); expect(res.body.error).to.equal("Bad Request"); done(); }) @@ -581,7 +581,7 @@ describe("/requests Onboarding Extension", () => { chai.request(app) .put(putEndpoint) .set("authorization", `Bearer ${authToken}`) - .send({...body, state: REQUEST_STATUS.REJECTED}) + .send({...body, status: REQUEST_STATUS.REJECTED}) .end((err, res) => { if (err) return done(err); expect(res.statusCode).to.equal(200); @@ -628,19 +628,19 @@ describe("/requests Onboarding Extension", () => { invalidUserId = await addUser(userData[0]); superUserId = await addUser(userData[4]); latestInvalidExtension = await requestsQuery.createRequest({ - state: REQUEST_STATUS.PENDING, + status: REQUEST_STATUS.PENDING, type: REQUEST_TYPE.ONBOARDING, oldEndsOn: Date.now() + convertDaysToMilliseconds(5), userId: userId, }); latestValidExtension = await requestsQuery.createRequest({ - state: REQUEST_STATUS.PENDING, + status: REQUEST_STATUS.PENDING, type: REQUEST_TYPE.ONBOARDING, oldEndsOn: Date.now() - convertDaysToMilliseconds(3), userId: userId }); latestApprovedExtension = await requestsQuery.createRequest({ - state: REQUEST_STATUS.APPROVED, + status: REQUEST_STATUS.APPROVED, type: REQUEST_TYPE.ONBOARDING, oldEndsOn: Date.now(), userId: userId diff --git a/test/integration/requests.test.ts b/test/integration/requests.test.ts index 870cca3b6..f02aceefe 100644 --- a/test/integration/requests.test.ts +++ b/test/integration/requests.test.ts @@ -176,7 +176,7 @@ describe("/requests OOO", function () { .request(app) .post("/requests") .set("cookie", `${cookieName}=${authToken}`) - .send({ ...validOooStatusRequests, state: REQUEST_STATUS.APPROVED }) + .send({ ...validOooStatusRequests, status: REQUEST_STATUS.APPROVED }) .end(function (err, res) { expect(res).to.have.status(400); expect(res.body).to.have.property("message"); @@ -390,7 +390,7 @@ describe("/requests Extension", function () { oldEndsOn: 1694736000, newEndsOn: 1709674980000, message: "Due to some reasons", - state: "PENDING" + status: "PENDING" }; const taskData = [ @@ -592,17 +592,17 @@ describe("/requests Extension", function () { let pendingExtensionRequestId: string; const approvedExtensionRequest = { - state: REQUEST_STATUS.APPROVED, + status: REQUEST_STATUS.APPROVED, type: REQUEST_TYPE.EXTENSION, }; const rejectedExtensionRequest = { - state: REQUEST_STATUS.REJECTED, + status: REQUEST_STATUS.REJECTED, type: REQUEST_TYPE.EXTENSION, }; const invalidExtensionRequest = { - state: "ACTIVE", + status: "ACTIVE", type: REQUEST_TYPE.EXTENSION, }; diff --git a/test/unit/middlewares/extensionRequests.test.ts b/test/unit/middlewares/extensionRequests.test.ts index ec1c37582..80af412f1 100644 --- a/test/unit/middlewares/extensionRequests.test.ts +++ b/test/unit/middlewares/extensionRequests.test.ts @@ -35,7 +35,7 @@ describe("Extension Request Validators", function () { it("should not validate for an invalid extension request on wrong status", async function () { const req = { - body: { ...extensionCreateObject, state: REQUEST_STATUS.APPROVED }, + body: { ...extensionCreateObject, status: REQUEST_STATUS.APPROVED }, }; const res = {}; const nextSpy = sinon.spy(); @@ -43,7 +43,7 @@ describe("Extension Request Validators", function () { 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]`); + expect(error.details[0].message).to.equal(`"status" must be [PENDING]`); } }); diff --git a/test/unit/middlewares/oooRequests.test.ts b/test/unit/middlewares/oooRequests.test.ts index 719ccaaa3..0fcc566ac 100644 --- a/test/unit/middlewares/oooRequests.test.ts +++ b/test/unit/middlewares/oooRequests.test.ts @@ -62,7 +62,7 @@ describe("OOO Status Request Validators", function () { from: null, until: null, message: "", - state: "APPROVED", + status: "APPROVED", }, }; try { @@ -73,7 +73,7 @@ describe("OOO Status Request Validators", function () { expect(error.details[0].message).to.equal(`"from" must be a number`); expect(error.details[1].message).to.equal(`"until" must be a number`); expect(error.details[2].message).to.equal("message cannot be empty"); - expect(error.details[3].message).to.equal("state must be PENDING"); + expect(error.details[3].message).to.equal("status must be PENDING"); } }); diff --git a/test/unit/middlewares/requests.test.ts b/test/unit/middlewares/requests.test.ts index f1eeefffd..8391acded 100644 --- a/test/unit/middlewares/requests.test.ts +++ b/test/unit/middlewares/requests.test.ts @@ -100,7 +100,7 @@ describe("Create Request Validators", function () { req = { query: { type: "RANDOM", - state: "RANDOM", + status: "RANDOM", }, }; res = { diff --git a/test/unit/models/requests.test.ts b/test/unit/models/requests.test.ts index 0ddb52636..ff1502dbc 100644 --- a/test/unit/models/requests.test.ts +++ b/test/unit/models/requests.test.ts @@ -54,8 +54,8 @@ describe("models/oooRequests", () => { , REQUEST_TYPE.OOO ); expect(updatedOooRequest).to.not.be.null; - expect(updatedOooRequest).to.have.property("state"); - expect(updatedOooRequest.state).to.equal(updateOooApprovedRequests.state); + expect(updatedOooRequest).to.have.property("status"); + expect(updatedOooRequest.status).to.equal(updateOooApprovedRequests.status); }); it("should throw an error if the OOO request does not exist", async () => { @@ -115,14 +115,14 @@ describe("models/oooRequests", () => { it("Should return a list of all the requests with specified state - APPROVED", async () => { const oooRequest: any = await createRequest(createOooStatusRequests); await updateRequest(oooRequest.id, updateOooApprovedRequests, updateOooApprovedRequests.lastModifiedBy, REQUEST_TYPE.OOO) - const query = { dev: "true", state: REQUEST_STATUS.APPROVED }; + const query = { dev: "true", status: REQUEST_STATUS.APPROVED }; const oooRequestData = await getRequests(query); expect(oooRequestData.allRequests[0].state).to.be.equal(REQUEST_STATUS.APPROVED); }); it("Should return a list of all the requests with specified state - PENDING", async () => { await createRequest(createOooStatusRequests); - const query = { dev: "true", state: REQUEST_STATUS.PENDING }; + const query = { dev: "true", status: REQUEST_STATUS.PENDING }; const oooRequestData = await getRequests(query); expect(oooRequestData.allRequests[0].state).to.be.equal(REQUEST_STATUS.PENDING); }); @@ -144,7 +144,7 @@ describe("models/oooRequests", () => { }); it("Should return empty array if no data is found", async () => { - const query = { dev: "true", state: REQUEST_STATUS.PENDING }; + const query = { dev: "true", status: REQUEST_STATUS.PENDING }; const oooRequestData = await getRequests(query); expect(oooRequestData).to.be.equal(null); }); diff --git a/test/unit/services/onboardingExtension.test.ts b/test/unit/services/onboardingExtension.test.ts index aabfda6b2..be167f6b7 100644 --- a/test/unit/services/onboardingExtension.test.ts +++ b/test/unit/services/onboardingExtension.test.ts @@ -29,7 +29,7 @@ describe("Test Onboarding Extension Service", () => { validExtensionRequest = await requestModel.add({ type: REQUEST_TYPE.ONBOARDING, oldEndsOn: Date.now() - convertDaysToMilliseconds(2), - state: REQUEST_STATUS.PENDING, + status: REQUEST_STATUS.PENDING, userId, }) validExtensionRequestDoc = await requestModel.doc(validExtensionRequest.id).get(); @@ -55,14 +55,14 @@ describe("Test Onboarding Extension Service", () => { }); invalidTypeRequestDoc = await requestModel.doc(invalidTypeRequest.id).get(); invalidStateRequest = await requestModel.add({ - state: REQUEST_STATUS.APPROVED, + status: REQUEST_STATUS.APPROVED, userId, type: REQUEST_TYPE.ONBOARDING, }) invalidStateRequestDoc = await requestModel.doc(invalidStateRequest.id).get(); invalidDeadlineRequest = await requestModel.add({ type: REQUEST_TYPE.ONBOARDING, - state: REQUEST_STATUS.PENDING, + status: REQUEST_STATUS.PENDING, oldEndsOn: Date.now() + convertDaysToMilliseconds(2), userId, }) From c92a7ac876c011457411d7a0d97d98a1db705c81 Mon Sep 17 00:00:00 2001 From: ZendeAditya Date: Fri, 2 May 2025 20:13:13 +0530 Subject: [PATCH 3/5] database migration script --- controllers/requests.ts | 31 ++++++++++++++++++++++---- models/requests.ts | 49 ++++++++++++++++++++++++++++++++++++++--- routes/requests.ts | 22 +++++++++--------- 3 files changed, 85 insertions(+), 17 deletions(-) diff --git a/controllers/requests.ts b/controllers/requests.ts index 476e253d8..32bd65379 100644 --- a/controllers/requests.ts +++ b/controllers/requests.ts @@ -3,7 +3,7 @@ import { REQUEST_FETCHED_SUCCESSFULLY, REQUEST_TYPE, } from "../constants/requests"; -import { getRequests } from "../models/requests"; +import { getRequests, updateRequestStateToStatus } from "../models/requests"; import { getPaginatedLink } from "../utils/helper"; import { createOooRequestController, updateOooRequestController } from "./oooRequests"; import { OooRequestCreateRequest, OooRequestResponse } from "../types/oooRequest"; @@ -130,13 +130,36 @@ export const updateRequestBeforeAcknowledgedController = async (req: Request, re default: return res.boom.badRequest("Invalid request"); } +}; +interface RequestWithParams extends Request { + params: { + id: string; + }; } -// Write a function here for 'state' to 'status' update. -export const updateRequestStateToStatus = async (req: Request, res: CustomResponse) => { +export const migrateRequestStateToStatus = async (req: RequestWithParams, res: CustomResponse) => { try { + const docId = req.params.id; + const result = await updateRequestStateToStatus(docId); + if (result.success) { + return res.status(200).json({ + success: true, + message: 'Successfully migrated state to status', + data: result.data + }); + } else { + return res.status(404).json({ + success: false, + message: result.message + }); + } } catch (error) { - + console.error('Migration error:', error); + return res.status(500).json({ + success: false, + message: 'Failed to migrate document', + error: error.message + }); } }; \ No newline at end of file diff --git a/models/requests.ts b/models/requests.ts index 4c8bacbaa..c4373afcc 100644 --- a/models/requests.ts +++ b/models/requests.ts @@ -9,7 +9,7 @@ import { } from "../constants/requests"; import { getUserId } from "../utils/users"; const SIZE = 5; - +import { FieldValue } from "firebase-admin/firestore"; export const createRequest = async (body: any) => { try { const requestBody: any = { @@ -29,7 +29,7 @@ export const createRequest = async (body: any) => { } }; -export const updateRequest = async (id: string, body: any, lastModifiedBy: string, type:string) => { +export const updateRequest = async (id: string, body: any, lastModifiedBy: string, type: string) => { try { const existingRequestDoc = await requestModel.doc(id).get(); if (!existingRequestDoc.exists) { @@ -191,4 +191,47 @@ export const getRequestByKeyValues = async (keyValues: KeyValues) => { } }; -// Write a function for migration for state to status. \ No newline at end of file +export const updateRequestStateToStatus = async (id: string) => { + const docRef = requestModel.doc(id); + + try { + return await firestore.runTransaction(async (transaction) => { + const doc = await transaction.get(docRef); + + if (!doc.exists) { + return { + success: false, + message: `Document with ID ${id} not found` + }; + } + + const data = doc.data(); + + if (data.state === undefined) { + return { + success: false, + message: 'Document does not have state field to migrate' + }; + } + + const updateData = { + status: data.state, + }; + updateData['state'] = FieldValue.delete(); + transaction.update(docRef, updateData); + + return { + success: true, + message: 'Successfully migrated document', + data: { + ...data, + status: data.state, + state: null + } + }; + }); + } catch (error) { + console.error(`Error migrating document ${id}:`, error); + throw error; + } +}; \ No newline at end of file diff --git a/routes/requests.ts b/routes/requests.ts index 2e52394bf..ca9f6bd2f 100644 --- a/routes/requests.ts +++ b/routes/requests.ts @@ -3,26 +3,28 @@ const router = express.Router(); const authorizeRoles = require("../middlewares/authorizeRoles"); const { SUPERUSER } = require("../constants/roles"); import authenticate from "../middlewares/authenticate"; -import { - createRequestsMiddleware, +import { + createRequestsMiddleware, updateRequestsMiddleware, - getRequestsMiddleware, + getRequestsMiddleware, updateRequestValidator } from "../middlewares/validators/requests"; -import { - createRequestController , - updateRequestController, +import { + createRequestController, + updateRequestController, getRequestsController, - updateRequestBeforeAcknowledgedController + updateRequestBeforeAcknowledgedController, + migrateRequestStateToStatus } from "../controllers/requests"; import { skipAuthenticateForOnboardingExtensionRequest } from "../middlewares/skipAuthenticateForOnboardingExtension"; import { verifyDiscordBot } from "../middlewares/authorizeBot"; +import { updateRequestStateToStatus } from "../models/requests"; router.get("/", getRequestsMiddleware, getRequestsController); router.post("/", skipAuthenticateForOnboardingExtensionRequest(authenticate, verifyDiscordBot), createRequestsMiddleware, createRequestController); -router.put("/:id",authenticate, authorizeRoles([SUPERUSER]), updateRequestsMiddleware, updateRequestController); +router.put("/:id", authenticate, authorizeRoles([SUPERUSER]), updateRequestsMiddleware, updateRequestController); router.patch("/:id", authenticate, updateRequestValidator, updateRequestBeforeAcknowledgedController); -// Make a route here -> / router.post("/migrations"); -module.exports = router; +router.post("/migrations", authenticate, authorizeRoles([SUPERUSER]), migrateRequestStateToStatus); +module.exports = router From db5e50c824b0c7f4508fc7259fe65680f1f24186 Mon Sep 17 00:00:00 2001 From: ZendeAditya Date: Fri, 2 May 2025 20:26:14 +0530 Subject: [PATCH 4/5] removed unwanted files --- .gitignore | 127 ----------------- config/default.js | 139 ------------------ controllers/auth.js | 340 -------------------------------------------- 3 files changed, 606 deletions(-) delete mode 100644 .gitignore delete mode 100644 config/default.js delete mode 100644 controllers/auth.js diff --git a/.gitignore b/.gitignore deleted file mode 100644 index c38e59bbf..000000000 --- a/.gitignore +++ /dev/null @@ -1,127 +0,0 @@ -# Created by .ignore support plugin (hsz.mobi) -### Node template -# Logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -.coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v2 - -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.pnp.* - -# Jetbrains IDE files -.idea - -# VSCode files -.vscode - -# Local config file -config/local.js -package-lock.json \ No newline at end of file diff --git a/config/default.js b/config/default.js deleted file mode 100644 index c3874f10e..000000000 --- a/config/default.js +++ /dev/null @@ -1,139 +0,0 @@ -/** - * Default config to be used if environment specific config for the specific key is absent - * Every config key to be added to `default.js` to keep a track of all config keys used in the project. - * Use placeholders as values wherever required. - * - * Documentation: https://github.com/lorenwest/node-config/wiki/Configuration-Files - */ -const NODE_ENV = process.env.NODE_ENV; -module.exports = { - port: 3000, - enableFileLogs: true, - enableConsoleLogs: false, - discordUnverifiedRoleId: "", - discordDeveloperRoleId: "", - discordMavenRoleId: "", - discordMissedUpdatesRoleId: "", - githubApi: { - baseUrl: "https://api.github.com", - org: "Real-Dev-Squad", - }, - - aws: { - region: "", - access_key: "", - secret_key: "", - identity_store_id: "", - }, - - githubOauth: { - clientId: "", - clientSecret: "", - }, - - googleOauth: { - clientId: "", - clientSecret: "", - }, - - emailServiceConfig: { - email: "", - password: "", - host: "", - port: "", - }, - - firestore: `{ - "type": "service_account", - "project_id": "", - "private_key_id": "", - "private_key": "-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDaHHM8SL5skUp2\\nmbHnSBbJyk60O+nsq4hV23Ii3HLhe82Rndwji5DvAXv5AFKM9h2pQ3P2iZA7LQ4+\\nJ3BbTR1HgUDEuARlLz9C/CxKOtpM/2Hh5rKZkCxEUTulpJgW4XT16+qXdamaH2xz\\n/Cuec1H43HUuTZTj58yypCOh6SIZGOv4TQtMiz2DNKOktlhBr0GyN8+bsDgmEcoR\\n7DUeC5re2kcOHGqtzRoZhEPHWRcM40IjT0IffALUlqXQ/kYrWbbyPIHfo/ojxfeR\\nbLXLvWaCK+j9k28hvOb3nC0lWS+B/LerFzMF3q4vE1MjwAj2uU2DdhZXvTe8bnYR\\ntshsmRURAgMBAAECggEACZiWl9SF/0IRrf/jWD7/VKu4VHzt1O4zkn50CjVbfxDq\\nuZFB0BUKAH/217zURPQnmCy574L8I+Res+yZiITPKNgWlMkZVWfTn1N3seDevaQ/\\nRth262Nw1SUjTA1+rQQImLDDWpxQRNIE3CIAO8mMVdgCNlIuvquyQXpQAIXArKyr\\njsP8HFKYBb515Hn4NSO9eG8u1Mp+3hTMmwjb6k33ezDh3kJo9u0FTPOSD67GuMJv\\nXHbB96+xYDBa9vCqEHCukqtmTz1Bg3Oe47X/QlJWNOxqSBZ+ikfxWSF7DPt1r+5w\\nWHHlFsUvirHEug8tsLVmKv0hkU1TicE0ktjF8X6HGQKBgQD8/z4K1AkPX1gq/9jc\\nQcErZXBI8eYZOUJywJ5Cw4fiVq1VByqgBqr9sfJdkWi8WrWkDLCEf4h4Fy/yKClh\\nW/A5qHbkC8B9LWPiYJw++8FUpUHDPkxA+Szeq5UDkJH2aumcFwGdMywUvh0sVqwv\\nbvhYDtlanQSEGAvCd+NiQgNDyQKBgQDcszQO7S0EGpABGc6bkGvAlbmHqhUV5TzA\\ndvanSFXQ3f+MhboMUuZiDwpplvvqeAKc3Y3d2Ps6OPVD+ailL8QjN9FQ1sHZeuHy\\nckUYxaenspNdP6AWQxEAzJqHdpzV3EyJvMsDmhhaNwNSJ4vuZ53mVrh+6u4TWyks\\nUko2O2mbCQKBgQD1qov+S8K4cKbWqjVUO21tzERqMKp0l8tUToHe5qtON0h8pkbX\\nuWHUkzR7czU2oQZ8U+4b2xMTOcDO7fywk2wDMPixnE+/vZGeQp218xTaMtZW1mmJ\\nNexCFG7QVVPG6i4J6bUhho0pXypI4ai1LpZsO48HlCzMb+ULYwsjYGJ3MQKBgFZt\\ns2hZB3UA9f4IXjnbr+bme5aeS82cTVNOAz/1eu3l0kr0n6xt1pz2KOy63QKwZs2J\\nkiIb9B6T6bDqF1pBP31PQaB3ychicBOjHl4aIZLxwvYUkZvGPeVjOuzrzXWO5UZX\\nceWCNiE2RA2rQQhm+ZYXxf6mAAAChjg+LaPZVn0JAoGAXBGDDjWORrbD7T7qSTim\\n8sVCMmBX385JhWZGwE1BbdS/eOpnKR4uVRKTsDS8Q0uN3sydaHv1uTQhk2eLKaWZ\\nSzda8nPVduaIiXm79YDpALDHFdjIGcTb/s0MRNLLt6sBNw0Ytma9KHg6tzpPpJwP\\n4TwE9j91+jzusl9988Eke6s=\\n-----END PRIVATE KEY-----\\n", - "client_email": "firebase-adminsdk-hqc2v@dev-rds.iam.gserviceaccount.com", - "client_id": "", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://oauth2.googleapis.com/token", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-hqc2v%40dev-rds.iam.gserviceaccount.com" - }`, - - services: { - rdsApi: { - baseUrl: "https://api.realdevsquad.com", - }, - - rdsUi: { - baseUrl: "https://realdevsquad.com", - routes: { - authRedirection: "/goto", - }, - goalAPI: { - baseUrl: "https://goals-api.realdevsquad.com", - cookieName: `goals-session-${NODE_ENV}`, - }, - }, - discordBot: { - baseUrl: "", - }, - }, - - cors: { - allowedOrigins: /(https:\/\/([a-zA-Z0-9-_]+\.)?realdevsquad\.com$)/, // Allow realdevsquad.com, *.realdevsquad.com - }, - - userToken: { - cookieName: `rds-session-${NODE_ENV}`, - cookieV2Name: `rds-session-v2-${NODE_ENV}`, - ttl: 30 * 24 * 60 * 60, // in seconds - refreshTtl: 180 * 24 * 60 * 60, // in seconds - publicKey: "", - privateKey: "", - }, - - botToken: { - botPublicKey: "", - }, - - // Cloudinary keys - cloudinary: { - cloud_name: "Cloud_name", - api_key: "API_KEY", - api_secret: "api_secret_key", - }, - - // Cloudflare - cloudflare: { - CLOUDFLARE_ZONE_ID: "Cloudflare_Zone_ID_or_ID", - CLOUDFLARE_X_AUTH_KEY: "Cloudflare_API_Auth_Key", - CLOUDFLARE_X_AUTH_EMAIL: "Cloudflare_User_Email", - }, - - rdsServerlessBot: { - rdsServerLessPrivateKey: "RDS_SERVERLESS_PRIVATE_KEY", - ttl: 60, - }, - - cronJobHandler: { - publicKey: "CRON_JOB_PUBLIC_KEY", - }, - - integrations: { - newrelic: { - appName: "RDS_API_production", - licenseKey: "", - }, - }, - - routesCacheTTL: { - "/members": 900, - }, - - Event100ms: { - APP_ACCESS_KEY: "EVENT_100MS_APP_ACCESS_KEY", - APP_SECRET: "EVENT_100MS_APP_SECRET", - }, - githubAccessToken: "GITHUB_PERSONAL_ACCESS_TOKEN", - - externalServices: { - EXTERNAL_SERVICE_PUBLIC_KEY: "EXTERNAL_SERVICE_PUBLIC_KEY", - }, -}; diff --git a/controllers/auth.js b/controllers/auth.js deleted file mode 100644 index 42168665b..000000000 --- a/controllers/auth.js +++ /dev/null @@ -1,340 +0,0 @@ -const passport = require("passport"); -const users = require("../models/users"); -const QrCodeAuthModel = require("../models/qrCodeAuth"); -const authService = require("../services/authService"); -const dataAccess = require("../services/dataAccessLayer"); -const { - SOMETHING_WENT_WRONG, - DATA_ADDED_SUCCESSFULLY, - USER_DOES_NOT_EXIST_ERROR, -} = require("../constants/errorMessages"); - -const googleAuthLogin = (req, res, next) => { - const { redirectURL } = req.query; - return passport.authenticate("google", { - scope: ["email"], - state: redirectURL, - })(req, res, next); -}; - -function handleRedirectUrl(req) { - const rdsUiUrl = new URL(config.get("services.rdsUi.baseUrl")); - let authRedirectionUrl = rdsUiUrl; - let isMobileApp = false; - let isV2FlagPresent = false; - let devMode = false; - - if ("state" in req.query) { - try { - const redirectUrl = new URL(req.query.state); - if (redirectUrl.searchParams.get("isMobileApp") === "true") { - isMobileApp = true; - redirectUrl.searchParams.delete("isMobileApp"); - } - - if (`.${redirectUrl.hostname}`.endsWith(`.${rdsUiUrl.hostname}`)) { - // Matching *.realdevsquad.com - authRedirectionUrl = redirectUrl; - devMode = Boolean(redirectUrl.searchParams.get("dev")); - } else { - logger.error(`Malicious redirect URL provided URL: ${redirectUrl}, Will redirect to RDS`); - } - if (redirectUrl.searchParams.get("v2") === "true") { - isV2FlagPresent = true; - } - } catch (error) { - logger.error("Invalid redirect URL provided", error); - } - } - return { - authRedirectionUrl, - isMobileApp, - isV2FlagPresent, - devMode, - }; -} - -const getAuthCookieOptions = () => { - const rdsUiUrl = new URL(config.get("services.rdsUi.baseUrl")); - return { - domain: rdsUiUrl.hostname, - expires: new Date(Date.now() + config.get("userToken.ttl") * 1000), - httpOnly: true, - secure: true, - sameSite: "lax", - }; -}; - -async function handleGoogleLogin(req, res, user, authRedirectionUrl) { - try { - if (!user.emails || user.emails.length === 0) { - logger.error("Google login failed: No emails found in user data"); - return res.boom.unauthorized("No email found in Google account"); - } - const primaryEmail = user.emails.find((email) => email.verified === true); - if (!primaryEmail) { - logger.error("Google login failed: No verified email found"); - return res.boom.unauthorized("No verified email found in Google account"); - } - - const userData = { - email: primaryEmail.value, - created_at: Date.now(), - updated_at: null, - }; - - const userDataFromDB = await users.fetchUser({ email: userData.email }); - - if (userDataFromDB.userExists) { - if (userDataFromDB.user.roles?.developer) { - const errorMessage = encodeURIComponent("Google login is restricted for developer role."); - const separator = authRedirectionUrl.search ? "&" : "?"; - return res.redirect(`${authRedirectionUrl}${separator}error=${errorMessage}`); - } - } - - const { userId, incompleteUserDetails } = await users.addOrUpdate(userData); - - const token = authService.generateAuthToken({ userId }); - - const cookieOptions = getAuthCookieOptions(); - - res.cookie(config.get("userToken.cookieName"), token, cookieOptions); - - if (incompleteUserDetails) { - authRedirectionUrl = "https://my.realdevsquad.com/new-signup"; - } - - return res.redirect(authRedirectionUrl); - } catch (err) { - logger.error("Unexpected error during Google login", err); - return res.boom.unauthorized("User cannot be authenticated"); - } -} - -const googleAuthCallback = (req, res, next) => { - const { authRedirectionUrl } = handleRedirectUrl(req); - return passport.authenticate("google", { session: false }, async (err, accessToken, user) => { - if (err) { - logger.error(err); - return res.boom.unauthorized("User cannot be authenticated"); - } - return await handleGoogleLogin(req, res, user, authRedirectionUrl); - })(req, res, next); -}; - -/** - * Makes authentication call to GitHub statergy - * - * @param req {Object} - Express request object - * @param res {Object} - Express response object - * @param next {Function} - Express middleware function - */ -const githubAuthLogin = (req, res, next) => { - let { sourceUtm, redirectURL } = req.query; - - const isMobileApp = sourceUtm === "rds-mobile-app"; - - if (isMobileApp) { - const newUrl = new URL(redirectURL); - newUrl.searchParams.set("isMobileApp", true); - redirectURL = newUrl.toString(); - } - return passport.authenticate("github", { - scope: ["user:email"], - state: redirectURL, - })(req, res, next); -}; - -/** - * Fetches the user info from GitHub and authenticates User - * - * @param req {Object} - Express request object - * @param res {Object} - Express response object - * @param next {Function} - Express middleware function - */ -const githubAuthCallback = (req, res, next) => { - let userData; - let { authRedirectionUrl, isMobileApp, isV2FlagPresent, devMode } = handleRedirectUrl(req); - try { - return passport.authenticate("github", { session: false }, async (err, accessToken, user) => { - if (err) { - logger.error(err); - return res.boom.unauthorized("User cannot be authenticated"); - } - userData = { - github_id: user.username, - github_display_name: user.displayName, - email: user._json.email, - github_created_at: Number(new Date(user._json.created_at).getTime()), - github_user_id: user.id, - created_at: Date.now(), - updated_at: null, - }; - - // if (!userData.email) { - // const githubBaseUrl = config.get("githubApi.baseUrl"); - // const res = await fetch(`${githubBaseUrl}/user/emails`, { - // headers: { - // Authorization: `token ${accessToken}`, - // }, - // }); - // const emails = await res.json(); - // const primaryEmails = emails.filter((item) => item.primary); - - // if (primaryEmails.length > 0) { - // userData.email = primaryEmails[0].email; - // } - // } - - const { userId, incompleteUserDetails, role } = await users.addOrUpdate(userData); - - const token = authService.generateAuthToken({ userId }); - - const cookieOptions = getAuthCookieOptions(); - // respond with a cookie - res.cookie(config.get("userToken.cookieName"), token, cookieOptions); - - /* redirectUrl woud be like https://realdevsquad.com?v2=true */ - if (isV2FlagPresent) { - const tokenV2 = authService.generateAuthToken({ userId, role }); - res.cookie(config.get("userToken.cookieV2Name"), tokenV2, cookieOptions); - } - - if (!devMode) { - // TODO: Revisit incompleteUserDetails redirect condition - if (incompleteUserDetails) authRedirectionUrl = "https://my.realdevsquad.com/new-signup"; - } - - if (isMobileApp) { - const newUrl = new URL(authRedirectionUrl); - newUrl.searchParams.set("token", token); - authRedirectionUrl = newUrl.toString(); - } - return res.redirect(authRedirectionUrl); - })(req, res, next); - } catch (err) { - logger.error(err); - return res.boom.unauthorized("User cannot be authenticated"); - } -}; - -const signout = (req, res) => { - const cookieName = config.get("userToken.cookieName"); - const rdsUiUrl = new URL(config.get("services.rdsUi.baseUrl")); - const cookieOptions = { - domain: rdsUiUrl.hostname, - httpOnly: true, - secure: true, - sameSite: "lax", - }; - res.clearCookie(cookieName, cookieOptions); - const cookieV2Name = config.get("userToken.cookieV2Name"); - res.clearCookie(cookieV2Name, cookieOptions); - return res.json({ - message: "Signout successful", - }); -}; - -/** - * Stores user-device data inside the DB for mobile auth - * - * @param req {Object} - Express request object - * @param res {Object} - Express response object - */ - -const storeUserDeviceInfo = async (req, res) => { - try { - const userJson = { - user_id: req.body.user_id, - device_info: req.body.device_info, - device_id: req.body.device_id, - authorization_status: "NOT_INIT", - }; - - const userInfoData = await dataAccess.retrieveUsers({ id: userJson.user_id }); - - if (!userInfoData.userExists) { - return res.boom.notFound(USER_DOES_NOT_EXIST_ERROR); - } - const userInfo = await QrCodeAuthModel.storeUserDeviceInfo(userJson); - - return res.status(201).json({ - ...userInfo, - message: DATA_ADDED_SUCCESSFULLY, - }); - } catch (err) { - logger.error(`Error while storing user device info : ${err}`); - return res.boom.badImplementation(SOMETHING_WENT_WRONG); - } -}; - -const updateAuthStatus = async (req, res) => { - try { - const userId = req.userData.id; - const authStatus = req.params.authorization_status; - let token; - if (authStatus === "AUTHORIZED") { - token = authService.generateAuthToken({ userId }); - } - const result = await QrCodeAuthModel.updateStatus(userId, authStatus, token); - - if (!result.userExists) { - return res.boom.notFound("Document not found!"); - } - - return res.json({ - message: `Authentication document for user ${userId} updated successfully`, - data: { ...result.data }, - }); - } catch (error) { - logger.error(`Error while fetching user: ${error}`); - return res.boom.badImplementation(SOMETHING_WENT_WRONG); - } -}; - -const fetchUserDeviceInfo = async (req, res) => { - try { - const { device_id: deviceId } = req.query; - const userDeviceInfoData = await QrCodeAuthModel.retrieveUserDeviceInfo({ deviceId }); - if (!userDeviceInfoData.userExists) { - return res.boom.notFound(`User with id ${deviceId} does not exist.`); - } - return res.json({ - message: "Authentication document retrieved successfully.", - data: { ...userDeviceInfoData.data }, - }); - } catch (error) { - logger.error(`Error while fetching user: ${error}`); - return res.boom.badImplementation(SOMETHING_WENT_WRONG); - } -}; - -const fetchDeviceDetails = async (req, res) => { - try { - const userId = req.userData.id; - const userDeviceInfoData = await QrCodeAuthModel.retrieveUserDeviceInfo({ userId }); - if (!userDeviceInfoData.userExists) { - return res.boom.notFound(`User with id ${userId} does not exist.`); - } - return res.json({ - message: "Authentication document Exists", - data: { device_info: userDeviceInfoData.data?.device_info }, - }); - } catch (error) { - logger.error(`Error while fetching user device info: ${error}`); - return res.boom.badImplementation(SOMETHING_WENT_WRONG); - } -}; - -module.exports = { - githubAuthLogin, - githubAuthCallback, - googleAuthLogin, - googleAuthCallback, - signout, - storeUserDeviceInfo, - updateAuthStatus, - fetchUserDeviceInfo, - fetchDeviceDetails, -}; From af831967e66cc002eb3841646cb9394904d53a6d Mon Sep 17 00:00:00 2001 From: ZendeAditya Date: Thu, 8 May 2025 23:59:03 +0530 Subject: [PATCH 5/5] chore: rename state to status --- .gitignore | 127 +++++++++ config/default.js | 139 ++++++++++ controllers/auth.js | 340 +++++++++++++++++++++++++ controllers/requests.ts | 2 + controllers/taskRequestsv2.ts | 1 + controllers/tasksRequests.js | 7 +- middlewares/validators/oooRequests.ts | 2 +- middlewares/validators/requests.ts | 4 +- middlewares/validators/taskRequests.ts | 4 +- test/unit/middlewares/requests.test.ts | 80 +++--- 10 files changed, 661 insertions(+), 45 deletions(-) create mode 100644 .gitignore create mode 100644 config/default.js create mode 100644 controllers/auth.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..c38e59bbf --- /dev/null +++ b/.gitignore @@ -0,0 +1,127 @@ +# Created by .ignore support plugin (hsz.mobi) +### Node template +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +.coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.pnp.* + +# Jetbrains IDE files +.idea + +# VSCode files +.vscode + +# Local config file +config/local.js +package-lock.json \ No newline at end of file diff --git a/config/default.js b/config/default.js new file mode 100644 index 000000000..c3874f10e --- /dev/null +++ b/config/default.js @@ -0,0 +1,139 @@ +/** + * Default config to be used if environment specific config for the specific key is absent + * Every config key to be added to `default.js` to keep a track of all config keys used in the project. + * Use placeholders as values wherever required. + * + * Documentation: https://github.com/lorenwest/node-config/wiki/Configuration-Files + */ +const NODE_ENV = process.env.NODE_ENV; +module.exports = { + port: 3000, + enableFileLogs: true, + enableConsoleLogs: false, + discordUnverifiedRoleId: "", + discordDeveloperRoleId: "", + discordMavenRoleId: "", + discordMissedUpdatesRoleId: "", + githubApi: { + baseUrl: "https://api.github.com", + org: "Real-Dev-Squad", + }, + + aws: { + region: "", + access_key: "", + secret_key: "", + identity_store_id: "", + }, + + githubOauth: { + clientId: "", + clientSecret: "", + }, + + googleOauth: { + clientId: "", + clientSecret: "", + }, + + emailServiceConfig: { + email: "", + password: "", + host: "", + port: "", + }, + + firestore: `{ + "type": "service_account", + "project_id": "", + "private_key_id": "", + "private_key": "-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDaHHM8SL5skUp2\\nmbHnSBbJyk60O+nsq4hV23Ii3HLhe82Rndwji5DvAXv5AFKM9h2pQ3P2iZA7LQ4+\\nJ3BbTR1HgUDEuARlLz9C/CxKOtpM/2Hh5rKZkCxEUTulpJgW4XT16+qXdamaH2xz\\n/Cuec1H43HUuTZTj58yypCOh6SIZGOv4TQtMiz2DNKOktlhBr0GyN8+bsDgmEcoR\\n7DUeC5re2kcOHGqtzRoZhEPHWRcM40IjT0IffALUlqXQ/kYrWbbyPIHfo/ojxfeR\\nbLXLvWaCK+j9k28hvOb3nC0lWS+B/LerFzMF3q4vE1MjwAj2uU2DdhZXvTe8bnYR\\ntshsmRURAgMBAAECggEACZiWl9SF/0IRrf/jWD7/VKu4VHzt1O4zkn50CjVbfxDq\\nuZFB0BUKAH/217zURPQnmCy574L8I+Res+yZiITPKNgWlMkZVWfTn1N3seDevaQ/\\nRth262Nw1SUjTA1+rQQImLDDWpxQRNIE3CIAO8mMVdgCNlIuvquyQXpQAIXArKyr\\njsP8HFKYBb515Hn4NSO9eG8u1Mp+3hTMmwjb6k33ezDh3kJo9u0FTPOSD67GuMJv\\nXHbB96+xYDBa9vCqEHCukqtmTz1Bg3Oe47X/QlJWNOxqSBZ+ikfxWSF7DPt1r+5w\\nWHHlFsUvirHEug8tsLVmKv0hkU1TicE0ktjF8X6HGQKBgQD8/z4K1AkPX1gq/9jc\\nQcErZXBI8eYZOUJywJ5Cw4fiVq1VByqgBqr9sfJdkWi8WrWkDLCEf4h4Fy/yKClh\\nW/A5qHbkC8B9LWPiYJw++8FUpUHDPkxA+Szeq5UDkJH2aumcFwGdMywUvh0sVqwv\\nbvhYDtlanQSEGAvCd+NiQgNDyQKBgQDcszQO7S0EGpABGc6bkGvAlbmHqhUV5TzA\\ndvanSFXQ3f+MhboMUuZiDwpplvvqeAKc3Y3d2Ps6OPVD+ailL8QjN9FQ1sHZeuHy\\nckUYxaenspNdP6AWQxEAzJqHdpzV3EyJvMsDmhhaNwNSJ4vuZ53mVrh+6u4TWyks\\nUko2O2mbCQKBgQD1qov+S8K4cKbWqjVUO21tzERqMKp0l8tUToHe5qtON0h8pkbX\\nuWHUkzR7czU2oQZ8U+4b2xMTOcDO7fywk2wDMPixnE+/vZGeQp218xTaMtZW1mmJ\\nNexCFG7QVVPG6i4J6bUhho0pXypI4ai1LpZsO48HlCzMb+ULYwsjYGJ3MQKBgFZt\\ns2hZB3UA9f4IXjnbr+bme5aeS82cTVNOAz/1eu3l0kr0n6xt1pz2KOy63QKwZs2J\\nkiIb9B6T6bDqF1pBP31PQaB3ychicBOjHl4aIZLxwvYUkZvGPeVjOuzrzXWO5UZX\\nceWCNiE2RA2rQQhm+ZYXxf6mAAAChjg+LaPZVn0JAoGAXBGDDjWORrbD7T7qSTim\\n8sVCMmBX385JhWZGwE1BbdS/eOpnKR4uVRKTsDS8Q0uN3sydaHv1uTQhk2eLKaWZ\\nSzda8nPVduaIiXm79YDpALDHFdjIGcTb/s0MRNLLt6sBNw0Ytma9KHg6tzpPpJwP\\n4TwE9j91+jzusl9988Eke6s=\\n-----END PRIVATE KEY-----\\n", + "client_email": "firebase-adminsdk-hqc2v@dev-rds.iam.gserviceaccount.com", + "client_id": "", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-hqc2v%40dev-rds.iam.gserviceaccount.com" + }`, + + services: { + rdsApi: { + baseUrl: "https://api.realdevsquad.com", + }, + + rdsUi: { + baseUrl: "https://realdevsquad.com", + routes: { + authRedirection: "/goto", + }, + goalAPI: { + baseUrl: "https://goals-api.realdevsquad.com", + cookieName: `goals-session-${NODE_ENV}`, + }, + }, + discordBot: { + baseUrl: "", + }, + }, + + cors: { + allowedOrigins: /(https:\/\/([a-zA-Z0-9-_]+\.)?realdevsquad\.com$)/, // Allow realdevsquad.com, *.realdevsquad.com + }, + + userToken: { + cookieName: `rds-session-${NODE_ENV}`, + cookieV2Name: `rds-session-v2-${NODE_ENV}`, + ttl: 30 * 24 * 60 * 60, // in seconds + refreshTtl: 180 * 24 * 60 * 60, // in seconds + publicKey: "", + privateKey: "", + }, + + botToken: { + botPublicKey: "", + }, + + // Cloudinary keys + cloudinary: { + cloud_name: "Cloud_name", + api_key: "API_KEY", + api_secret: "api_secret_key", + }, + + // Cloudflare + cloudflare: { + CLOUDFLARE_ZONE_ID: "Cloudflare_Zone_ID_or_ID", + CLOUDFLARE_X_AUTH_KEY: "Cloudflare_API_Auth_Key", + CLOUDFLARE_X_AUTH_EMAIL: "Cloudflare_User_Email", + }, + + rdsServerlessBot: { + rdsServerLessPrivateKey: "RDS_SERVERLESS_PRIVATE_KEY", + ttl: 60, + }, + + cronJobHandler: { + publicKey: "CRON_JOB_PUBLIC_KEY", + }, + + integrations: { + newrelic: { + appName: "RDS_API_production", + licenseKey: "", + }, + }, + + routesCacheTTL: { + "/members": 900, + }, + + Event100ms: { + APP_ACCESS_KEY: "EVENT_100MS_APP_ACCESS_KEY", + APP_SECRET: "EVENT_100MS_APP_SECRET", + }, + githubAccessToken: "GITHUB_PERSONAL_ACCESS_TOKEN", + + externalServices: { + EXTERNAL_SERVICE_PUBLIC_KEY: "EXTERNAL_SERVICE_PUBLIC_KEY", + }, +}; diff --git a/controllers/auth.js b/controllers/auth.js new file mode 100644 index 000000000..42168665b --- /dev/null +++ b/controllers/auth.js @@ -0,0 +1,340 @@ +const passport = require("passport"); +const users = require("../models/users"); +const QrCodeAuthModel = require("../models/qrCodeAuth"); +const authService = require("../services/authService"); +const dataAccess = require("../services/dataAccessLayer"); +const { + SOMETHING_WENT_WRONG, + DATA_ADDED_SUCCESSFULLY, + USER_DOES_NOT_EXIST_ERROR, +} = require("../constants/errorMessages"); + +const googleAuthLogin = (req, res, next) => { + const { redirectURL } = req.query; + return passport.authenticate("google", { + scope: ["email"], + state: redirectURL, + })(req, res, next); +}; + +function handleRedirectUrl(req) { + const rdsUiUrl = new URL(config.get("services.rdsUi.baseUrl")); + let authRedirectionUrl = rdsUiUrl; + let isMobileApp = false; + let isV2FlagPresent = false; + let devMode = false; + + if ("state" in req.query) { + try { + const redirectUrl = new URL(req.query.state); + if (redirectUrl.searchParams.get("isMobileApp") === "true") { + isMobileApp = true; + redirectUrl.searchParams.delete("isMobileApp"); + } + + if (`.${redirectUrl.hostname}`.endsWith(`.${rdsUiUrl.hostname}`)) { + // Matching *.realdevsquad.com + authRedirectionUrl = redirectUrl; + devMode = Boolean(redirectUrl.searchParams.get("dev")); + } else { + logger.error(`Malicious redirect URL provided URL: ${redirectUrl}, Will redirect to RDS`); + } + if (redirectUrl.searchParams.get("v2") === "true") { + isV2FlagPresent = true; + } + } catch (error) { + logger.error("Invalid redirect URL provided", error); + } + } + return { + authRedirectionUrl, + isMobileApp, + isV2FlagPresent, + devMode, + }; +} + +const getAuthCookieOptions = () => { + const rdsUiUrl = new URL(config.get("services.rdsUi.baseUrl")); + return { + domain: rdsUiUrl.hostname, + expires: new Date(Date.now() + config.get("userToken.ttl") * 1000), + httpOnly: true, + secure: true, + sameSite: "lax", + }; +}; + +async function handleGoogleLogin(req, res, user, authRedirectionUrl) { + try { + if (!user.emails || user.emails.length === 0) { + logger.error("Google login failed: No emails found in user data"); + return res.boom.unauthorized("No email found in Google account"); + } + const primaryEmail = user.emails.find((email) => email.verified === true); + if (!primaryEmail) { + logger.error("Google login failed: No verified email found"); + return res.boom.unauthorized("No verified email found in Google account"); + } + + const userData = { + email: primaryEmail.value, + created_at: Date.now(), + updated_at: null, + }; + + const userDataFromDB = await users.fetchUser({ email: userData.email }); + + if (userDataFromDB.userExists) { + if (userDataFromDB.user.roles?.developer) { + const errorMessage = encodeURIComponent("Google login is restricted for developer role."); + const separator = authRedirectionUrl.search ? "&" : "?"; + return res.redirect(`${authRedirectionUrl}${separator}error=${errorMessage}`); + } + } + + const { userId, incompleteUserDetails } = await users.addOrUpdate(userData); + + const token = authService.generateAuthToken({ userId }); + + const cookieOptions = getAuthCookieOptions(); + + res.cookie(config.get("userToken.cookieName"), token, cookieOptions); + + if (incompleteUserDetails) { + authRedirectionUrl = "https://my.realdevsquad.com/new-signup"; + } + + return res.redirect(authRedirectionUrl); + } catch (err) { + logger.error("Unexpected error during Google login", err); + return res.boom.unauthorized("User cannot be authenticated"); + } +} + +const googleAuthCallback = (req, res, next) => { + const { authRedirectionUrl } = handleRedirectUrl(req); + return passport.authenticate("google", { session: false }, async (err, accessToken, user) => { + if (err) { + logger.error(err); + return res.boom.unauthorized("User cannot be authenticated"); + } + return await handleGoogleLogin(req, res, user, authRedirectionUrl); + })(req, res, next); +}; + +/** + * Makes authentication call to GitHub statergy + * + * @param req {Object} - Express request object + * @param res {Object} - Express response object + * @param next {Function} - Express middleware function + */ +const githubAuthLogin = (req, res, next) => { + let { sourceUtm, redirectURL } = req.query; + + const isMobileApp = sourceUtm === "rds-mobile-app"; + + if (isMobileApp) { + const newUrl = new URL(redirectURL); + newUrl.searchParams.set("isMobileApp", true); + redirectURL = newUrl.toString(); + } + return passport.authenticate("github", { + scope: ["user:email"], + state: redirectURL, + })(req, res, next); +}; + +/** + * Fetches the user info from GitHub and authenticates User + * + * @param req {Object} - Express request object + * @param res {Object} - Express response object + * @param next {Function} - Express middleware function + */ +const githubAuthCallback = (req, res, next) => { + let userData; + let { authRedirectionUrl, isMobileApp, isV2FlagPresent, devMode } = handleRedirectUrl(req); + try { + return passport.authenticate("github", { session: false }, async (err, accessToken, user) => { + if (err) { + logger.error(err); + return res.boom.unauthorized("User cannot be authenticated"); + } + userData = { + github_id: user.username, + github_display_name: user.displayName, + email: user._json.email, + github_created_at: Number(new Date(user._json.created_at).getTime()), + github_user_id: user.id, + created_at: Date.now(), + updated_at: null, + }; + + // if (!userData.email) { + // const githubBaseUrl = config.get("githubApi.baseUrl"); + // const res = await fetch(`${githubBaseUrl}/user/emails`, { + // headers: { + // Authorization: `token ${accessToken}`, + // }, + // }); + // const emails = await res.json(); + // const primaryEmails = emails.filter((item) => item.primary); + + // if (primaryEmails.length > 0) { + // userData.email = primaryEmails[0].email; + // } + // } + + const { userId, incompleteUserDetails, role } = await users.addOrUpdate(userData); + + const token = authService.generateAuthToken({ userId }); + + const cookieOptions = getAuthCookieOptions(); + // respond with a cookie + res.cookie(config.get("userToken.cookieName"), token, cookieOptions); + + /* redirectUrl woud be like https://realdevsquad.com?v2=true */ + if (isV2FlagPresent) { + const tokenV2 = authService.generateAuthToken({ userId, role }); + res.cookie(config.get("userToken.cookieV2Name"), tokenV2, cookieOptions); + } + + if (!devMode) { + // TODO: Revisit incompleteUserDetails redirect condition + if (incompleteUserDetails) authRedirectionUrl = "https://my.realdevsquad.com/new-signup"; + } + + if (isMobileApp) { + const newUrl = new URL(authRedirectionUrl); + newUrl.searchParams.set("token", token); + authRedirectionUrl = newUrl.toString(); + } + return res.redirect(authRedirectionUrl); + })(req, res, next); + } catch (err) { + logger.error(err); + return res.boom.unauthorized("User cannot be authenticated"); + } +}; + +const signout = (req, res) => { + const cookieName = config.get("userToken.cookieName"); + const rdsUiUrl = new URL(config.get("services.rdsUi.baseUrl")); + const cookieOptions = { + domain: rdsUiUrl.hostname, + httpOnly: true, + secure: true, + sameSite: "lax", + }; + res.clearCookie(cookieName, cookieOptions); + const cookieV2Name = config.get("userToken.cookieV2Name"); + res.clearCookie(cookieV2Name, cookieOptions); + return res.json({ + message: "Signout successful", + }); +}; + +/** + * Stores user-device data inside the DB for mobile auth + * + * @param req {Object} - Express request object + * @param res {Object} - Express response object + */ + +const storeUserDeviceInfo = async (req, res) => { + try { + const userJson = { + user_id: req.body.user_id, + device_info: req.body.device_info, + device_id: req.body.device_id, + authorization_status: "NOT_INIT", + }; + + const userInfoData = await dataAccess.retrieveUsers({ id: userJson.user_id }); + + if (!userInfoData.userExists) { + return res.boom.notFound(USER_DOES_NOT_EXIST_ERROR); + } + const userInfo = await QrCodeAuthModel.storeUserDeviceInfo(userJson); + + return res.status(201).json({ + ...userInfo, + message: DATA_ADDED_SUCCESSFULLY, + }); + } catch (err) { + logger.error(`Error while storing user device info : ${err}`); + return res.boom.badImplementation(SOMETHING_WENT_WRONG); + } +}; + +const updateAuthStatus = async (req, res) => { + try { + const userId = req.userData.id; + const authStatus = req.params.authorization_status; + let token; + if (authStatus === "AUTHORIZED") { + token = authService.generateAuthToken({ userId }); + } + const result = await QrCodeAuthModel.updateStatus(userId, authStatus, token); + + if (!result.userExists) { + return res.boom.notFound("Document not found!"); + } + + return res.json({ + message: `Authentication document for user ${userId} updated successfully`, + data: { ...result.data }, + }); + } catch (error) { + logger.error(`Error while fetching user: ${error}`); + return res.boom.badImplementation(SOMETHING_WENT_WRONG); + } +}; + +const fetchUserDeviceInfo = async (req, res) => { + try { + const { device_id: deviceId } = req.query; + const userDeviceInfoData = await QrCodeAuthModel.retrieveUserDeviceInfo({ deviceId }); + if (!userDeviceInfoData.userExists) { + return res.boom.notFound(`User with id ${deviceId} does not exist.`); + } + return res.json({ + message: "Authentication document retrieved successfully.", + data: { ...userDeviceInfoData.data }, + }); + } catch (error) { + logger.error(`Error while fetching user: ${error}`); + return res.boom.badImplementation(SOMETHING_WENT_WRONG); + } +}; + +const fetchDeviceDetails = async (req, res) => { + try { + const userId = req.userData.id; + const userDeviceInfoData = await QrCodeAuthModel.retrieveUserDeviceInfo({ userId }); + if (!userDeviceInfoData.userExists) { + return res.boom.notFound(`User with id ${userId} does not exist.`); + } + return res.json({ + message: "Authentication document Exists", + data: { device_info: userDeviceInfoData.data?.device_info }, + }); + } catch (error) { + logger.error(`Error while fetching user device info: ${error}`); + return res.boom.badImplementation(SOMETHING_WENT_WRONG); + } +}; + +module.exports = { + githubAuthLogin, + githubAuthCallback, + googleAuthLogin, + googleAuthCallback, + signout, + storeUserDeviceInfo, + updateAuthStatus, + fetchUserDeviceInfo, + fetchDeviceDetails, +}; diff --git a/controllers/requests.ts b/controllers/requests.ts index 32bd65379..dcda9f28e 100644 --- a/controllers/requests.ts +++ b/controllers/requests.ts @@ -24,6 +24,7 @@ export const createRequestController = async ( res: CustomResponse ) => { const type = req.body.type; + console.log("type", type); switch (type) { case REQUEST_TYPE.OOO: return await createOooRequestController(req as OooRequestCreateRequest, res as OooRequestResponse); @@ -140,6 +141,7 @@ interface RequestWithParams extends Request { export const migrateRequestStateToStatus = async (req: RequestWithParams, res: CustomResponse) => { try { const docId = req.params.id; + console.log(docId); const result = await updateRequestStateToStatus(docId); if (result.success) { diff --git a/controllers/taskRequestsv2.ts b/controllers/taskRequestsv2.ts index 0c18db332..929b2ced0 100644 --- a/controllers/taskRequestsv2.ts +++ b/controllers/taskRequestsv2.ts @@ -24,6 +24,7 @@ export const createTaskRequestController = async (req: TaskRequestRequest, res: const userPromise: any = await fetchUser({ userId: taskRequestData.userId }); const userData: userData = userPromise.user; if (!userData.id || !userData.username) { + console.log("userData", userData); return res.boom.notFound(TASK_REQUEST_MESSAGES.USER_NOT_FOUND); } try { diff --git a/controllers/tasksRequests.js b/controllers/tasksRequests.js index 1b4487334..1d9f5a609 100644 --- a/controllers/tasksRequests.js +++ b/controllers/tasksRequests.js @@ -84,9 +84,9 @@ const addTaskRequests = async (req, res) => { if (!username) { return res.boom.badRequest("User not found"); } - if (!issueData) { - return res.boom.badRequest("Issue does not exist"); - } + // if (!issueData) { + // return res.boom.badRequest("Issue does not exist"); + // } break; } } @@ -112,7 +112,6 @@ const addTaskRequests = async (req, res) => { body: newTaskRequest.taskRequest, }; await addLog(taskRequestLog.type, taskRequestLog.meta, taskRequestLog.body); - const statusCode = newTaskRequest.isCreate ? 201 : 200; return res.status(statusCode).json({ message: "Task request successful.", diff --git a/middlewares/validators/oooRequests.ts b/middlewares/validators/oooRequests.ts index 56f2d78ba..9c348e927 100644 --- a/middlewares/validators/oooRequests.ts +++ b/middlewares/validators/oooRequests.ts @@ -31,7 +31,7 @@ export const createOooStatusRequestValidator = async ( "string.empty": "message cannot be empty", }), state: joi.string().valid(REQUEST_STATUS.PENDING).required().messages({ - "any.only": "state must be PENDING", + "any.only": "status must be PENDING", }), type: joi.string().valid(REQUEST_TYPE.OOO).required(), }); diff --git a/middlewares/validators/requests.ts b/middlewares/validators/requests.ts index f8a98abba..af9b45e7d 100644 --- a/middlewares/validators/requests.ts +++ b/middlewares/validators/requests.ts @@ -58,12 +58,12 @@ export const updateRequestsMiddleware = async ( .messages({ "string.empty": "reason cannot be empty", }), - state: joi + status: joi .string() .valid(REQUEST_STATUS.APPROVED, REQUEST_STATUS.REJECTED) .required() .messages({ - "any.only": "state must be APPROVED or REJECTED", + "any.only": "status must be APPROVED or REJECTED", }), type: joi.string().valid(REQUEST_TYPE.OOO, REQUEST_TYPE.EXTENSION, REQUEST_TYPE.ONBOARDING).required(), message: joi.string().optional() diff --git a/middlewares/validators/taskRequests.ts b/middlewares/validators/taskRequests.ts index f1527f1ed..85d47f400 100644 --- a/middlewares/validators/taskRequests.ts +++ b/middlewares/validators/taskRequests.ts @@ -37,8 +37,8 @@ export const createTaskRequestValidator = async ( "any.required": "type is required", }), state: joi.string().valid(REQUEST_STATUS.PENDING).required().messages({ - "string.empty": "state cannot be empty", - "any.required": "state is required", + "string.empty": "status cannot be empty", + "any.required": "status is required", }), proposedStartDate: joi.number().required().messages({ "number.base": "proposedStartDate must be a number", diff --git a/test/unit/middlewares/requests.test.ts b/test/unit/middlewares/requests.test.ts index 8391acded..c2c56b0a9 100644 --- a/test/unit/middlewares/requests.test.ts +++ b/test/unit/middlewares/requests.test.ts @@ -36,7 +36,11 @@ describe("Create Request Validators", function () { req = { body: validOooStatusRequests, }; - res = {}; + res = { + boom: { + badRequest: sinon.spy(), + } + }; await createRequestsMiddleware(req as OooRequestCreateRequest, res as OooRequestResponse, nextSpy); expect(nextSpy.calledOnce).to.equal(true); }); @@ -65,7 +69,11 @@ describe("Create Request Validators", function () { dev: "true", }, }; - res = {}; + res = { + boom: { + badRequest: sinon.spy(), + } + }; await updateRequestsMiddleware(req as any, res as any, nextSpy); expect(nextSpy.calledOnce).to.equal(true); }); @@ -117,26 +125,26 @@ describe("Create Request Validators", function () { describe("updateRequestValidator", () => { let req, res, next: sinon.SinonSpy; - + beforeEach(() => { - next = sinon.spy(); - res = { boom: { badRequest: sinon.spy() } } + next = sinon.spy(); + res = { boom: { badRequest: sinon.spy() } } }); afterEach(() => { - sinon.restore(); + sinon.restore(); }) it("should call next for correct type", async () => { - req = { body: { type: REQUEST_TYPE.ONBOARDING, newEndsOn: Date.now() + convertDaysToMilliseconds(2) } }; - await updateRequestValidator(req, res, next); - expect(next.calledOnce).to.be.true; + req = { body: { type: REQUEST_TYPE.ONBOARDING, newEndsOn: Date.now() + convertDaysToMilliseconds(2) } }; + await updateRequestValidator(req, res, next); + expect(next.calledOnce).to.be.true; }) it("should not call next for incorrect type", async () => { - req = { body: { type: REQUEST_TYPE.OOO } }; - await updateRequestValidator(req, res, next); - expect(next.notCalled).to.be.true; + req = { body: { type: REQUEST_TYPE.OOO } }; + await updateRequestValidator(req, res, next); + expect(next.notCalled).to.be.true; }) }) @@ -144,47 +152,47 @@ describe("updateOnboardingExtensionRequestValidator", () => { let req, res, next: sinon.SinonSpy; beforeEach(() => { - next = sinon.spy(); - res = { boom: { badRequest: sinon.spy() } }; + next = sinon.spy(); + res = { boom: { badRequest: sinon.spy() } }; }); afterEach(() => { - sinon.restore(); + sinon.restore(); }) it("should not call next for incorrect type ", async () => { - req = { - body: { - type: REQUEST_TYPE.OOO, - newEndsOn: Date.now() + convertDaysToMilliseconds(3) - } + req = { + body: { + type: REQUEST_TYPE.OOO, + newEndsOn: Date.now() + convertDaysToMilliseconds(3) } + } - await updateOnboardingExtensionRequestValidator(req, res, next); - expect(next.notCalled).to.be.true; + await updateOnboardingExtensionRequestValidator(req, res, next); + expect(next.notCalled).to.be.true; }); it("should not call next for incorrect newEndsOn ", async () => { - req = { - body: { - type: REQUEST_TYPE.ONBOARDING, - newEndsOn: Date.now() - convertDaysToMilliseconds(1) - } + req = { + body: { + type: REQUEST_TYPE.ONBOARDING, + newEndsOn: Date.now() - convertDaysToMilliseconds(1) } + } - await updateOnboardingExtensionRequestValidator(req, res, next); - expect(next.notCalled).to.be.true; + await updateOnboardingExtensionRequestValidator(req, res, next); + expect(next.notCalled).to.be.true; }); it("should call next for successful validaton", async () => { - req = { - body: { - type: REQUEST_TYPE.ONBOARDING, - newEndsOn: Date.now() + convertDaysToMilliseconds(3) - } + req = { + body: { + type: REQUEST_TYPE.ONBOARDING, + newEndsOn: Date.now() + convertDaysToMilliseconds(3) } + } - await updateOnboardingExtensionRequestValidator(req, res, next); - expect(next.calledOnce).to.be.true; + await updateOnboardingExtensionRequestValidator(req, res, next); + expect(next.calledOnce).to.be.true; }); }) \ No newline at end of file