Skip to content

Commit 3c7c582

Browse files
Merge pull request #1714 from Real-Dev-Squad/revert-1705-remove-scripting-code
Revert "Remove code- One-Time Script to Migrate Status from "COMPLETED" to "DONE" in taskModel Database"
2 parents 16d4a50 + 4ea44b1 commit 3c7c582

File tree

7 files changed

+269
-0
lines changed

7 files changed

+269
-0
lines changed

controllers/tasks.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,16 @@ const assignTask = async (req, res) => {
426426
}
427427
};
428428

429+
const updateStatus = async (req, res) => {
430+
try {
431+
const response = await tasks.updateTaskStatus();
432+
return res.status(200).json(response);
433+
} catch (error) {
434+
logger.error("Error in migration scripts", error);
435+
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
436+
}
437+
};
438+
429439
module.exports = {
430440
addNewTask,
431441
fetchTasks,
@@ -436,4 +446,5 @@ module.exports = {
436446
updateTaskStatus,
437447
overdueTasks,
438448
assignTask,
449+
updateStatus,
439450
};

models/tasks.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ const tasksModel = firestore.collection("tasks");
33
const ItemModel = firestore.collection("itemTags");
44
const dependencyModel = firestore.collection("taskDependencies");
55
const userUtils = require("../utils/users");
6+
const { updateTaskStatusToDone } = require("../services/tasks");
7+
const { chunks } = require("../utils/array");
8+
const { DOCUMENT_WRITE_SIZE } = require("../constants/constants");
69
const { fromFirestoreData, toFirestoreData, buildTasks } = require("../utils/tasks");
710
const { TASK_TYPE, TASK_STATUS, TASK_STATUS_OLD, TASK_SIZE } = require("../constants/tasks");
811
const { IN_PROGRESS, NEEDS_REVIEW, IN_REVIEW, ASSIGNED, BLOCKED, SMOKE_TESTING, COMPLETED, SANITY_CHECK } = TASK_STATUS;
912
const { OLD_ACTIVE, OLD_BLOCKED, OLD_PENDING, OLD_COMPLETED } = TASK_STATUS_OLD;
13+
const { INTERNAL_SERVER_ERROR } = require("../constants/errorMessages");
1014

1115
/**
1216
* Adds and Updates tasks
@@ -575,6 +579,62 @@ const getOverdueTasks = async (days = 0) => {
575579
}
576580
};
577581

582+
const updateTaskStatus = async () => {
583+
try {
584+
const snapshot = await tasksModel.where("status", "==", "COMPLETED").get();
585+
const tasksStatusCompleted = [];
586+
let summary = {
587+
totalTasks: snapshot.size,
588+
totalUpdatedStatus: 0,
589+
totalOperationsFailed: 0,
590+
updatedTaskDetails: [],
591+
failedTaskDetails: [],
592+
};
593+
594+
if (snapshot.size === 0) {
595+
return summary;
596+
}
597+
598+
snapshot.forEach((task) => {
599+
const id = task.id;
600+
const taskData = task.data();
601+
tasksStatusCompleted.push({ ...taskData, id });
602+
});
603+
const taskStatusCompletedChunks = chunks(tasksStatusCompleted, DOCUMENT_WRITE_SIZE);
604+
605+
const updatedTasksPromises = await Promise.all(
606+
taskStatusCompletedChunks.map(async (tasks) => {
607+
const res = await updateTaskStatusToDone(tasks);
608+
return {
609+
totalUpdatedStatus: res.totalUpdatedStatus,
610+
totalOperationsFailed: res.totalOperationsFailed,
611+
updatedTaskDetails: res.updatedTaskDetails,
612+
failedTaskDetails: res.failedTaskDetails,
613+
};
614+
})
615+
);
616+
617+
updatedTasksPromises.forEach((res) => {
618+
summary = {
619+
...summary,
620+
totalUpdatedStatus: (summary.totalUpdatedStatus += res.totalUpdatedStatus),
621+
totalOperationsFailed: (summary.totalOperationsFailed += res.totalOperationsFailed),
622+
updatedTaskDetails: [...summary.updatedTaskDetails, ...res.updatedTaskDetails],
623+
failedTaskDetails: [...summary.failedTaskDetails, ...res.failedTaskDetails],
624+
};
625+
});
626+
627+
if (summary.totalOperationsFailed === summary.totalTasks) {
628+
throw Error(INTERNAL_SERVER_ERROR);
629+
}
630+
631+
return summary;
632+
} catch (error) {
633+
logger.error(`Error in updating task status: ${error}`);
634+
throw error;
635+
}
636+
};
637+
578638
module.exports = {
579639
updateTask,
580640
fetchTasks,
@@ -591,4 +651,5 @@ module.exports = {
591651
fetchPaginatedTasks,
592652
getBuiltTasks,
593653
getOverdueTasks,
654+
updateTaskStatus,
594655
};

routes/tasks.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,6 @@ router.patch(
4040
);
4141
router.patch("/assign/self", authenticate, invalidateCache({ invalidationKeys: [ALL_TASKS] }), tasks.assignTask);
4242

43+
router.post("/migration", authenticate, authorizeRoles([SUPERUSER]), tasks.updateStatus);
44+
4345
module.exports = router;

services/tasks.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
const firestore = require("../utils/firestore");
2+
const tasksModel = firestore.collection("tasks");
3+
const updateTaskStatusToDone = async (tasksData) => {
4+
const batch = firestore.batch();
5+
const tasksBatch = [];
6+
const summary = {
7+
totalUpdatedStatus: 0,
8+
totalOperationsFailed: 0,
9+
updatedTaskDetails: [],
10+
failedTaskDetails: [],
11+
};
12+
tasksData.forEach((task) => {
13+
const updateTaskData = { ...task, status: "DONE" };
14+
batch.update(tasksModel.doc(task.id), updateTaskData);
15+
tasksBatch.push(task.id);
16+
});
17+
try {
18+
await batch.commit();
19+
summary.totalUpdatedStatus += tasksData.length;
20+
summary.updatedTaskDetails = [...tasksBatch];
21+
return { ...summary };
22+
} catch (err) {
23+
logger.error("Firebase batch Operation Failed!");
24+
summary.totalOperationsFailed += tasksData.length;
25+
summary.failedTaskDetails = [...tasksBatch];
26+
return { ...summary };
27+
}
28+
};
29+
module.exports = {
30+
updateTaskStatusToDone,
31+
};

test/integration/tasks.test.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const sinon = require("sinon");
33
const { expect } = chai;
44
const chaiHttp = require("chai-http");
55

6+
const firestore = require("../../utils/firestore");
67
const app = require("../../server");
78
const tasks = require("../../models/tasks");
89
const authService = require("../../services/authService");
@@ -1021,4 +1022,51 @@ describe("Tasks", function () {
10211022
expect(res.body.message).to.be.equal("No overdue tasks found");
10221023
});
10231024
});
1025+
1026+
describe("POST /tasks/migration", function () {
1027+
it("Should update status COMPLETED to DONE successful", async function () {
1028+
const taskData1 = { status: "COMPLETED" };
1029+
await firestore.collection("tasks").doc("updateTaskStatus1").set(taskData1);
1030+
const res = await chai.request(app).post("/tasks/migration").set("cookie", `${cookieName}=${superUserJwt}`);
1031+
expect(res).to.have.status(200);
1032+
expect(res.body.totalTasks).to.be.equal(1);
1033+
expect(res.body.totalUpdatedStatus).to.be.equal(1);
1034+
expect(res.body.updatedTaskDetails).to.deep.equal(["updateTaskStatus1"]);
1035+
expect(res.body.totalOperationsFailed).to.be.equal(0);
1036+
expect(res.body.failedTaskDetails).to.deep.equal([]);
1037+
});
1038+
1039+
it("Should not update if not found any COMPLETED task status ", async function () {
1040+
const res = await chai.request(app).post("/tasks/migration").set("cookie", `${cookieName}=${superUserJwt}`);
1041+
expect(res).to.have.status(200);
1042+
expect(res.body.totalTasks).to.be.equal(0);
1043+
expect(res.body.totalUpdatedStatus).to.be.equal(0);
1044+
expect(res.body.updatedTaskDetails).to.deep.equal([]);
1045+
expect(res.body.totalOperationsFailed).to.be.equal(0);
1046+
expect(res.body.failedTaskDetails).to.deep.equal([]);
1047+
});
1048+
1049+
it("should throw an error if firestore batch operations fail", async function () {
1050+
const stub = sinon.stub(firestore, "batch");
1051+
stub.returns({
1052+
update: function () {},
1053+
commit: function () {
1054+
throw new Error("Firestore batch commit failed!");
1055+
},
1056+
});
1057+
const taskData1 = { status: "COMPLETED" };
1058+
await firestore.collection("tasks").doc("updateTaskStatus1").set(taskData1);
1059+
const res = await chai.request(app).post("/tasks/migration").set("cookie", `${cookieName}=${superUserJwt}`);
1060+
expect(res.status).to.equal(500);
1061+
const response = res.body;
1062+
expect(response.message).to.be.equal("An internal server error occurred");
1063+
});
1064+
1065+
it("Should return 401 if not super_user", async function () {
1066+
const nonSuperUserId = await addUser(appOwner);
1067+
const nonSuperUserJwt = authService.generateAuthToken({ userId: nonSuperUserId });
1068+
const res = await chai.request(app).post("/tasks/migration").set("cookie", `${cookieName}=${nonSuperUserJwt}`);
1069+
expect(res).to.have.status(401);
1070+
});
1071+
});
10241072
});

test/unit/models/tasks.test.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/* eslint-disable security/detect-object-injection */
66

77
const chai = require("chai");
8+
const sinon = require("sinon");
89
const { expect } = chai;
910
const cleanDb = require("../../utils/cleanDb");
1011
const tasksData = require("../../fixtures/tasks/tasks")();
@@ -304,4 +305,45 @@ describe("tasks", function () {
304305
expect(usersWithOverdueTasks.length).to.be.equal(4);
305306
});
306307
});
308+
309+
describe("update task status", function () {
310+
beforeEach(async function () {
311+
const addTasksPromises = [];
312+
tasksData.forEach((task) => {
313+
const taskData = {
314+
...task,
315+
status: "COMPLETED",
316+
};
317+
addTasksPromises.push(tasksModel.add(taskData));
318+
});
319+
320+
await Promise.all(addTasksPromises);
321+
});
322+
323+
afterEach(function () {
324+
sinon.restore();
325+
});
326+
327+
it("Should update task status COMPLETED to DONE", async function () {
328+
const res = await tasks.updateTaskStatus();
329+
expect(res.totalTasks).to.be.equal(8);
330+
expect(res.totalUpdatedStatus).to.be.equal(8);
331+
});
332+
333+
it("should throw an error if firebase batch operation fails", async function () {
334+
const stub = sinon.stub(firestore, "batch");
335+
stub.returns({
336+
update: function () {},
337+
commit: function () {
338+
throw new Error("Firestore batch update failed");
339+
},
340+
});
341+
try {
342+
await tasks.updateTaskStatus();
343+
} catch (error) {
344+
expect(error).to.be.an.instanceOf(Error);
345+
expect(error.message).to.equal("An internal server error occurred");
346+
}
347+
});
348+
});
307349
});

test/unit/services/tasks.test.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
const Sinon = require("sinon");
2+
const { expect } = require("chai");
3+
4+
const firestore = require("../../../utils/firestore");
5+
const tasksModel = firestore.collection("tasks");
6+
const cleanDb = require("../../utils/cleanDb");
7+
const taskDataArray = require("../../fixtures/tasks/tasks")();
8+
const { updateTaskStatusToDone } = require("../../../services/tasks");
9+
10+
describe("Tasks services", function () {
11+
describe("task status COMPLETED to DONE in bulk", function () {
12+
const tasks = [];
13+
const taskDetails = [];
14+
beforeEach(async function () {
15+
const addTasksPromises = [];
16+
taskDataArray.forEach((task) => {
17+
const taskData = {
18+
...task,
19+
status: "COMPLETED",
20+
};
21+
addTasksPromises.push(tasksModel.add(taskData));
22+
});
23+
24+
await Promise.all(addTasksPromises);
25+
26+
tasks.length = 0;
27+
taskDetails.length = 0;
28+
29+
const snapshot = await tasksModel.where("status", "==", "COMPLETED").get();
30+
31+
snapshot.forEach((task) => {
32+
const id = task.id;
33+
const taskData = task.data();
34+
tasks.push({ ...taskData, id });
35+
taskDetails.push(id);
36+
});
37+
});
38+
39+
afterEach(async function () {
40+
await cleanDb();
41+
Sinon.restore();
42+
});
43+
44+
it("successfully updated task status COMPLETED To DONE", async function () {
45+
const res = await updateTaskStatusToDone(tasks);
46+
47+
expect(res).to.deep.equal({
48+
totalUpdatedStatus: 8,
49+
totalOperationsFailed: 0,
50+
updatedTaskDetails: taskDetails,
51+
failedTaskDetails: [],
52+
});
53+
});
54+
55+
it("should throw an error if firebase batch operation fails", async function () {
56+
const batchStub = Sinon.stub(firestore, "batch");
57+
batchStub.returns({
58+
update: function () {},
59+
commit: function () {
60+
throw new Error("Firebase batch operation failed");
61+
},
62+
});
63+
64+
const res = await updateTaskStatusToDone(tasks);
65+
66+
expect(res).to.deep.equal({
67+
totalUpdatedStatus: 0,
68+
totalOperationsFailed: 8,
69+
updatedTaskDetails: [],
70+
failedTaskDetails: taskDetails,
71+
});
72+
});
73+
});
74+
});

0 commit comments

Comments
 (0)