diff --git a/constants/userStatus.ts b/constants/userStatus.ts index 1c37cf4f28..f37e95734d 100644 --- a/constants/userStatus.ts +++ b/constants/userStatus.ts @@ -1,10 +1,14 @@ -const userState = { +export const userState = { ACTIVE: "ACTIVE", IDLE: "IDLE", OOO: "OOO", ONBOARDING: "ONBOARDING", }; -const CANCEL_OOO = "cancelOoo"; +export const statusState = { + UPCOMING: "UPCOMING", + APPLIED: "APPLIED", + NOT_APPLIED: "NOT_APPLIED", +}; -module.exports = { userState, CANCEL_OOO }; +export const CANCEL_OOO = "cancelOoo"; diff --git a/controllers/requests.ts b/controllers/requests.ts index a463315f80..a5d0708c44 100644 --- a/controllers/requests.ts +++ b/controllers/requests.ts @@ -9,7 +9,11 @@ import { REQUEST_STATE, LOG_ACTION, REQUEST_LOG_TYPE, + REQUEST_TYPE, } from "../constants/requests"; +import { statusState } from "../constants/userStatus"; +import {addFutureStatus} from "../models/userStatus"; +import { createUserFutureStatus } from "../models/userFutureStatus"; import { createRequest, getRequests, updateRequest } from "../models/requests"; import { addLog } from "../models/logs"; import { getPaginatedLink } from "../utils/helper"; @@ -94,6 +98,24 @@ export const updateRequestController = async (req: any, res: any) => { body: requestResult, }; await addLog(requestLog.type, requestLog.meta, requestLog.body); + if (requestResult.state === REQUEST_STATE.APPROVED) { + const requestData = await getRequests({ id: requestId }); + + if (requestData) { + const { from, until, requestedBy, message } = requestData as any; + const userFutureStatusData = { + requestId, + status: REQUEST_TYPE.OOO, + state: statusState.UPCOMING, + from, + endsOn: until, + userId: requestedBy, + message, + }; + await createUserFutureStatus(userFutureStatusData); + await addFutureStatus(userFutureStatusData); + } + } return res.status(201).json({ message: returnMessage, data: { diff --git a/models/requests.ts b/models/requests.ts index 939e9cfc2a..c34f360d04 100644 --- a/models/requests.ts +++ b/models/requests.ts @@ -91,12 +91,8 @@ export const getRequests = async (query: any) => { return null; } return { - allRequests: [ - { - id: requestDoc.id, - ...requestDoc.data(), - }, - ], + id: requestDoc.id, + ...requestDoc.data(), }; } diff --git a/models/userFutureStatus.ts b/models/userFutureStatus.ts new file mode 100644 index 0000000000..b1e003ba47 --- /dev/null +++ b/models/userFutureStatus.ts @@ -0,0 +1,56 @@ +import firestore from "../utils/firestore"; +const userFutureStatusModel = firestore.collection("userFutureStatus"); +import { UserFutureStatusType } from "../types/userFutureStatus"; +import * as admin from "firebase-admin"; + +/** + * Function to create user future status + * @param body: UserFutureStatusType + * @returns UserFutureStatusType + */ +export const createUserFutureStatus = async (body: UserFutureStatusType) => { + try { + const statusBody: UserFutureStatusType = { + createdAt: Date.now(), + ...body, + }; + const resultDoc = await userFutureStatusModel.add(statusBody); + return { + id: resultDoc.id, + ...body, + }; + } catch (error) { + logger.error("Error while creating user future status", error); + throw error; + } +}; + +/** + * Function to get user future status + * @param: id: string, status: string, state: string + * @returns Array of user future status + **/ +export const getUserFutureStatus = async (userId: string, status: string, state: string) => { + try { + let resultArray = []; + let query: admin.firestore.Query = userFutureStatusModel; + + if (userId) { + query = query.where("userId", "==", userId); + } + if (status) { + query = query.where("status", "==", status); + } + if (state) { + query = query.where("state", "==", state); + } + const resultDoc = await query.get(); + resultDoc.forEach((doc) => { + resultArray.push({ id: doc.id, ...doc.data() }); + }); + return resultArray; + } catch (error) { + logger.error("Error while fetching user future status", error); + throw error; + } +}; \ No newline at end of file diff --git a/models/userStatus.js b/models/userStatus.js index 75599971ec..4f7288d7c6 100644 --- a/models/userStatus.js +++ b/models/userStatus.js @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ const { Forbidden, NotFound } = require("http-errors"); const admin = require("firebase-admin"); const firestore = require("../utils/firestore"); @@ -673,6 +674,36 @@ const cancelOooStatus = async (userId) => { } }; +const addFutureStatus = async (futureStatusData) => { + try { + const userStatusDocs = await userStatusModel.where("userId", "==", futureStatusData.userId).limit(1).get(); + const [userStatusDoc] = userStatusDocs.docs; + let docId; + let userStatusData; + + if (userStatusDoc) { + docId = userStatusDoc.id; + userStatusData = userStatusDoc.data(); + } else { + const newUserStatusRef = userStatusModel.doc(); + await newUserStatusRef.set({ userId: futureStatusData.userId }); + docId = newUserStatusRef.id; + userStatusData = { userId: futureStatusData.userId }; + } + + delete futureStatusData.userId; + const newStatusData = { + ...userStatusData, + futureStatus: futureStatusData, + }; + await userStatusModel.doc(docId).update(newStatusData); + return { id: docId, userStatusExists: true, data: newStatusData }; + } catch (error) { + logger.error(`error in updating User Status Document ${error}`); + throw error; + } +}; + module.exports = { deleteUserStatus, getUserStatus, @@ -686,4 +717,5 @@ module.exports = { getTaskBasedUsersStatus, cancelOooStatus, getGroupRole, + addFutureStatus, }; diff --git a/package.json b/package.json index 64149101e6..fe6fb2cdae 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "eslint-plugin-standard": "^4.1.0", "firebase-tools": "^13.4.0", "mocha": "^10.3.0", - "nock": "~13.2.9", + "nock": "~13.5.0", "nodemon": "^3.1.0", "nyc": "^15.1.0", "pre-commit": "^1.2.2", diff --git a/test/fixtures/userFutureStatus/userFutureStatusData.ts b/test/fixtures/userFutureStatus/userFutureStatusData.ts new file mode 100644 index 0000000000..50408284a2 --- /dev/null +++ b/test/fixtures/userFutureStatus/userFutureStatusData.ts @@ -0,0 +1,12 @@ +import { REQUEST_TYPE } from "../../../constants/requests"; +import { statusState } from "../../../constants/userStatus"; + +export const userFutureStatusData = { + requestId: "randomId", + status: REQUEST_TYPE.OOO, + state: statusState.UPCOMING, + from: 1712277700000, + endsOn: 1712277700000, + userId: "randomUserId", + message: "I am out of office", +}; diff --git a/test/unit/models/requests.test.ts b/test/unit/models/requests.test.ts index ed26bcafd5..eab4ed9f07 100644 --- a/test/unit/models/requests.test.ts +++ b/test/unit/models/requests.test.ts @@ -101,9 +101,7 @@ describe("models/oooRequests", () => { const oooRequest = await createRequest(createOooRequests2); const query = { id: oooRequest.id, dev: "true" }; const oooRequestData: any = await getRequests(query); - expect(oooRequestData).to.have.property("allRequests"); - expect(oooRequestData.allRequests[0].id).to.be.equal(oooRequest.id); - expect(oooRequestData.allRequests).to.have.lengthOf(1); + expect(oooRequestData.id).to.be.equal(oooRequest.id); }); it("Should return null if the request with the specified ID does not exist", async () => { diff --git a/test/unit/models/userFutureStatus.test.ts b/test/unit/models/userFutureStatus.test.ts new file mode 100644 index 0000000000..42a089b201 --- /dev/null +++ b/test/unit/models/userFutureStatus.test.ts @@ -0,0 +1,34 @@ +import { createUserFutureStatus, getUserFutureStatus } from "../../../models/userFutureStatus"; +import { expect } from "chai"; +import cleanDb from "../../utils/cleanDb"; +import { UserFutureStatusType } from "../../../types/userFutureStatus"; +import { userFutureStatusData } from "../../fixtures/userFutureStatus/userFutureStatusData"; + +describe("models/userFutureStatus", () => { + afterEach(async () => { + await cleanDb(); + }); + + describe("createUserFutureStatus ", () => { + it("should successfully create a new user future status", async () => { + const userFutureStatus = await createUserFutureStatus(userFutureStatusData as UserFutureStatusType); + expect(userFutureStatus).to.not.be.null; + expect(userFutureStatus).to.have.property("id"); + expect(userFutureStatus).to.have.property("userId"); + }); + }); + + describe("getUserFutureStatus", () => { + it("should successfully get user future status", async () => { + await createUserFutureStatus(userFutureStatusData as UserFutureStatusType); + const userFutureStatus = await getUserFutureStatus( + userFutureStatusData.userId, + userFutureStatusData.status, + userFutureStatusData.state + ); + expect(userFutureStatus).to.not.be.null; + expect(userFutureStatus).to.be.an("array"); + expect(userFutureStatus).to.have.length(1); + }); + }); +}); diff --git a/test/unit/models/userStatus.js b/test/unit/models/userStatus.js index 57fdb19538..d537ce829e 100644 --- a/test/unit/models/userStatus.js +++ b/test/unit/models/userStatus.js @@ -1,3 +1,4 @@ +import { userFutureStatusData } from "../../fixtures/userFutureStatus/userFutureStatusData"; const chai = require("chai"); const sinon = require("sinon"); const { NotFound, Forbidden } = require("http-errors"); @@ -5,7 +6,7 @@ const { expect } = chai; const firestore = require("../../../utils/firestore"); const userStatusModel = firestore.collection("usersStatus"); const tasksModel = firestore.collection("tasks"); -const { cancelOooStatus } = require("../../../models/userStatus"); +const { cancelOooStatus, addFutureStatus } = require("../../../models/userStatus"); const cleanDb = require("../../utils/cleanDb"); const addUser = require("../../utils/addUser"); const { userState } = require("../../../constants/userStatus"); @@ -79,4 +80,10 @@ describe("tasks", function () { expect(err.message).to.be.equal("Task not found"); }); }); + + it("Should add future status to the User", async function () { + const response = await addFutureStatus(userFutureStatusData); + expect(response.userStatusExists).to.equal(true); + expect(response.data.futureStatus.state).to.equal("UPCOMING"); + }); }); diff --git a/types/oooRequest.d.ts b/types/oooRequest.d.ts index 74cc84c097..46cf45c270 100644 --- a/types/oooRequest.d.ts +++ b/types/oooRequest.d.ts @@ -1,9 +1,10 @@ import { Request, Response } from "express"; -import { REQUEST_STATE, REQUEST_TYPE } from "../constants/request"; +import { REQUEST_STATE, REQUEST_TYPE } from "../constants/requests"; import { userState } from "../constants/userStatus"; import { Boom } from "express-boom"; export type OooStatusRequest = { + id: string; type: REQUEST_TYPE.OOO; from: number; until?: number; @@ -11,6 +12,7 @@ export type OooStatusRequest = { status: userState; state?: REQUEST_STATE; lastModifiedBy?: string; + requestedBy?: string; createdAt?: Timestamp; updatedAt?: Timestamp; reason?: string; @@ -40,7 +42,7 @@ export type userData= { }; export type RequestQuery = { - dev: string; + dev?: string; type?: string; requestedBy?: string; state?: REQUEST_STATE.APPROVED | REQUEST_STATE.PENDING | REQUEST_STATE.REJECTED; diff --git a/types/userFutureStatus.d.ts b/types/userFutureStatus.d.ts new file mode 100644 index 0000000000..271a7ddde9 --- /dev/null +++ b/types/userFutureStatus.d.ts @@ -0,0 +1,13 @@ +import { userState, statusState } from "../constants/userStatus"; + +export type UserFutureStatusType = { + id?: string; + requestId?: string; + status: userState.OOO | userState.IDLE | userState.ACTIVE; + state: statusState.UPCOMING | statusState.APPLIED | statusState.NOT_APPLIED; + from: number; + endsOn?: number; + userId: string; + message?: string; + createdAt?: number; +}; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 7f54267e1d..65535396c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5589,14 +5589,13 @@ nise@^5.1.5: just-extend "^6.2.0" path-to-regexp "^6.2.1" -nock@~13.2.9: - version "13.2.9" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.2.9.tgz#4faf6c28175d36044da4cfa68e33e5a15086ad4c" - integrity sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA== +nock@~13.5.0: + version "13.5.4" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.5.4.tgz#8918f0addc70a63736170fef7106a9721e0dc479" + integrity sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw== dependencies: debug "^4.1.0" json-stringify-safe "^5.0.1" - lodash "^4.17.21" propagate "^2.0.0" node-abi@^3.3.0: @@ -7026,7 +7025,16 @@ streamsearch@0.1.2: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" integrity sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -7095,7 +7103,14 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -7877,7 +7892,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -7895,6 +7910,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"