diff --git a/constants/logs.ts b/constants/logs.ts index 86ad01a9c..3ebfdf790 100644 --- a/constants/logs.ts +++ b/constants/logs.ts @@ -22,6 +22,8 @@ export const logType = { INVALID_REQUEST_TYPE: "INVALID_REQUEST_TYPE", PENDING_REQUEST_CAN_BE_UPDATED: "PENDING_REQUEST_CAN_BE_UPDATED", INVALID_REQUEST_DEADLINE: "INVALID_REQUEST_DEADLINE", + USER_STATUS_NOT_FOUND: "USER_STATUS_NOT_FOUND", + OOO_STATUS_FOUND: "OOO_STATUS_FOUND", ...REQUEST_LOG_TYPE, }; diff --git a/constants/requests.ts b/constants/requests.ts index 8a9635d2d..498cab9d6 100644 --- a/constants/requests.ts +++ b/constants/requests.ts @@ -25,6 +25,7 @@ export const REQUEST_LOG_TYPE = { REQUEST_BLOCKED: "REQUEST_BLOCKED", REQUEST_CANCELLED: "REQUEST_CANCELLED", REQUEST_UPDATED: "REQUEST_UPDATED", + PENDING_REQUEST_FOUND: "PENDING_REQUEST_FOUND", }; export const REQUEST_CREATED_SUCCESSFULLY = "Request created successfully"; @@ -41,6 +42,9 @@ export const ERROR_WHILE_UPDATING_REQUEST = "Error while updating request"; export const REQUEST_DOES_NOT_EXIST = "Request does not exist"; export const REQUEST_ALREADY_PENDING = "Request already exists please wait for approval or rejection"; +export const UNAUTHORIZED_TO_CREATE_OOO_REQUEST = "Unauthorized to create OOO request"; +export const USER_STATUS_NOT_FOUND = "User status not found"; +export const OOO_STATUS_ALREADY_EXIST = "Your status is already OOO. Please cancel OOO to raise new one"; export const TASK_REQUEST_MESSAGES = { NOT_AUTHORIZED_TO_CREATE_REQUEST: "Not authorized to create the request", diff --git a/controllers/oooRequests.ts b/controllers/oooRequests.ts index a3dea406b..36d2baeab 100644 --- a/controllers/oooRequests.ts +++ b/controllers/oooRequests.ts @@ -3,57 +3,85 @@ import { LOG_ACTION, REQUEST_CREATED_SUCCESSFULLY, ERROR_WHILE_CREATING_REQUEST, - REQUEST_ALREADY_PENDING, REQUEST_STATE, REQUEST_TYPE, ERROR_WHILE_UPDATING_REQUEST, REQUEST_APPROVED_SUCCESSFULLY, REQUEST_REJECTED_SUCCESSFULLY, + UNAUTHORIZED_TO_CREATE_OOO_REQUEST, + REQUEST_ALREADY_PENDING, + USER_STATUS_NOT_FOUND, + OOO_STATUS_ALREADY_EXIST, } from "../constants/requests"; import { statusState } from "../constants/userStatus"; +import { logType } from "../constants/logs"; import { addLog } from "../models/logs"; -import { createRequest, getRequestByKeyValues, getRequests, updateRequest } from "../models/requests"; +import { getRequestByKeyValues, getRequests, updateRequest } from "../models/requests"; import { createUserFutureStatus } from "../models/userFutureStatus"; -import { addFutureStatus } from "../models/userStatus"; +import { getUserStatus, addFutureStatus } from "../models/userStatus"; +import { createOooRequest, validateUserStatus } from "../services/oooRequest"; import { CustomResponse } from "../typeDefinitions/global"; -import { OooRequestCreateRequest, OooStatusRequest } from "../types/oooRequest"; +import { OooRequestCreateRequest, OooRequestResponse, OooStatusRequest } from "../types/oooRequest"; import { UpdateRequest } from "../types/requests"; -export const createOooRequestController = async (req: OooRequestCreateRequest, res: CustomResponse) => { +/** + * Controller to handle the creation of OOO requests. + * + * This function processes the request to create an OOO request, + * validates the user status, checks existing requests, + * and stores the new request in the database with logging. + * + * @param {OooRequestCreateRequest} req - The Express request object containing the body with OOO details. + * @param {CustomResponse} res - The Express response object used to send back the response. + * @returns {Promise} Resolves to a response with the success or an error message. + */ +export const createOooRequestController = async ( + req: OooRequestCreateRequest, + res: OooRequestResponse +): Promise => { + const requestBody = req.body; - const userId = req?.userData?.id; + const { id: userId, username } = req.userData; + const isUserPartOfDiscord = req.userData.roles.in_discord; + const dev = req.query.dev === "true"; - if (!userId) { - return res.boom.unauthorized(); + if (!dev) return res.boom.notImplemented("Feature not implemented"); + + if (!isUserPartOfDiscord) { + return res.boom.forbidden(UNAUTHORIZED_TO_CREATE_OOO_REQUEST); } try { - const latestOooRequest:OooStatusRequest = await getRequestByKeyValues({ requestedBy: userId, type: REQUEST_TYPE.OOO , state: REQUEST_STATE.PENDING }); + const userStatus = await getUserStatus(userId); + const validationResponse = await validateUserStatus(userId, userStatus); - if (latestOooRequest && latestOooRequest.state === REQUEST_STATE.PENDING) { - return res.boom.badRequest(REQUEST_ALREADY_PENDING); + if (validationResponse) { + if (validationResponse.error === USER_STATUS_NOT_FOUND) { + return res.boom.notFound(validationResponse.error); + } + if (validationResponse.error === OOO_STATUS_ALREADY_EXIST) { + return res.boom.forbidden(validationResponse.error); + } } - const requestResult = await createRequest({ requestedBy: userId, ...requestBody }); + const latestOooRequest: OooStatusRequest = await getRequestByKeyValues({ + userId, + type: REQUEST_TYPE.OOO, + status: REQUEST_STATE.PENDING, + }); - const requestLog = { - type: REQUEST_LOG_TYPE.REQUEST_CREATED, - meta: { - requestId: requestResult.id, - action: LOG_ACTION.CREATE, - userId: userId, - createdAt: Date.now(), - }, - body: requestResult, - }; - await addLog(requestLog.type, requestLog.meta, requestLog.body); + if (latestOooRequest) { + await addLog(logType.PENDING_REQUEST_FOUND, + { userId, oooRequestId: latestOooRequest.id }, + { message: REQUEST_ALREADY_PENDING } + ); + return res.boom.conflict(REQUEST_ALREADY_PENDING); + } + + await createOooRequest(requestBody, username, userId); return res.status(201).json({ message: REQUEST_CREATED_SUCCESSFULLY, - data: { - id: requestResult.id, - ...requestResult, - }, }); } catch (err) { logger.error(ERROR_WHILE_CREATING_REQUEST, err); diff --git a/middlewares/validators/oooRequests.ts b/middlewares/validators/oooRequests.ts index 1a90aea6e..ab73929f1 100644 --- a/middlewares/validators/oooRequests.ts +++ b/middlewares/validators/oooRequests.ts @@ -26,14 +26,14 @@ export const createOooStatusRequestValidator = async ( "number.min": "until date must be greater than or equal to from date", }) .required(), - message: joi.string().required().messages({ - "any.required": "message is required", - "string.empty": "message cannot be empty", + reason: joi.string().required().messages({ + "any.required": "reason is required", + "string.empty": "reason cannot be empty", }), - state: joi.string().valid(REQUEST_STATE.PENDING).required().messages({ - "any.only": "state must be PENDING", + type: joi.string().valid(REQUEST_TYPE.OOO).required().messages({ + "string.empty": "type cannot be empty", + "any.required": "type is required", }), - type: joi.string().valid(REQUEST_TYPE.OOO).required(), }); await schema.validateAsync(req.body, { abortEarly: false }); diff --git a/models/discordactions.js b/models/discordactions.js index 3ba9ee658..dabff6f2c 100644 --- a/models/discordactions.js +++ b/models/discordactions.js @@ -1019,7 +1019,7 @@ const getMissedProgressUpdatesUsers = async (options = {}) => { usersMap.set(taskAssignee, { tasksCount: 1, latestProgressCount: dateGap + 1, - isActive: false, + isOOO: false, }); } const updateTasksIdMap = async () => { @@ -1038,10 +1038,7 @@ const getMissedProgressUpdatesUsers = async (options = {}) => { const userIdChunks = chunks(Array.from(usersMap.keys()), FIRESTORE_IN_CLAUSE_SIZE); const userStatusSnapshotPromise = userIdChunks.map( async (userIdList) => - await userStatusModel - .where("currentStatus.state", "==", userState.ACTIVE) - .where("userId", "in", userIdList) - .get() + await userStatusModel.where("currentStatus.state", "==", userState.OOO).where("userId", "in", userIdList).get() ); const userDetailsPromise = userIdChunks.map( async (userIdList) => @@ -1055,7 +1052,7 @@ const getMissedProgressUpdatesUsers = async (options = {}) => { userStatusChunks.forEach((userStatusList) => userStatusList.forEach((doc) => { - usersMap.get(doc.data().userId).isActive = true; + usersMap.get(doc.data().userId).isOOO = true; }) ); @@ -1097,7 +1094,7 @@ const getMissedProgressUpdatesUsers = async (options = {}) => { const isDiscordMember = !!discordUserData; const shouldAddRole = userData.latestProgressCount === 0 && - userData.isActive && + !userData.isOOO && isDiscordMember && discordUserData.isDeveloper && !discordUserData.isMaven && diff --git a/services/oooRequest.ts b/services/oooRequest.ts new file mode 100644 index 000000000..2e2f35ab7 --- /dev/null +++ b/services/oooRequest.ts @@ -0,0 +1,95 @@ +import { logType } from "../constants/logs"; +import { + LOG_ACTION, + OOO_STATUS_ALREADY_EXIST, + REQUEST_LOG_TYPE, + REQUEST_STATE, + USER_STATUS_NOT_FOUND, +} from "../constants/requests"; +import { userState } from "../constants/userStatus"; +import { createRequest } from "../models/requests"; +import { OooStatusRequest, OooStatusRequestBody } from "../types/oooRequest"; +import { UserStatus } from "../types/userStatus"; +import { addLog } from "./logService"; + +/** + * Validates the user status. + * + * @param {string} userId - The unique identifier of the user. + * @param {UserStatus} userStatus - The status object of the user. + * @throws {Error} Throws an error if an issue occurs during validation. + */ +export const validateUserStatus = async ( + userId: string, + userStatus: UserStatus +) => { + try { + + if (!userStatus.userStatusExists) { + await addLog(logType.USER_STATUS_NOT_FOUND, { userId }, { message: USER_STATUS_NOT_FOUND }); + return { + error: USER_STATUS_NOT_FOUND + }; + } + + if (userStatus.data.currentStatus.state === userState.OOO) { + await addLog(logType.OOO_STATUS_FOUND, + { userId, userStatus: userState.OOO }, + { message: OOO_STATUS_ALREADY_EXIST } + ); + return { + error: OOO_STATUS_ALREADY_EXIST + }; + } + } catch (error) { + logger.error("Error while validating OOO create request", error); + throw error; + } +} + +/** + * Create an OOO request for a user. + * + * @param {OooStatusRequestBody} body - The request body containing OOO details. + * @param {string} username - The username of the person creating the request. + * @param {string} userId - The unique identifier of the user. + * @returns {Promise} The created OOO request. + * @throws {Error} Throws an error if an issue occurs during validation. + */ +export const createOooRequest = async ( + body: OooStatusRequestBody, + username: string, + userId: string +) => { + try { + const request: OooStatusRequest = await createRequest({ + from: body.from, + until: body.until, + type: body.type, + requestedBy: username, + userId, + reason: body.reason, + comment: null, + status: REQUEST_STATE.PENDING, + lastModifiedBy: null, + }); + + const requestLog = { + type: REQUEST_LOG_TYPE.REQUEST_CREATED, + meta: { + requestId: request.id, + action: LOG_ACTION.CREATE, + userId, + createdAt: Date.now(), + }, + body: request, + }; + + await addLog(requestLog.type, requestLog.meta, requestLog.body); + + return request; + } catch (error) { + logger.error("Error while creating OOO request", error); + throw error; + } +} diff --git a/test/fixtures/oooRequest/oooRequest.ts b/test/fixtures/oooRequest/oooRequest.ts index 8b6e8bcdf..30b72d2a0 100644 --- a/test/fixtures/oooRequest/oooRequest.ts +++ b/test/fixtures/oooRequest/oooRequest.ts @@ -1,4 +1,5 @@ import { REQUEST_STATE, REQUEST_TYPE } from "../../../constants/requests"; +import { UserStatus } from "../../../types/userStatus"; export const createOooStatusRequests = { type: "OOO", @@ -15,8 +16,36 @@ export const validOooStatusRequests = { type: "OOO", from: Date.now() + 1 * 24 * 60 * 60 * 1000, until: Date.now() + 5 * 24 * 60 * 60 * 1000, - message: "Out of office for personal reasons.", - state: REQUEST_STATE.PENDING, + reason: "Out of office for personal reasons." +}; + +export const createdOOORequest = { + id: "Js7JnT6uRBLjGvSJM5X5", + type: validOooStatusRequests.type, + from: validOooStatusRequests.from, + until: validOooStatusRequests.until, + reason: validOooStatusRequests.reason, + status: "PENDING", + lastModifiedBy: null, + requestedBy: "suraj-maity-1", + userId: "jCqqOYCnm93mcmaYuSsQ", + comment: null +}; + +export const validUserCurrentStatus = { + from: Date.now(), + until: Date.now() + 1 * 24 * 60 * 60 * 1000, + message: "", + state: "ACTIVE", + updatedAt: Date.now(), +}; + +export const testUserStatus: UserStatus = { + id: "wcl0ZLsnngKUNZY9GkCo", + data: { + currentStatus: validUserCurrentStatus + }, + userStatusExists: true }; export const invalidOooStatusRequests = { @@ -129,3 +158,18 @@ export const updateOooStatusRequest = [ reason: "Approval granted.", }, ]; + +export const createOooRequests3 = { + from: Date.now() + 100000, + until: Date.now() + 200000, + type: "OOO", + requestedBy: "suraj-maity-1", + reason: "Out of office for personal emergency.", + status: REQUEST_STATE.PENDING +}; + +export const acknowledgeOooRequest = { + type: REQUEST_TYPE.OOO, + status: REQUEST_STATE.APPROVED, + comment: "OOO request approved as it's emergency." +}; \ No newline at end of file diff --git a/test/integration/requests.test.ts b/test/integration/requests.test.ts index 45e31fd06..2e83acf18 100644 --- a/test/integration/requests.test.ts +++ b/test/integration/requests.test.ts @@ -7,6 +7,7 @@ import app from "../../server"; import cleanDb from "../utils/cleanDb"; import authService from "../../services/authService"; import userDataFixture from "../fixtures/user/user"; +import sinon from "sinon"; const cookieName = config.get("userToken.cookieName"); import addUser from "../utils/addUser"; import { @@ -14,6 +15,8 @@ import { validOooStatusRequests, validOooStatusUpdate, createOooRequests2, + acknowledgeOooRequest, + createOooRequests3, } from "../fixtures/oooRequest/oooRequest"; import { createRequest, updateRequest } from "../../models/requests"; import { @@ -26,9 +29,18 @@ import { REQUEST_ALREADY_PENDING, REQUEST_REJECTED_SUCCESSFULLY, REQUEST_ALREADY_REJECTED, + INVALID_REQUEST_TYPE, + // UNAUTHORIZED_TO_ACKNOWLEDGE_OOO_REQUEST, + UNAUTHORIZED_TO_CREATE_OOO_REQUEST, + USER_STATUS_NOT_FOUND, + OOO_STATUS_ALREADY_EXIST, } from "../../constants/requests"; import { updateTask } from "../../models/tasks"; import { validTaskAssignmentRequest, validTaskCreqtionRequest } from "../fixtures/taskRequests/taskRequests"; +import { deleteUserStatus, updateUserStatus } from "../../models/userStatus"; +import * as requestsQuery from "../../models/requests"; +import { userState } from "../../constants/userStatus"; +import * as logUtils from "../../services/logService"; const userData = userDataFixture(); chai.use(chaiHttp); @@ -41,12 +53,19 @@ let approvedOooRequestId: string; let oooRequestData: any; let oooRequestData2: any; let testUserId: string; +let testSuperUserId: string; +let testArchivedUserId: string; describe("/requests OOO", function () { + + const requestsEndpoint: string = "/requests?dev=true"; + beforeEach(async function () { - const userIdPromises = [addUser(userData[16]), addUser(userData[4])]; - const [userId, superUserId] = await Promise.all(userIdPromises); + const userIdPromises = [addUser(userData[16]), addUser(userData[4]), addUser(userData[18])]; + const [userId, superUserId, archivedUserId] = await Promise.all(userIdPromises); testUserId = userId; + testSuperUserId = superUserId; + testArchivedUserId = archivedUserId; oooRequestData = { ...createOooRequests, requestedBy: userId }; oooRequestData2 = { ...createOooRequests2, requestedBy: superUserId }; @@ -69,134 +88,419 @@ describe("/requests OOO", function () { }); afterEach(async function () { + sinon.restore(); await cleanDb(); }); describe("POST /requests", function () { + beforeEach(async function () { const userIdPromises = [addUser(userData[16])]; const [userId] = await Promise.all(userIdPromises); authToken = authService.generateAuthToken({ userId }); + + const testUserStatus = { + currentStatus: { + state: userState.ACTIVE + } + }; + + await updateUserStatus(userId, testUserStatus); }); afterEach(async function () { await cleanDb(); }); + it("should return 501 and 'Feature not implemented' message when dev is false", function (done) { + chai + .request(app) + .post("/requests?dev=false") + .set("cookie", `${cookieName}=${authToken}`) + .send(validOooStatusRequests) + .end(function (err, res) { + if (err) { + return done(err); + } + expect(res.statusCode).to.equal(501); + expect(res.body.message).to.equal("Feature not implemented"); + done(); + }); + }); + it("should return 401 if user is not logged in", function (done) { chai .request(app) - .post("/requests") + .post(requestsEndpoint) .send(validOooStatusRequests) .end(function (err, res) { expect(res).to.have.status(401); + expect(res.body.error).to.equal("Unauthorized"); + expect(res.body.message).to.equal("Unauthenticated User"); done(); }); }); - it("should create a new request", function (done) { + it("should return 403 if user is not part of discord", function (done) { + const authTokenForArchivedUserId = authService.generateAuthToken( + { userId: testArchivedUserId } + ); chai .request(app) - .post("/requests") - .set("cookie", `${cookieName}=${authToken}`) + .post(requestsEndpoint) + .set("cookie", `${cookieName}=${authTokenForArchivedUserId}`) .send(validOooStatusRequests) .end(function (err, res) { - expect(res).to.have.status(201); - expect(res.body).to.have.property("message"); - expect(res.body.message).to.equal(REQUEST_CREATED_SUCCESSFULLY); + expect(res).to.have.status(403); + expect(res.body.error).to.equal("Forbidden"); + expect(res.body.message).to.equal(UNAUTHORIZED_TO_CREATE_OOO_REQUEST); done(); }); }); - it("should return 400, if already created request is created again", async function () { - await chai - .request(app) - .post("/requests") - .set("cookie", `${cookieName}=${authToken}`) - .send(validOooStatusRequests); - const response = await chai - .request(app) - .post("/requests") + it("should return 500 response when creating OOO request fails", function (done) { + sinon.stub(requestsQuery, "createRequest") + .throws("Error while creating OOO request"); + chai.request(app) + .post(requestsEndpoint) .set("cookie", `${cookieName}=${authToken}`) - .send(validOooStatusRequests); - expect(response).to.have.status(400); - expect(response.body).to.have.property("message"); - expect(response.body.message).to.equal(REQUEST_ALREADY_PENDING); + .send(validOooStatusRequests) + .end(function (err, res) { + if (err) return done(err); + expect(res.statusCode).to.equal(500); + expect(res.body.message).to.equal("An internal server error occurred"); + done(); + }); }); - it("should create a new request and have all the required fields in the response", function (done) { + it("should create a new request when dev is true", function (done) { chai .request(app) - .post("/requests") + .post(requestsEndpoint) .set("cookie", `${cookieName}=${authToken}`) .send(validOooStatusRequests) - .end(function (err, res) { + .end(async function (err, res) { + if (err) return done(err); expect(res).to.have.status(201); expect(res.body).to.have.property("message"); - expect(Object.keys(res.body.data)).to.have.lengthOf(9); - 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.message).to.equal(REQUEST_CREATED_SUCCESSFULLY); + expect(res.body).to.not.have.property("data"); + + await requestsQuery.getRequestByKeyValues({ + userId: testUserId, + type: REQUEST_TYPE.OOO, + status: REQUEST_STATE.PENDING + }).then((request) => { + expect(request).to.not.be.null; + expect(request.reason).to.equal(validOooStatusRequests.reason); + done(); + }).catch(done); + }); + }); + + it("should return error if invalid type is passed", function (done) { + const type = "ACTIVE"; + chai + .request(app) + .post(requestsEndpoint) + .set("cookie", `${cookieName}=${authToken}`) + .send({ ...validOooStatusRequests, type }) + .end(function (err, res) { + expect(res).to.have.status(400); + expect(res.body).to.have.property("message"); + expect(res.body.message).to.equal(`Invalid request type: ${type}`); done(); }); }); - it("should create a new request", function (done) { + it("should return 400 when until date is smalller than from date in request body", function (done) { chai .request(app) - .post("/requests") + .post(requestsEndpoint) .set("cookie", `${cookieName}=${authToken}`) - .send(validOooStatusRequests) + .send({...validOooStatusRequests, until: Date.now()}) .end(function (err, res) { - expect(res).to.have.status(201); + if (err) return done(err); + expect(res).to.have.status(400); expect(res.body).to.have.property("message"); - expect(res.body.message).to.equal(REQUEST_CREATED_SUCCESSFULLY); + expect(res.body.message).to.equal("until date must be greater than or equal to from date"); done(); }); }); - it("should return error if invalid type is passed", function (done) { - const type = "ACTIVE"; + it("should return 400 when from date is less than today's date in request body", function (done) { chai .request(app) - .post("/requests") + .post(requestsEndpoint) .set("cookie", `${cookieName}=${authToken}`) - .send({ ...validOooStatusRequests, type }) + .send({...validOooStatusRequests, from: Date.now() - 1 * 24 * 60 * 60 * 1000 }) .end(function (err, res) { + if (err) return done(err); expect(res).to.have.status(400); expect(res.body).to.have.property("message"); - expect(res.body.message).to.equal(`Invalid request type: ${type}`); + expect(res.body.message).to.equal("from date must be greater than or equal to Today's date"); done(); }); }); - it("should return error if message is not present in body", function (done) { + it("should return 400 when reason field is missing in request body", function (done) { chai .request(app) - .post("/requests") + .post(requestsEndpoint) .set("cookie", `${cookieName}=${authToken}`) - .send(_.omit(validOooStatusRequests, "message")) + .send(_.omit(validOooStatusRequests, "reason")) .end(function (err, res) { + if (err) return done(err); expect(res).to.have.status(400); expect(res.body).to.have.property("message"); - expect(res.body.message).to.equal("message is required"); + expect(res.body.message).to.equal("reason is required"); done(); }); }); - it("should return error if state in the body is not PENDING", function (done) { + it("should return 400 with error when status field is included in request body", function (done) { chai .request(app) - .post("/requests") + .post(requestsEndpoint) .set("cookie", `${cookieName}=${authToken}`) - .send({ ...validOooStatusRequests, state: REQUEST_STATE.APPROVED }) + .send({ ...validOooStatusRequests, status: REQUEST_STATE.APPROVED }) .end(function (err, res) { + if (err) return done(err); expect(res).to.have.status(400); expect(res.body).to.have.property("message"); - expect(res.body.message).to.equal("state must be PENDING"); + expect(res.body.message).to.equal(`"status" is not allowed`); + done(); + }); + }); + + it("should return 404 with error when user status not found", async function () { + await deleteUserStatus(testUserId); + const response = await chai + .request(app) + .post(requestsEndpoint) + .set("cookie", `${cookieName}=${authToken}`) + .send(validOooStatusRequests); + + expect(response).to.have.status(404); + expect(response.body).to.have.property("message"); + expect(response.body.message).to.equal(USER_STATUS_NOT_FOUND); + }); + + it("should return 403 with error when user status is already OOO", async function () { + const testOOOUserStatus = { + currentStatus: { + state: userState.OOO + } + }; + await updateUserStatus(testUserId, testOOOUserStatus); + const response = await chai + .request(app) + .post(requestsEndpoint) + .set("cookie", `${cookieName}=${authToken}`) + .send(validOooStatusRequests); + + expect(response).to.have.status(403); + expect(response.body).to.have.property("message"); + expect(response.body.message).to.equal(OOO_STATUS_ALREADY_EXIST); + }); + + it("should return 409 with error when user already have pending OOO request", async function () { + await chai + .request(app) + .post(requestsEndpoint) + .set("cookie", `${cookieName}=${authToken}`) + .send(validOooStatusRequests); + const response = await chai + .request(app) + .post(requestsEndpoint) + .set("cookie", `${cookieName}=${authToken}`) + .send(validOooStatusRequests); + + expect(response).to.have.status(409); + expect(response.body).to.have.property("message"); + expect(response.body.message).to.equal(REQUEST_ALREADY_PENDING); + }); + }); + + describe.skip("PATCH /requests/:id", function () { + let testOooRequest; + let onboardingRequest; + let approvedOooRequest; + let rejectedOooRequest; + + beforeEach(async function () { + + oooRequestData = { ...createOooRequests3, userId: testUserId }; + testOooRequest = await createRequest(oooRequestData); + + onboardingRequest = await createRequest({ + type: REQUEST_TYPE.ONBOARDING, + numberOfDays: 5, + reason: "This is the reason", + userId: testUserId, + }); + + const pendingOooRequest1 = await createRequest(oooRequestData); + approvedOooRequest = await updateRequest(pendingOooRequest1.id, { status: REQUEST_STATE.APPROVED }, testSuperUserId, REQUEST_TYPE.OOO); + + const pendingOooRequest2 = await createRequest(oooRequestData); + rejectedOooRequest = await updateRequest(pendingOooRequest2.id, { status: REQUEST_STATE.REJECTED }, testSuperUserId, REQUEST_TYPE.OOO); + }); + + it("should return 401 if user is not logged in", function (done) { + chai + .request(app) + .patch(`/requests/${testOooRequest.id}?dev=true`) + .send(acknowledgeOooRequest) + .end(function (err, res) { + expect(res).to.have.status(401); + expect(res.body.error).to.equal("Unauthorized"); + expect(res.body.message).to.equal("Unauthenticated User"); + done(); + }); + }); + + it("should return 501 and 'Feature not implemented' message when dev is false", function (done) { + chai + .request(app) + .patch(`/requests/${testOooRequest.id}?dev=false`) + .set("cookie", `${cookieName}=${superUserToken}`) + .send(acknowledgeOooRequest) + .end(function (err, res) { + if (err) { + return done(err); + } + expect(res.statusCode).to.equal(501); + expect(res.body.message).to.equal("Feature not implemented"); + done(); + }); + }); + + it("should return 404 if request does not exist", function (done) { + chai + .request(app) + .patch(`/requests/11111111111111?dev=true`) + .set("cookie", `${cookieName}=${superUserToken}`) + .send(acknowledgeOooRequest) + .end(function (err, res) { + if (err) { + return done(err); + } + expect(res.statusCode).to.equal(404); + expect(res.body.message).to.equal(REQUEST_DOES_NOT_EXIST); + done(); + }); + }); + + it("should return 403 if user does not have super user permission", function (done) { + chai + .request(app) + .patch(`/requests/${testOooRequest.id}?dev=true`) + .set("cookie", `${cookieName}=${authToken}`) + .send(acknowledgeOooRequest) + .end(function (err, res) { + if (err) { + return done(err); + } + expect(res.statusCode).to.equal(403); + // expect(res.body.message).to.equal(UNAUTHORIZED_TO_ACKNOWLEDGE_OOO_REQUEST); + done(); + }); + }); + + it("should return 409 if OOO request is already approved", function (done) { + chai + .request(app) + .patch(`/requests/${approvedOooRequest.id}?dev=true`) + .set("cookie", `${cookieName}=${superUserToken}`) + .send(acknowledgeOooRequest) + .end(function (err, res) { + if (err) { + return done(err); + } + expect(res.statusCode).to.equal(409); + expect(res.body.message).to.equal(REQUEST_ALREADY_APPROVED); + done(); + }); + }); + + it("should return 409 if OOO request is already rejected", function (done) { + chai + .request(app) + .patch(`/requests/${rejectedOooRequest.id}?dev=true`) + .set("cookie", `${cookieName}=${superUserToken}`) + .send(acknowledgeOooRequest) + .end(function (err, res) { + if (err) { + return done(err); + } + expect(res.statusCode).to.equal(409); + expect(res.body.message).to.equal(REQUEST_ALREADY_REJECTED); + done(); + }); + }); + + it("should return 400 when the request type for the given ID is not 'OOO'", function (done) { + chai + .request(app) + .patch(`/requests/${onboardingRequest.id}?dev=true`) + .set("cookie", `${cookieName}=${superUserToken}`) + .send(acknowledgeOooRequest) + .end(function (err, res) { + if (err) { + return done(err); + } + expect(res.statusCode).to.equal(400); + expect(res.body.message).to.equal(INVALID_REQUEST_TYPE); + done(); + }); + }); + + it("should approve OOO request when dev is true", function (done) { + chai + .request(app) + .patch(`/requests/${testOooRequest.id}?dev=true`) + .set("cookie", `${cookieName}=${superUserToken}`) + .send(acknowledgeOooRequest) + .end(function (err, res) { + if (err) { + return done(err); + } + expect(res.statusCode).to.equal(200); + expect(res.body.message).to.equal(REQUEST_APPROVED_SUCCESSFULLY); + done(); + }); + }); + + it("should reject OOO request when dev is true", function (done) { + chai + .request(app) + .patch(`/requests/${testOooRequest.id}?dev=true`) + .set("cookie", `${cookieName}=${superUserToken}`) + .send({...acknowledgeOooRequest, status: REQUEST_STATE.REJECTED}) + .end(function (err, res) { + if (err) { + return done(err); + } + expect(res.statusCode).to.equal(200); + expect(res.body.message).to.equal(REQUEST_REJECTED_SUCCESSFULLY); + done(); + }); + }); + + it("should return 500 response for unexpected error", function (done) { + sinon.stub(logUtils, "addLog").throws("Error"); + chai + .request(app) + .patch(`/requests/${testOooRequest.id}?dev=true`) + .set("cookie", `${cookieName}=${superUserToken}`) + .send(acknowledgeOooRequest) + .end(function (err, res) { + if (err) return done(err); + expect(res.statusCode).to.equal(500); + expect(res.body.error).to.equal("Internal Server Error"); + expect(res.body.message).to.equal("An internal server error occurred"); done(); }); }); @@ -827,4 +1131,4 @@ describe("/requests Task", function () { }); }); }); -}); +}); \ No newline at end of file diff --git a/test/integration/tasks.test.js b/test/integration/tasks.test.js index 1d384337d..5d61d48f7 100644 --- a/test/integration/tasks.test.js +++ b/test/integration/tasks.test.js @@ -1813,39 +1813,45 @@ describe("Tasks", function () { }); describe("GET /tasks/users", function () { - let activeUserWithProgressUpdates; + let activeUserWithMissedProgressUpdates; let idleUser; let userNotInDiscord; let jwtToken; let getDiscordMembersStub; + let oooUserWithMissedUpdates; + let activeUserWithProgressUpdates; beforeEach(async function () { await cleanDb(); idleUser = { ...userData[9], discordId: getDiscordMembers[0].user.id }; - activeUserWithProgressUpdates = { ...userData[10], discordId: getDiscordMembers[1].user.id }; - const activeUserWithNoUpdates = { ...userData[0], discordId: getDiscordMembers[2].user.id }; + activeUserWithMissedProgressUpdates = { ...userData[10], discordId: getDiscordMembers[1].user.id }; + activeUserWithProgressUpdates = { ...userData[0], discordId: getDiscordMembers[2].user.id }; userNotInDiscord = { ...userData[4], discordId: "Not in discord" }; + oooUserWithMissedUpdates = { ...userData[1], discordId: getDiscordMembers[3].user.id }; + const { idleStatus: idleUserStatus, activeStatus: activeUserStatus, userStatusDataForOooState: oooUserStatus, } = userStatusData; const userIdList = await Promise.all([ - await addUser(idleUser), // idle user with no task progress updates - await addUser(activeUserWithProgressUpdates), // active user with task progress updates - await addUser(activeUserWithNoUpdates), // active user with no task progress updates - await addUser(userNotInDiscord), // OOO user with + await addUser(idleUser), + await addUser(activeUserWithMissedProgressUpdates), + await addUser(activeUserWithProgressUpdates), + await addUser(userNotInDiscord), + await addUser(oooUserWithMissedUpdates), ]); await Promise.all([ await userStatusModel.updateUserStatus(userIdList[0], idleUserStatus), await userStatusModel.updateUserStatus(userIdList[1], activeUserStatus), await userStatusModel.updateUserStatus(userIdList[2], activeUserStatus), await userStatusModel.updateUserStatus(userIdList[3], oooUserStatus), + await userStatusModel.updateUserStatus(userIdList[4], oooUserStatus), ]); const tasksPromise = []; - for (let index = 0; index < 4; index++) { + for (let index = 0; index < 5; index++) { const task = tasksData[index]; const validTask = { ...task, @@ -1889,14 +1895,15 @@ describe("Tasks", function () { .get("/tasks/users/discord") .query({ q: `status:${tasksUsersStatus.MISSED_UPDATES}` }) .set("Authorization", `Bearer ${jwtToken}`); - expect(response.body).to.be.deep.equal({ - message: "Discord details of users with status missed updates fetched successfully", - data: { - usersToAddRole: [activeUserWithProgressUpdates.discordId], - tasks: 4, - missedUpdatesTasks: 3, - }, - }); + + expect(response.body.message).to.equal( + "Discord details of users with status missed updates fetched successfully" + ); + expect(response.body.data.tasks).to.equal(5); + expect(response.body.data.missedUpdatesTasks).to.equal(4); + expect(response.body.data.usersToAddRole.includes(activeUserWithMissedProgressUpdates.discordId)).to.equal(true); + expect(response.body.data.usersToAddRole.includes(idleUser.discordId)).to.equal(true); + expect(response.body.data.usersToAddRole.includes(oooUserWithMissedUpdates.discordId)).to.equal(false); expect(response.status).to.be.equal(200); }); @@ -1905,7 +1912,7 @@ describe("Tasks", function () { .request(app) .get("/tasks/users/discord") .query({ - size: 5, + size: 6, q: `status:${tasksUsersStatus.MISSED_UPDATES} -weekday:sun -weekday:mon -weekday:tue -weekday:wed -weekday:thu -weekday:fri -date:231423432 -days-count:4`, }) .set("Authorization", `Bearer ${jwtToken}`); @@ -1913,7 +1920,7 @@ describe("Tasks", function () { message: "Discord details of users with status missed updates fetched successfully", data: { usersToAddRole: [], - tasks: 4, + tasks: 5, missedUpdatesTasks: 0, }, }); diff --git a/test/unit/middlewares/oooRequests.test.ts b/test/unit/middlewares/oooRequests.test.ts index 719ccaaa3..11272e860 100644 --- a/test/unit/middlewares/oooRequests.test.ts +++ b/test/unit/middlewares/oooRequests.test.ts @@ -4,8 +4,10 @@ const { expect } = chai; import { createOooStatusRequestValidator, + // acknowledgeOOORequestsValidator, } from "./../../../middlewares/validators/oooRequests"; -import { validOooStatusRequests, validOooStatusUpdate } from "../../fixtures/oooRequest/oooRequest"; +import { acknowledgeOooRequest, validOooStatusRequests, validOooStatusUpdate } from "../../fixtures/oooRequest/oooRequest"; +import _ from "lodash"; describe("OOO Status Request Validators", function () { let req: any; @@ -42,15 +44,15 @@ describe("OOO Status Request Validators", function () { } }); - it("should not validate for an invalid request on empty msg", async function () { + it("should not validate when reason is empty and type is invalid", async function () { req = { - body: { ...validOooStatusRequests, message: "", type: "ACTIVE" }, + body: { ...validOooStatusRequests, reason: "", type: "ACTIVE" }, }; try { await createOooStatusRequestValidator(req as any, res as any, nextSpy); } catch (error) { expect(error).to.be.an.instanceOf(Error); - expect(error.details[0].message).to.equal(`message cannot be empty`); + expect(error.details[0].message).to.equal(`reason cannot be empty`); expect(error.details[1].message).to.equal(`"type" must be [OOO]`); } }); @@ -58,11 +60,10 @@ describe("OOO Status Request Validators", function () { it("should not validate for an invalid request if all invalid values", async function () { req = { body: { - type: "OOO", + type: "ABC", from: null, until: null, - message: "", - state: "APPROVED", + reason: "", }, }; try { @@ -72,8 +73,8 @@ describe("OOO Status Request Validators", function () { expect(error.details.length).to.equal(4); 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[2].message).to.equal('reason cannot be empty'); + expect(error.details[3].message).to.equal('"type" must be [OOO]'); } }); @@ -89,4 +90,42 @@ describe("OOO Status Request Validators", function () { } }); }); + + describe.skip("acknowledgeOOORequestsValidator", function () { + it("should not validate for an invalid request for invalid request type", async function () { + req = { + body: { ...acknowledgeOooRequest, type: "XYZ"} + }; + + // await acknowledgeOOORequestsValidator(req, res, nextSpy); + expect(nextSpy.notCalled).to.be.true; + }); + + it("should not validate for an invalid request if status is incorrect", async function () { + req = { + body: { ...acknowledgeOooRequest, status: "PENDING"} + }; + + // await acknowledgeOOORequestsValidator(req, res, nextSpy); + expect(nextSpy.notCalled).to.be.true; + }); + + it("should validate for a valid acknowledge OOO request if comment not provided by superusers", async function() { + req = { + body: _.omit(acknowledgeOooRequest, "comment") + }; + res = {}; + // await acknowledgeOOORequestsValidator(req, res, nextSpy); + expect(nextSpy.calledOnce).to.be.true; + }); + + it("should validate for a valid acknowledge OOO request", async function() { + req = { + body: acknowledgeOooRequest + }; + res = {}; + // await acknowledgeOOORequestsValidator(req, res, nextSpy); + expect(nextSpy.calledOnce).to.be.true; + }); + }); }); diff --git a/test/unit/models/discordactions.test.js b/test/unit/models/discordactions.test.js index 1a21c6f61..a1a721fc1 100644 --- a/test/unit/models/discordactions.test.js +++ b/test/unit/models/discordactions.test.js @@ -685,15 +685,16 @@ describe("discordactions", function () { }); describe("getMissedProgressUpdatesUsers", function () { - let activeUserWithProgressUpdates; + let activeUserWithMissedProgressUpdates; let idleUser; let userNotInDiscord; let activeUserId; + let activeUserWithProgressUpdates; beforeEach(async function () { idleUser = { ...userData[9], discordId: getDiscordMembers[0].user.id }; - activeUserWithProgressUpdates = { ...userData[10], discordId: getDiscordMembers[1].user.id }; - const activeUserWithNoUpdates = { ...userData[0], discordId: getDiscordMembers[2].user.id }; + activeUserWithMissedProgressUpdates = { ...userData[10], discordId: getDiscordMembers[1].user.id }; + activeUserWithProgressUpdates = { ...userData[0], discordId: getDiscordMembers[2].user.id }; userNotInDiscord = { ...userData[4], discordId: "Not in discord" }; const { idleStatus: idleUserStatus, @@ -701,10 +702,10 @@ describe("discordactions", function () { userStatusDataForOooState: oooUserStatus, } = userStatusData; const userIdList = await Promise.all([ - await addUser(idleUser), // idle user with no task progress updates - await addUser(activeUserWithProgressUpdates), // active user with task progress updates - await addUser(activeUserWithNoUpdates), // active user with no task progress updates - await addUser(userNotInDiscord), // OOO user with no task progress updates + await addUser(idleUser), + await addUser(activeUserWithMissedProgressUpdates), + await addUser(activeUserWithProgressUpdates), + await addUser(userNotInDiscord), ]); activeUserId = userIdList[2]; await Promise.all([ @@ -751,17 +752,17 @@ describe("discordactions", function () { it("should list of users who missed updating progress", async function () { const result = await getMissedProgressUpdatesUsers(); expect(result).to.be.an("object"); - expect(result).to.be.deep.equal({ - tasks: 4, - missedUpdatesTasks: 3, - usersToAddRole: [activeUserWithProgressUpdates.discordId], - }); + expect(result.tasks).to.not.equal(undefined); + expect(result.tasks).to.equal(4); + expect(result.missedUpdatesTasks).to.not.equal(undefined); + expect(result.missedUpdatesTasks).to.equal(3); + expect(result.usersToAddRole.includes(activeUserWithMissedProgressUpdates.discordId)).to.equal(true); + expect(result.usersToAddRole.includes(idleUser.discordId)).to.equal(true); }); it("should not list of users who are not active and who missed updating progress", async function () { const result = await getMissedProgressUpdatesUsers(); expect(result).to.be.an("object"); - expect(result.usersToAddRole).to.not.contain(idleUser.discordId); expect(result.usersToAddRole).to.not.contain(userNotInDiscord.discordId); }); diff --git a/test/unit/services/oooRequest.test.ts b/test/unit/services/oooRequest.test.ts new file mode 100644 index 000000000..eb63242b6 --- /dev/null +++ b/test/unit/services/oooRequest.test.ts @@ -0,0 +1,246 @@ +import sinon from "sinon"; +import cleanDb from "../../utils/cleanDb"; +import { + INVALID_REQUEST_TYPE, + REQUEST_ALREADY_APPROVED, + REQUEST_ALREADY_REJECTED, + REQUEST_APPROVED_SUCCESSFULLY, + REQUEST_DOES_NOT_EXIST, + REQUEST_REJECTED_SUCCESSFULLY, + REQUEST_STATE, + REQUEST_TYPE, + OOO_STATUS_ALREADY_EXIST, + USER_STATUS_NOT_FOUND, +} from "../../../constants/requests"; +import { + createOooRequest, + validateUserStatus, + // acknowledgeOOORequest, + // validateOOOAcknowledgeRequest +} from "../../../services/oooRequest"; +import { expect } from "chai"; +import { testUserStatus, validOooStatusRequests, validUserCurrentStatus, createdOOORequest } from "../../fixtures/oooRequest/oooRequest"; +import { updateUserStatus } from "../../../models/userStatus"; +import { userState } from "../../../constants/userStatus"; +import addUser from "../../utils/addUser"; +import userDataFixture from "../../fixtures/user/user"; +import * as logService from "../../../services/logService"; +import { acknowledgeOooRequest, createOooRequests3 } from "../../fixtures/oooRequest/oooRequest"; +import { createRequest } from "../../../models/requests"; + +describe("Test OOO Request Service", function() { + + let testUserName: string; + let testUserId: string; + const errorMessage = "Unexpected error occured"; + + beforeEach(async function() { + const users = userDataFixture(); + testUserId = await addUser(users[8]); + testUserName = users[8].username; + }); + + afterEach(async function() { + sinon.restore(); + await cleanDb(); + }); + + describe("validateUserStatus", function() { + + it("should return USER_STATUS_NOT_FOUND if user status not found", async function() { + const validationResponse = await validateUserStatus( + testUserId, + { ...testUserStatus, userStatusExists: false } + ); + expect(validationResponse).to.be.not.undefined; + expect(validationResponse.error).to.equal(USER_STATUS_NOT_FOUND); + }); + + it("should return OOO_STATUS_ALREADY_EXIST if user status is already OOO", async function() { + const validationResponse = await validateUserStatus( + testUserId, + { + ...testUserStatus, + data: { + ...testUserStatus.data, + currentStatus: { + ...testUserStatus.data.currentStatus, + state: userState.OOO + } + } + } + ); + expect(validationResponse).to.be.not.undefined; + expect(validationResponse.error).to.equal(OOO_STATUS_ALREADY_EXIST); + }); + + it("should return undefined when all validation checks passes", async function() { + const response = await validateUserStatus(testUserId, testUserStatus); + expect(response).to.not.exist; + }); + }); + + describe("createOooRequest", function() { + + beforeEach(async function() { + await updateUserStatus(testUserId, testUserStatus.data); + }); + + afterEach(async function () { + sinon.restore(); + }); + + it("should create OOO request", async function() { + const response = await createOooRequest(validOooStatusRequests, testUserName, testUserId); + expect(response).to.deep.include({ + ...createdOOORequest, + id: response.id, + requestedBy:testUserName, + userId: testUserId + }); + }); + + it("should throw error", async function () { + sinon.stub(logService, "addLog").throws(new Error(errorMessage)); + + try { + await createOooRequest(validOooStatusRequests, testUserName, testUserId); + } catch (error) { + expect(error.message).to.equal(errorMessage); + } + }); + }); + + describe.skip("validateOOOAcknowledgeRequest", function() { + + let testOooRequest; + + beforeEach(async function () { + testOooRequest = await createRequest({ + ...createOooRequests3, + userId: testUserId, + comment: null, + lastModifiedBy: null, + }); + }); + + it("should return INVALID_REQUEST_TYPE if request type is not OOO", async function() { + // const validationResponse = await validateOOOAcknowledgeRequest( + // testOooRequest.id, + // REQUEST_TYPE.ONBOARDING, + // testOooRequest.status + // ); + // expect(validationResponse.error).to.be.not.undefined; + // expect(validationResponse.error).to.equal(INVALID_REQUEST_TYPE); + }); + + it("should return REQUEST_ALREADY_APPROVED if request is already approved", async function() { + // const validationResponse = await validateOOOAcknowledgeRequest( + // testOooRequest.id, + // testOooRequest.type, + // REQUEST_STATE.APPROVED + // ); + // expect(validationResponse.error).to.be.not.undefined; + // expect(validationResponse.error).to.equal(REQUEST_ALREADY_APPROVED); + }); + + it("should return REQUEST_ALREADY_REJECTED if request is already rejected", async function() { + // const validationResponse = await validateOOOAcknowledgeRequest( + // testOooRequest.id, + // testOooRequest.type, + // REQUEST_STATE.REJECTED + // ); + // expect(validationResponse.error).to.be.not.undefined; + // expect(validationResponse.error).to.equal(REQUEST_ALREADY_REJECTED); + }); + + it("should return undefined when all validation checks passes", async function() { + // const response = await validateOOOAcknowledgeRequest( + // testOooRequest.id, + // testOooRequest.type, + // testOooRequest.status + // ); + // expect(response).to.not.exist; + }); + }); + + describe.skip("acknowledgeOOORequest", function() { + + let testSuperUserId; + let testOooRequest; + + beforeEach(async function () { + const users = userDataFixture(); + const superUserId = await addUser(users[4]); + testSuperUserId = superUserId; + + testOooRequest = await createRequest({ + ...createOooRequests3, + userId: testUserId, + comment: null, + lastModifiedBy: null, + }); + }); + + it("should return REQUEST_DOES_NOT_EXIST if invalid request id is passed", async function () { + // const invalidOOORequestId = "11111111111111111111"; + // const response = await acknowledgeOOORequest( + // invalidOOORequestId, + // acknowledgeOooRequest, + // testSuperUserId + // ); + // expect(response.error).to.equal(REQUEST_DOES_NOT_EXIST); + }); + + it("should approve OOO request", async function() { + // const response = await acknowledgeOOORequest( + // testOooRequest.id, + // acknowledgeOooRequest, + // testSuperUserId + // ); + // expect(response).to.deep.include({ + // message: REQUEST_APPROVED_SUCCESSFULLY, + // data: { + // ...acknowledgeOooRequest, + // id: testOooRequest.id, + // lastModifiedBy: testSuperUserId, + // updatedAt: response.data.updatedAt + // } + // }); + }); + + it("should reject OOO request", async function() { + // const response = await acknowledgeOOORequest( + // testOooRequest.id, + // { ...acknowledgeOooRequest, status: REQUEST_STATE.REJECTED }, + // testSuperUserId + // ); + // expect(response).to.deep.include({ + // message: REQUEST_REJECTED_SUCCESSFULLY, + // data: { + // ...acknowledgeOooRequest, + // id: testOooRequest.id, + // status: REQUEST_STATE.REJECTED, + // lastModifiedBy: testSuperUserId, + // updatedAt: response.data.updatedAt + // } + // }); + }); + + it("should throw error", async function() { + // sinon.stub(logService, "addLog").throws(new Error(errorMessage)); + // const createSpy = sinon.spy(require("../../../services/oooRequest"), "acknowledgeOOORequest"); + + // try { + // await acknowledgeOOORequest( + // testOooRequest.id, + // acknowledgeOooRequest, + // testSuperUserId + // ); + // } catch (error) { + // expect(error.message).to.equal(errorMessage); + // expect(createSpy.calledOnce).to.be.true; + // } + }); + }); +}); \ No newline at end of file diff --git a/types/oooRequest.d.ts b/types/oooRequest.d.ts index 5655cee01..6b1c282a8 100644 --- a/types/oooRequest.d.ts +++ b/types/oooRequest.d.ts @@ -1,6 +1,5 @@ import { Request, Response } from "express"; import { REQUEST_STATE, REQUEST_TYPE } from "../constants/requests"; -import { userState } from "../constants/userStatus"; import { Boom } from "express-boom"; import { RequestParams, RequestQuery } from "./requests"; import { userData } from "./global"; @@ -9,25 +8,21 @@ export type OooStatusRequest = { id: string; type: REQUEST_TYPE.OOO; from: number; - until?: number; - message?: string; - status: userState; - state?: REQUEST_STATE; - lastModifiedBy?: string; - requestedBy?: string; - createdAt?: Timestamp; - updatedAt?: Timestamp; - reason?: string; + until: number; + reason: string; + status: REQUEST_STATE; + lastModifiedBy: string | null; + requestedBy: string; + userId: string; + createdAt: Timestamp; + updatedAt: Timestamp; + comment: string | null; }; export type OooStatusRequestBody = { - type: REQUEST_TYPE.OOO; - requestedBy?: string; from: number; until: number; - message: string; - state: REQUEST_STATE.PENDING; - createdAt?: Timestamp; - updatedAt?: Timestamp; + type: REQUEST_TYPE.OOO; + reason: string; }; export type OooRequestUpdateBody = { @@ -40,6 +35,10 @@ export type OooRequestUpdateBody = { }; export type OooRequestResponse = Response & { boom: Boom }; -export type OooRequestCreateRequest = Request & { OooStatusRequestBody , userData: userData , query: RequestQuery }; +export type OooRequestCreateRequest = Request & { + body: OooStatusRequestBody; + userData: userData; + query: RequestQuery; +}; export type OooRequestUpdateRequest = Request & { oooRequestUpdateBody , userData: userData , query: RequestQuery , params: RequestParams }; diff --git a/types/userStatus.ts b/types/userStatus.ts new file mode 100644 index 000000000..a8ce1cbd6 --- /dev/null +++ b/types/userStatus.ts @@ -0,0 +1,15 @@ +export type CurrentStatus = { + from: number, + until: number, + state: string, + message: string, + updatedAt: number, +}; + +export type UserStatus = { + id: string, + data: { + currentStatus: CurrentStatus + }, + userStatusExists: boolean +};