Skip to content

Commit 4463fad

Browse files
authored
Enhancement: Allow Null Values for startsOn and endsOn in /tasks PATCH Endpoint (#1756)
* test and code changes for task un assignment * fix the failing test on ci cd pipeline * resets the field internally for task update * moved the payload update method to controller * update the model test as previous * remove the extra line
1 parent fd92f8b commit 4463fad

File tree

4 files changed

+193
-3
lines changed

4 files changed

+193
-3
lines changed

controllers/tasks.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,18 @@ const updateTask = async (req, res) => {
285285
}
286286
}
287287

288+
// currently the task is assigned to a user and the superuser is trying to un assign this task from them.
289+
if (
290+
requestData?.status === TASK_STATUS.AVAILABLE &&
291+
task.taskData.status !== TASK_STATUS.AVAILABLE &&
292+
Object.keys(req.body).length === 1
293+
) {
294+
requestData.assignee = null;
295+
requestData.percentCompleted = 0;
296+
requestData.startedOn = null;
297+
requestData.endsOn = null;
298+
}
299+
288300
await tasks.updateTask(requestData, req.params.id);
289301
if (requestData.assignee) {
290302
// New Assignee Status Update

middlewares/validators/tasks.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ const updateTask = async (req, res, next) => {
7777
featureUrl: joi.string().optional(),
7878
type: joi.string().optional(),
7979
links: joi.array().items(joi.string()).optional(),
80-
endsOn: joi.number().optional(),
81-
startedOn: joi.number().optional(),
80+
endsOn: joi.alternatives().try(joi.number().optional(), joi.valid(null)),
81+
startedOn: joi.alternatives().try(joi.number().optional(), joi.valid(null)),
8282
category: joi.string().optional(),
8383
level: joi.number().optional(),
8484
status: joi

test/integration/tasks.test.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,4 +1292,87 @@ describe("Tasks", function () {
12921292
expect(res.body.failedTasksIds.length).to.equal(4);
12931293
});
12941294
});
1295+
1296+
describe("PATCH /tasks/:id should update the tasks by SuperUser", function () {
1297+
beforeEach(async function () {
1298+
const superUserId = await addUser(superUser);
1299+
superUserJwt = authService.generateAuthToken({ userId: superUserId });
1300+
1301+
await firestore.collection("tasks").doc("4kAkRv9TBlOfR6WEUhoQ").set({
1302+
assignee: "SooJK37gzjIZfFNH0tlL",
1303+
status: "IN_PROGRESS",
1304+
percentCompleted: 80,
1305+
startedOn: 1701388800000,
1306+
endsOn: 1701561600000,
1307+
});
1308+
});
1309+
1310+
afterEach(async function () {
1311+
await firestore.collection("tasks").doc("4kAkRv9TBlOfR6WEUhoQ").delete();
1312+
});
1313+
1314+
it("Should unassign the task with other fields reset exclusively", async function () {
1315+
const res = await chai
1316+
.request(app)
1317+
.patch(`/tasks/4kAkRv9TBlOfR6WEUhoQ`)
1318+
.set("cookie", `${cookieName}=${superUserJwt}`)
1319+
.send({
1320+
assignee: null,
1321+
status: TASK_STATUS.AVAILABLE,
1322+
percentCompleted: 0,
1323+
startedOn: null,
1324+
endsOn: null,
1325+
});
1326+
1327+
expect(res).to.have.status(204);
1328+
1329+
const docSnapshot = await firestore.collection("tasks").doc("4kAkRv9TBlOfR6WEUhoQ").get();
1330+
1331+
const updatedData = docSnapshot.data();
1332+
expect(updatedData.assignee).to.equal(null);
1333+
expect(updatedData.status).to.equal(TASK_STATUS.AVAILABLE);
1334+
expect(updatedData.percentCompleted).to.equal(0);
1335+
expect(updatedData.startedOn).to.equal(null);
1336+
expect(updatedData.endsOn).to.equal(null);
1337+
});
1338+
1339+
it("Should unassign the task with other fields reset internally", async function () {
1340+
const res = await chai
1341+
.request(app)
1342+
.patch(`/tasks/4kAkRv9TBlOfR6WEUhoQ`)
1343+
.set("cookie", `${cookieName}=${superUserJwt}`)
1344+
.send({
1345+
status: TASK_STATUS.AVAILABLE,
1346+
});
1347+
1348+
expect(res).to.have.status(204);
1349+
1350+
const docSnapshot = await firestore.collection("tasks").doc("4kAkRv9TBlOfR6WEUhoQ").get();
1351+
1352+
const updatedData = docSnapshot.data();
1353+
expect(updatedData.assignee).to.equal(null);
1354+
expect(updatedData.status).to.equal(TASK_STATUS.AVAILABLE);
1355+
expect(updatedData.percentCompleted).to.equal(0);
1356+
expect(updatedData.startedOn).to.equal(null);
1357+
expect(updatedData.endsOn).to.equal(null);
1358+
});
1359+
1360+
it("Should throw bad request if the req body is invalid", async function () {
1361+
const res = await chai
1362+
.request(app)
1363+
.patch(`/tasks/4kAkRv9TBlOfR6WEUhoQ`)
1364+
.set("cookie", `${cookieName}=${superUserJwt}`)
1365+
.send({
1366+
assignee: null,
1367+
status: TASK_STATUS.AVAILABLE,
1368+
percentCompleted: 0,
1369+
startedOn: "null",
1370+
endsOn: false,
1371+
});
1372+
1373+
expect(res).to.have.status(400);
1374+
expect(res.body.error).to.equal("Bad Request");
1375+
expect(res.body.message).to.equal('"endsOn" must be one of [number, null]');
1376+
});
1377+
});
12951378
});

test/unit/middlewares/tasks-validator.test.js

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
const Sinon = require("sinon");
2-
const { getTasksValidator, createTask } = require("../../../middlewares/validators/tasks");
2+
const {
3+
getTasksValidator,
4+
createTask,
5+
updateTask: updateTaskValidator,
6+
} = require("../../../middlewares/validators/tasks");
37
const { expect } = require("chai");
48
const { TASK_STATUS } = require("../../../constants/tasks");
59

@@ -520,4 +524,95 @@ describe("getTasks validator", function () {
520524
}
521525
expect(nextMiddlewareSpy.callCount).to.be.equal(0);
522526
});
527+
528+
it("should call nextMiddlewareSpy for updateTaskValidator if startedOn is null", async function () {
529+
const req = {
530+
body: {
531+
startedOn: null,
532+
endsOn: new Date().getTime(),
533+
},
534+
};
535+
const res = { boom: { badRequest: Sinon.spy() } };
536+
const nextMiddlewareSpy = Sinon.spy();
537+
await updateTaskValidator(req, res, nextMiddlewareSpy);
538+
expect(nextMiddlewareSpy.callCount).to.be.equal(1);
539+
});
540+
541+
it("should call nextMiddlewareSpy for updateTaskValidator if endsOn is null", async function () {
542+
const req = {
543+
body: {
544+
startedOn: new Date().getTime(),
545+
endsOn: null,
546+
},
547+
};
548+
const res = { boom: { badRequest: Sinon.spy() } };
549+
const nextMiddlewareSpy = Sinon.spy();
550+
await updateTaskValidator(req, res, nextMiddlewareSpy);
551+
expect(nextMiddlewareSpy.callCount).to.be.equal(1);
552+
});
553+
554+
it("should call nextMiddlewareSpy for updateTaskValidator if both startedOn and endsOn are null", async function () {
555+
const req = {
556+
body: {
557+
startedOn: null,
558+
endsOn: null,
559+
},
560+
};
561+
const res = { boom: { badRequest: Sinon.spy() } };
562+
const nextMiddlewareSpy = Sinon.spy();
563+
await updateTaskValidator(req, res, nextMiddlewareSpy);
564+
expect(nextMiddlewareSpy.callCount).to.be.equal(1);
565+
});
566+
567+
it("should call nextMiddlewareSpy for updateTaskValidator if both startedOn and endsOn are valid number", async function () {
568+
const req = {
569+
body: {
570+
startedOn: new Date("2023-11-15").getTime(),
571+
endsOn: new Date("2023-11-18").getTime(),
572+
},
573+
};
574+
const res = { boom: { badRequest: Sinon.spy() } };
575+
const nextMiddlewareSpy = Sinon.spy();
576+
await updateTaskValidator(req, res, nextMiddlewareSpy);
577+
expect(nextMiddlewareSpy.callCount).to.be.equal(1);
578+
});
579+
580+
it("should not call nextMiddlewareSpy for updateTaskValidator if startedOn is not null or a number", async function () {
581+
const req = {
582+
body: {
583+
startedOn: "December 6 2023",
584+
endsOn: new Date().getTime(),
585+
},
586+
};
587+
const res = { boom: { badRequest: Sinon.spy() } };
588+
const nextMiddlewareSpy = Sinon.spy();
589+
await updateTaskValidator(req, res, nextMiddlewareSpy);
590+
expect(nextMiddlewareSpy.callCount).to.be.equal(0);
591+
});
592+
593+
it("should not call nextMiddlewareSpy for updateTaskValidator if endsOn is not null or a number", async function () {
594+
const req = {
595+
body: {
596+
startedOn: new Date().getTime(),
597+
endsOn: true,
598+
},
599+
};
600+
const res = { boom: { badRequest: Sinon.spy() } };
601+
const nextMiddlewareSpy = Sinon.spy();
602+
await updateTaskValidator(req, res, nextMiddlewareSpy);
603+
expect(nextMiddlewareSpy.callCount).to.be.equal(0);
604+
});
605+
606+
it("should not call nextMiddlewareSpy for updateTaskValidator if both startedOn and endsOn is not null or a number", async function () {
607+
const req = {
608+
body: {
609+
startedOn: "December 6 2023",
610+
endsOn: true,
611+
},
612+
};
613+
const res = { boom: { badRequest: Sinon.spy() } };
614+
const nextMiddlewareSpy = Sinon.spy();
615+
await updateTaskValidator(req, res, nextMiddlewareSpy);
616+
expect(nextMiddlewareSpy.callCount).to.be.equal(0);
617+
});
523618
});

0 commit comments

Comments
 (0)