Skip to content

Commit 6b497b1

Browse files
committed
feat: acknowledge OOO request
2 parents 5540a2e + 6a4319c commit 6b497b1

File tree

7 files changed

+87
-15
lines changed

7 files changed

+87
-15
lines changed

constants/progresses.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ const PROGRESSES_SIZE = 20;
2222
const PROGRESSES_PAGE_SIZE = 0;
2323
const VALID_PROGRESS_TYPES = ["task", "user"];
2424

25+
const UNAUTHORIZED_WRITE = "Unauthorized to write progress of task";
26+
2527
module.exports = {
2628
PROGRESSES_RESPONSE_MESSAGES,
2729
MILLISECONDS_IN_DAY,
@@ -31,4 +33,5 @@ module.exports = {
3133
PROGRESS_VALID_SORT_FIELDS,
3234
PROGRESSES_SIZE,
3335
PROGRESSES_PAGE_SIZE,
36+
UNAUTHORIZED_WRITE,
3437
};

controllers/extensionRequests.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -204,20 +204,21 @@ const getSelfExtensionRequests = async (req, res) => {
204204
const updateExtensionRequest = async (req, res) => {
205205
const { dev } = req.query;
206206
const isDev = dev === "true";
207+
const isSuperUser = req.userData?.roles.super_user;
207208
try {
208209
const extensionRequest = await extensionRequestsQuery.fetchExtensionRequest(req.params.id);
209210
if (!extensionRequest.extensionRequestData) {
210211
return res.boom.notFound("Extension Request not found");
211212
}
212213

213-
if (
214-
isDev &&
215-
!req.userData?.roles.super_user &&
216-
extensionRequest.extensionRequestData.status !== EXTENSION_REQUEST_STATUS.PENDING
217-
) {
214+
if (isDev && !isSuperUser && extensionRequest.extensionRequestData.status !== EXTENSION_REQUEST_STATUS.PENDING) {
218215
return res.boom.badRequest("Only pending extension request can be updated");
219216
}
220217

218+
if (isDev && !isSuperUser && extensionRequest.extensionRequestData.assigneeId !== req.userData.id) {
219+
return res.boom.forbidden("You don't have permission to update the extension request");
220+
}
221+
221222
if (req.body.assignee) {
222223
const { taskData: task } = await tasks.fetchTask(extensionRequest.extensionRequestData.taskId);
223224
if (task.assignee !== (await getUsername(req.body.assignee))) {

controllers/progresses.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const {
55
INTERNAL_SERVER_ERROR_MESSAGE,
66
PROGRESSES_SIZE,
77
PROGRESSES_PAGE_SIZE,
8+
UNAUTHORIZED_WRITE,
89
} = require("../constants/progresses");
910
const { sendTaskUpdate } = require("../utils/sendTaskUpdate");
1011
const { PROGRESS_DOCUMENT_RETRIEVAL_SUCCEEDED, PROGRESS_DOCUMENT_CREATED_SUCCEEDED } = PROGRESSES_RESPONSE_MESSAGES;
@@ -45,6 +46,10 @@ const { PROGRESS_DOCUMENT_RETRIEVAL_SUCCEEDED, PROGRESS_DOCUMENT_CREATED_SUCCEED
4546
*/
4647

4748
const createProgress = async (req, res) => {
49+
if (req.userData.roles.archived) {
50+
return res.boom.forbidden(UNAUTHORIZED_WRITE);
51+
}
52+
4853
const {
4954
body: { type, completed, planned, blockers, taskId },
5055
} = req;

middlewares/validators/oooRequests.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,19 @@ export const createOooStatusRequestValidator = async (
3939
await schema.validateAsync(req.body, { abortEarly: false });
4040
};
4141

42+
/**
43+
* Middleware to validate the acknowledge Out-Of-Office (OOO) request payload.
44+
*
45+
* @param {AcknowledgeOOORequest} req - The request object containing the body to be validated.
46+
* @param {OooRequestResponse} res - The response object used to send error responses if validation fails.
47+
* @param {NextFunction} next - The next middleware function to call if validation succeeds.
48+
* @returns {Promise<void>} Resolves or sends errors.
49+
*/
4250
export const acknowledgeOOORequestsValidator = async (
4351
req: AcknowledgeOOORequest,
4452
res: OooRequestResponse,
4553
next: NextFunction
46-
) => {
54+
): Promise<void> => {
4755
const schema = joi
4856
.object()
4957
.strict()

services/oooRequest.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,17 @@ import {
1616
} from "../constants/requests";
1717
import { getRequests, updateRequest } from "../models/requests";
1818
import { AcknowledgeOOORequestBody } from "../types/oooRequest";
19-
import { statusState } from "../constants/userStatus";
20-
import { createUserFutureStatus } from "../models/userFutureStatus";
2119
import { addFutureStatus } from "../models/userStatus";
2220
const requestModel = firestore.collection("requests");
2321

22+
/**
23+
* Validates an Out-Of-Office (OOO) acknowledge request
24+
*
25+
* @param {string} requestId - The unique identifier of the request.
26+
* @param {string} requestType - The type of the request (expected to be 'OOO').
27+
* @param {string} requestStatus - The current status of the request.
28+
* @throws {Error} Throws an error if an issue occurs during validation.
29+
*/
2430
export const validateOOOAcknowledgeRequest = async (
2531
requestId: string,
2632
requestType: string,
@@ -58,6 +64,15 @@ export const validateOOOAcknowledgeRequest = async (
5864
}
5965
}
6066

67+
/**
68+
* Acknowledges an Out-of-Office (OOO) request
69+
*
70+
* @param {string} requestId - The ID of the OOO request to acknowledge.
71+
* @param {AcknowledgeOOORequestBody} body - The acknowledgement body containing acknowledging details.
72+
* @param {string} userId - The unique identifier of the superuser user.
73+
* @returns {Promise<object>} The acknowledged OOO request.
74+
* @throws {Error} Throws an error if an issue occurs during acknowledgment process.
75+
*/
6176
export const acknowledgeOOORequest = async (
6277
requestId: string,
6378
body: AcknowledgeOOORequestBody,
@@ -106,17 +121,15 @@ export const acknowledgeOOORequest = async (
106121
const requestData = await getRequests({ id: requestId });
107122

108123
if (requestData) {
109-
const { from, until, requestedBy, comment } = requestData as any;
124+
const { from, until, userId, comment } = requestData as any;
110125
const userFutureStatusData = {
111126
requestId,
112-
status: REQUEST_TYPE.OOO,
113-
state: statusState.UPCOMING,
127+
state: REQUEST_TYPE.OOO,
114128
from,
115129
endsOn: until,
116-
userId: requestedBy,
130+
userId: userId,
117131
message: comment,
118132
};
119-
await createUserFutureStatus(userFutureStatusData);
120133
await addFutureStatus(userFutureStatusData);
121134
}
122135
}

test/integration/extensionRequests.test.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const user = userData[6];
2222
const appOwner = userData[3];
2323
const superUser = userData[4];
2424

25-
let appOwnerjwt, superUserJwt, jwt, superUserId, extensionRequestId5;
25+
let appOwnerjwt, superUserJwt, jwt, user2Jwt, superUserId, extensionRequestId5;
2626

2727
describe("Extension Requests", function () {
2828
let taskId0,
@@ -40,13 +40,15 @@ describe("Extension Requests", function () {
4040

4141
before(async function () {
4242
const userId = await addUser(user);
43+
const userId2 = await addUser(userData[5]);
4344
user.id = userId;
4445
const appOwnerUserId = await addUser(appOwner);
4546
appOwner.id = appOwnerUserId;
4647
superUserId = await addUser(superUser);
4748
appOwnerjwt = authService.generateAuthToken({ userId: appOwnerUserId });
4849
superUserJwt = authService.generateAuthToken({ userId: superUserId });
4950
jwt = authService.generateAuthToken({ userId: userId });
51+
user2Jwt = authService.generateAuthToken({ userId: userId2 });
5052

5153
const taskData = [
5254
{
@@ -1094,6 +1096,26 @@ describe("Extension Requests", function () {
10941096
});
10951097
});
10961098

1099+
it("should return forbidden response if superuser or request owner does not update the request when dev is enabled", function (done) {
1100+
chai
1101+
.request(app)
1102+
.patch(`/extension-requests/${extensionRequestId4}?dev=true`)
1103+
.set("cookie", `${cookieName}=${user2Jwt}`)
1104+
.send({
1105+
title: "new-title",
1106+
})
1107+
.end((err, res) => {
1108+
if (err) {
1109+
return done(err);
1110+
}
1111+
expect(res).to.have.status(403);
1112+
expect(res.body)
1113+
.to.have.property("message")
1114+
.that.equals("You don't have permission to update the extension request");
1115+
return done();
1116+
});
1117+
});
1118+
10971119
it("Should return 400 if assignee of the extensionrequest is upated with a different user", function (done) {
10981120
chai
10991121
.request(app)

test/integration/progressesTasks.test.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const {
1616

1717
const userData = require("../fixtures/user/user")();
1818
const taskData = require("../fixtures/tasks/tasks")();
19-
const { INTERNAL_SERVER_ERROR_MESSAGE } = require("../../constants/progresses");
19+
const { INTERNAL_SERVER_ERROR_MESSAGE, UNAUTHORIZED_WRITE } = require("../../constants/progresses");
2020
const cookieName = config.get("userToken.cookieName");
2121
const { expect } = chai;
2222

@@ -32,6 +32,8 @@ describe("Test Progress Updates API for Tasks", function () {
3232
let taskId1;
3333
let taskId2;
3434
let fetchMock;
35+
let archivedUserId;
36+
let archivedUserToken;
3537

3638
beforeEach(async function () {
3739
fetchMock = sinon.stub(global, "fetch");
@@ -40,6 +42,8 @@ describe("Test Progress Updates API for Tasks", function () {
4042
toFake: ["Date"],
4143
});
4244
userId = await addUser(userData[1]);
45+
archivedUserId = await addUser(userData[5]);
46+
archivedUserToken = authService.generateAuthToken({ userId: archivedUserId });
4347
userToken = authService.generateAuthToken({ userId: userId });
4448
const taskObject1 = await tasks.updateTask(taskData[0]);
4549
taskId1 = taskObject1.taskId;
@@ -165,6 +169,22 @@ describe("Test Progress Updates API for Tasks", function () {
165169
return done();
166170
});
167171
});
172+
173+
it("should return forbidden response when user is not in discord", function (done) {
174+
chai
175+
.request(app)
176+
.post("/progresses")
177+
.set("Cookie", `${cookieName}=${archivedUserToken}`)
178+
.send(taskProgressDay1("1111"))
179+
.end((err, res) => {
180+
if (err) {
181+
return done(err);
182+
}
183+
expect(res.statusCode).to.equal(403);
184+
expect(res.body.message).to.equal(UNAUTHORIZED_WRITE);
185+
return done();
186+
});
187+
});
168188
});
169189

170190
describe("Verify the GET progress records", function () {

0 commit comments

Comments
 (0)