Skip to content

Commit 0a199eb

Browse files
authored
Merge pull request #1996 from Real-Dev-Squad/feat/add-backlog-orphan-tasks
feat: Filter orphan tasks and mark them BACKLOG
2 parents 19829f7 + d6a953e commit 0a199eb

File tree

7 files changed

+145
-8
lines changed

7 files changed

+145
-8
lines changed

controllers/tasks.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,19 @@ const updateStatus = async (req, res) => {
489489
}
490490
};
491491

492+
const orphanTasks = async (req, res) => {
493+
try {
494+
const { lastOrphanTasksFilterationTimestamp = 0 } = req.body;
495+
496+
const updatedTasksData = await tasks.updateOrphanTasksStatus(lastOrphanTasksFilterationTimestamp);
497+
498+
return res.status(200).json({ message: "Orphan tasks filtered successfully", updatedTasksData });
499+
} catch (error) {
500+
logger.error("Error in filtering orphan tasks", error);
501+
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
502+
}
503+
};
504+
492505
const getUsersHandler = async (req, res) => {
493506
try {
494507
const { size, cursor, q: queryString } = req.query;
@@ -548,4 +561,5 @@ module.exports = {
548561
assignTask,
549562
updateStatus,
550563
getUsersHandler,
564+
orphanTasks,
551565
};

middlewares/validators/discordactions.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const Joi = require("joi");
2+
const { validateMillisecondsTimestamp } = require("./utils");
23

34
const validateGroupRoleBody = async (req, res, next) => {
45
const schema = Joi.object({
@@ -28,13 +29,6 @@ const validateMemberRoleBody = async (req, res, next) => {
2829
}
2930
};
3031

31-
const validateMillisecondsTimestamp = async (reqBody, timestampProperty) => {
32-
const schema = Joi.object({
33-
[timestampProperty]: Joi.number().unit("milliseconds").required(),
34-
});
35-
return schema.validateAsync(reqBody);
36-
};
37-
3832
const validateUpdateUsersNicknameStatusBody = async (req, res, next) => {
3933
try {
4034
await validateMillisecondsTimestamp(req.body, "lastNicknameUpdate");

middlewares/validators/tasks.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const { Operators } = require("../../typeDefinitions/rqlParser");
77
const { daysOfWeek } = require("../../constants/constants");
88
const TASK_STATUS_ENUM = Object.values(TASK_STATUS);
99
const MAPPED_TASK_STATUS_ENUM = Object.keys(MAPPED_TASK_STATUS);
10+
const { validateMillisecondsTimestamp } = require("./utils");
1011

1112
const createTask = async (req, res, next) => {
1213
const schema = joi
@@ -262,10 +263,21 @@ const getUsersValidator = async (req, res, next) => {
262263
res.boom.badRequest(error.details[0].message);
263264
}
264265
};
266+
267+
const filterOrphanTasksValidator = async (req, res, next) => {
268+
try {
269+
await validateMillisecondsTimestamp(req.body, "lastOrphanTasksFilterationTimestamp");
270+
next();
271+
} catch (error) {
272+
logger.error(`Error while validating request body for Orphan Tasks Filteration payload : ${error}`);
273+
res.boom.badRequest(error);
274+
}
275+
};
265276
module.exports = {
266277
createTask,
267278
updateTask,
268279
updateSelfTask,
269280
getTasksValidator,
270281
getUsersValidator,
282+
filterOrphanTasksValidator,
271283
};

middlewares/validators/utils.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const Joi = require("joi");
2+
3+
const validateMillisecondsTimestamp = async (reqBody, timestampProperty) => {
4+
const schema = Joi.object({
5+
[timestampProperty]: Joi.number().unit("milliseconds").required(),
6+
});
7+
return schema.validateAsync(reqBody);
8+
};
9+
10+
module.exports = {
11+
validateMillisecondsTimestamp
12+
}

models/tasks.js

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const firestore = require("../utils/firestore");
22
const tasksModel = firestore.collection("tasks");
3+
const userModel = firestore.collection("users");
34
const ItemModel = firestore.collection("itemTags");
45
const dependencyModel = firestore.collection("taskDependencies");
56
const userUtils = require("../utils/users");
@@ -8,7 +9,8 @@ const { chunks } = require("../utils/array");
89
const { DOCUMENT_WRITE_SIZE } = require("../constants/constants");
910
const { fromFirestoreData, toFirestoreData, buildTasks } = require("../utils/tasks");
1011
const { TASK_TYPE, TASK_STATUS, TASK_STATUS_OLD, TASK_SIZE } = require("../constants/tasks");
11-
const { IN_PROGRESS, NEEDS_REVIEW, IN_REVIEW, ASSIGNED, BLOCKED, SMOKE_TESTING, COMPLETED, SANITY_CHECK } = TASK_STATUS;
12+
const { IN_PROGRESS, NEEDS_REVIEW, IN_REVIEW, ASSIGNED, BLOCKED, SMOKE_TESTING, COMPLETED, SANITY_CHECK, BACKLOG } =
13+
TASK_STATUS;
1214
const { OLD_ACTIVE, OLD_BLOCKED, OLD_PENDING, OLD_COMPLETED } = TASK_STATUS_OLD;
1315
const { INTERNAL_SERVER_ERROR } = require("../constants/errorMessages");
1416

@@ -631,6 +633,40 @@ const updateTaskStatus = async () => {
631633
}
632634
};
633635

636+
const updateOrphanTasksStatus = async (lastOrphanTasksFilterationTimestamp) => {
637+
const lastTimestamp = Number(lastOrphanTasksFilterationTimestamp);
638+
try {
639+
const users = [];
640+
const currentTimeStamp = Date.now();
641+
642+
const usersQuerySnapshot = await userModel
643+
.where("roles.in_discord", "==", false)
644+
.where("updated_at", ">=", lastTimestamp)
645+
.where("updated_at", "<=", currentTimeStamp)
646+
.get();
647+
648+
usersQuerySnapshot.forEach((user) => users.push({ ...user.data(), id: user.id }));
649+
650+
let orphanTasksUpdatedCount = 0;
651+
652+
for (const user of users) {
653+
const tasksQuerySnapshot = await tasksModel
654+
.where("assigneeId", "==", user.id)
655+
.where("status", "not-in", [COMPLETED, BACKLOG])
656+
.get();
657+
tasksQuerySnapshot.forEach(async (taskDoc) => {
658+
orphanTasksUpdatedCount++;
659+
await tasksModel.doc(taskDoc.id).update({ status: BACKLOG });
660+
});
661+
}
662+
663+
return { orphanTasksUpdatedCount };
664+
} catch (error) {
665+
logger.error("Error marking tasks as backlog:", error);
666+
throw error;
667+
}
668+
};
669+
634670
module.exports = {
635671
updateTask,
636672
fetchTasks,
@@ -648,4 +684,5 @@ module.exports = {
648684
getBuiltTasks,
649685
getOverdueTasks,
650686
updateTaskStatus,
687+
updateOrphanTasksStatus,
651688
};

routes/tasks.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const {
88
updateSelfTask,
99
getTasksValidator,
1010
getUsersValidator,
11+
filterOrphanTasksValidator,
1112
} = require("../middlewares/validators/tasks");
1213
const authorizeRoles = require("../middlewares/authorizeRoles");
1314
const { authorizeAndAuthenticate } = require("../middlewares/authorizeUsersAndService");
@@ -67,5 +68,6 @@ router.patch("/assign/self", authenticate, invalidateCache({ invalidationKeys: [
6768
router.get("/users/discord", verifyCronJob, getUsersValidator, tasks.getUsersHandler);
6869

6970
router.post("/migration", authenticate, authorizeRoles([SUPERUSER]), tasks.updateStatus);
71+
router.post("/orphanTasks", verifyCronJob, filterOrphanTasksValidator, tasks.orphanTasks);
7072

7173
module.exports = router;

test/integration/tasks.test.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const { TASK_STATUS, tasksUsersStatus } = require("../../constants/tasks");
2121
const updateTaskStatus = require("../fixtures/tasks/tasks1")();
2222
const userStatusData = require("../fixtures/userStatus/userStatus");
2323
const tasksModel = firestore.collection("tasks");
24+
const userDBModel = firestore.collection("users");
2425
const discordService = require("../../services/discordService");
2526
const { CRON_JOB_HANDLER } = require("../../constants/bot");
2627
const { logType } = require("../../constants/logs");
@@ -1538,4 +1539,69 @@ describe("Tasks", function () {
15381539
expect(tasksLogs.body.error).to.be.equal("Error: Error occurred");
15391540
});
15401541
});
1542+
1543+
describe("POST /tasks/orphanTasks", function () {
1544+
let jwtToken;
1545+
1546+
beforeEach(async function () {
1547+
const user1 = userData[6];
1548+
user1.roles.in_discord = false;
1549+
user1.updated_at = 1712053284000;
1550+
const user2 = userData[18];
1551+
user2.updated_at = 1712064084000;
1552+
const [{ id: userId }, { id: userId2 }] = await Promise.all([userDBModel.add(user1), userDBModel.add(user2)]);
1553+
1554+
const task1 = {
1555+
assigneeId: userId,
1556+
status: "ACTIVE",
1557+
};
1558+
const task2 = {
1559+
assigneeId: userId2,
1560+
status: "COMPLETED",
1561+
};
1562+
const task3 = {
1563+
assigneeId: userId2,
1564+
status: "IN_PROGRESS",
1565+
};
1566+
await Promise.all([tasksModel.add(task1), tasksModel.add(task2), tasksModel.add(task3)]);
1567+
1568+
jwtToken = generateCronJobToken({ name: CRON_JOB_HANDLER });
1569+
});
1570+
1571+
afterEach(async function () {
1572+
await cleanDb();
1573+
});
1574+
it("Should update status of orphan tasks to BACKLOG", async function () {
1575+
const res = await chai.request(app).post("/tasks/orphanTasks").set("Authorization", `Bearer ${jwtToken}`).send({
1576+
lastOrphanTasksFilterationTimestamp: 1712040715000,
1577+
});
1578+
1579+
expect(res).to.have.status(200);
1580+
expect(res.body).to.deep.equal({
1581+
message: "Orphan tasks filtered successfully",
1582+
updatedTasksData: {
1583+
orphanTasksUpdatedCount: 2,
1584+
},
1585+
});
1586+
}).timeout(10000);
1587+
1588+
it("Should return 400 if not cron worker", async function () {
1589+
const nonSuperUserId = await addUser(appOwner);
1590+
const nonSuperUserJwt = authService.generateAuthToken({ userId: nonSuperUserId });
1591+
const res = await chai
1592+
.request(app)
1593+
.post("/tasks/orphanTasks")
1594+
.set("Authorization", `Bearer ${nonSuperUserJwt}`)
1595+
.send({
1596+
lastOrphanTasksFilterationTimestamp: 1712040715000,
1597+
});
1598+
1599+
expect(res).to.have.status(400);
1600+
expect(res.body).to.deep.equal({
1601+
statusCode: 400,
1602+
error: "Bad Request",
1603+
message: "Unauthorized Cron Worker",
1604+
});
1605+
});
1606+
});
15411607
});

0 commit comments

Comments
 (0)