Skip to content

Commit

Permalink
Merge pull request #1996 from Real-Dev-Squad/feat/add-backlog-orphan-…
Browse files Browse the repository at this point in the history
…tasks

feat: Filter orphan tasks and mark them BACKLOG
  • Loading branch information
lakshayman authored Apr 3, 2024
2 parents 19829f7 + d6a953e commit 0a199eb
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 8 deletions.
14 changes: 14 additions & 0 deletions controllers/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,19 @@ const updateStatus = async (req, res) => {
}
};

const orphanTasks = async (req, res) => {
try {
const { lastOrphanTasksFilterationTimestamp = 0 } = req.body;

const updatedTasksData = await tasks.updateOrphanTasksStatus(lastOrphanTasksFilterationTimestamp);

return res.status(200).json({ message: "Orphan tasks filtered successfully", updatedTasksData });
} catch (error) {
logger.error("Error in filtering orphan tasks", error);
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
}
};

const getUsersHandler = async (req, res) => {
try {
const { size, cursor, q: queryString } = req.query;
Expand Down Expand Up @@ -548,4 +561,5 @@ module.exports = {
assignTask,
updateStatus,
getUsersHandler,
orphanTasks,
};
8 changes: 1 addition & 7 deletions middlewares/validators/discordactions.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const Joi = require("joi");
const { validateMillisecondsTimestamp } = require("./utils");

const validateGroupRoleBody = async (req, res, next) => {
const schema = Joi.object({
Expand Down Expand Up @@ -28,13 +29,6 @@ const validateMemberRoleBody = async (req, res, next) => {
}
};

const validateMillisecondsTimestamp = async (reqBody, timestampProperty) => {
const schema = Joi.object({
[timestampProperty]: Joi.number().unit("milliseconds").required(),
});
return schema.validateAsync(reqBody);
};

const validateUpdateUsersNicknameStatusBody = async (req, res, next) => {
try {
await validateMillisecondsTimestamp(req.body, "lastNicknameUpdate");
Expand Down
12 changes: 12 additions & 0 deletions middlewares/validators/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const { Operators } = require("../../typeDefinitions/rqlParser");
const { daysOfWeek } = require("../../constants/constants");
const TASK_STATUS_ENUM = Object.values(TASK_STATUS);
const MAPPED_TASK_STATUS_ENUM = Object.keys(MAPPED_TASK_STATUS);
const { validateMillisecondsTimestamp } = require("./utils");

const createTask = async (req, res, next) => {
const schema = joi
Expand Down Expand Up @@ -262,10 +263,21 @@ const getUsersValidator = async (req, res, next) => {
res.boom.badRequest(error.details[0].message);
}
};

const filterOrphanTasksValidator = async (req, res, next) => {
try {
await validateMillisecondsTimestamp(req.body, "lastOrphanTasksFilterationTimestamp");
next();
} catch (error) {
logger.error(`Error while validating request body for Orphan Tasks Filteration payload : ${error}`);
res.boom.badRequest(error);
}
};
module.exports = {
createTask,
updateTask,
updateSelfTask,
getTasksValidator,
getUsersValidator,
filterOrphanTasksValidator,
};
12 changes: 12 additions & 0 deletions middlewares/validators/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const Joi = require("joi");

const validateMillisecondsTimestamp = async (reqBody, timestampProperty) => {
const schema = Joi.object({
[timestampProperty]: Joi.number().unit("milliseconds").required(),
});
return schema.validateAsync(reqBody);
};

module.exports = {
validateMillisecondsTimestamp
}
39 changes: 38 additions & 1 deletion models/tasks.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const firestore = require("../utils/firestore");
const tasksModel = firestore.collection("tasks");
const userModel = firestore.collection("users");
const ItemModel = firestore.collection("itemTags");
const dependencyModel = firestore.collection("taskDependencies");
const userUtils = require("../utils/users");
Expand All @@ -8,7 +9,8 @@ const { chunks } = require("../utils/array");
const { DOCUMENT_WRITE_SIZE } = require("../constants/constants");
const { fromFirestoreData, toFirestoreData, buildTasks } = require("../utils/tasks");
const { TASK_TYPE, TASK_STATUS, TASK_STATUS_OLD, TASK_SIZE } = require("../constants/tasks");
const { IN_PROGRESS, NEEDS_REVIEW, IN_REVIEW, ASSIGNED, BLOCKED, SMOKE_TESTING, COMPLETED, SANITY_CHECK } = TASK_STATUS;
const { IN_PROGRESS, NEEDS_REVIEW, IN_REVIEW, ASSIGNED, BLOCKED, SMOKE_TESTING, COMPLETED, SANITY_CHECK, BACKLOG } =
TASK_STATUS;
const { OLD_ACTIVE, OLD_BLOCKED, OLD_PENDING, OLD_COMPLETED } = TASK_STATUS_OLD;
const { INTERNAL_SERVER_ERROR } = require("../constants/errorMessages");

Expand Down Expand Up @@ -631,6 +633,40 @@ const updateTaskStatus = async () => {
}
};

const updateOrphanTasksStatus = async (lastOrphanTasksFilterationTimestamp) => {
const lastTimestamp = Number(lastOrphanTasksFilterationTimestamp);
try {
const users = [];
const currentTimeStamp = Date.now();

const usersQuerySnapshot = await userModel
.where("roles.in_discord", "==", false)
.where("updated_at", ">=", lastTimestamp)
.where("updated_at", "<=", currentTimeStamp)
.get();

usersQuerySnapshot.forEach((user) => users.push({ ...user.data(), id: user.id }));

let orphanTasksUpdatedCount = 0;

for (const user of users) {
const tasksQuerySnapshot = await tasksModel
.where("assigneeId", "==", user.id)
.where("status", "not-in", [COMPLETED, BACKLOG])
.get();
tasksQuerySnapshot.forEach(async (taskDoc) => {
orphanTasksUpdatedCount++;
await tasksModel.doc(taskDoc.id).update({ status: BACKLOG });
});
}

return { orphanTasksUpdatedCount };
} catch (error) {
logger.error("Error marking tasks as backlog:", error);
throw error;
}
};

module.exports = {
updateTask,
fetchTasks,
Expand All @@ -648,4 +684,5 @@ module.exports = {
getBuiltTasks,
getOverdueTasks,
updateTaskStatus,
updateOrphanTasksStatus,
};
2 changes: 2 additions & 0 deletions routes/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const {
updateSelfTask,
getTasksValidator,
getUsersValidator,
filterOrphanTasksValidator,
} = require("../middlewares/validators/tasks");
const authorizeRoles = require("../middlewares/authorizeRoles");
const { authorizeAndAuthenticate } = require("../middlewares/authorizeUsersAndService");
Expand Down Expand Up @@ -67,5 +68,6 @@ router.patch("/assign/self", authenticate, invalidateCache({ invalidationKeys: [
router.get("/users/discord", verifyCronJob, getUsersValidator, tasks.getUsersHandler);

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

module.exports = router;
66 changes: 66 additions & 0 deletions test/integration/tasks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const { TASK_STATUS, tasksUsersStatus } = require("../../constants/tasks");
const updateTaskStatus = require("../fixtures/tasks/tasks1")();
const userStatusData = require("../fixtures/userStatus/userStatus");
const tasksModel = firestore.collection("tasks");
const userDBModel = firestore.collection("users");
const discordService = require("../../services/discordService");
const { CRON_JOB_HANDLER } = require("../../constants/bot");
const { logType } = require("../../constants/logs");
Expand Down Expand Up @@ -1538,4 +1539,69 @@ describe("Tasks", function () {
expect(tasksLogs.body.error).to.be.equal("Error: Error occurred");
});
});

describe("POST /tasks/orphanTasks", function () {
let jwtToken;

beforeEach(async function () {
const user1 = userData[6];
user1.roles.in_discord = false;
user1.updated_at = 1712053284000;
const user2 = userData[18];
user2.updated_at = 1712064084000;
const [{ id: userId }, { id: userId2 }] = await Promise.all([userDBModel.add(user1), userDBModel.add(user2)]);

const task1 = {
assigneeId: userId,
status: "ACTIVE",
};
const task2 = {
assigneeId: userId2,
status: "COMPLETED",
};
const task3 = {
assigneeId: userId2,
status: "IN_PROGRESS",
};
await Promise.all([tasksModel.add(task1), tasksModel.add(task2), tasksModel.add(task3)]);

jwtToken = generateCronJobToken({ name: CRON_JOB_HANDLER });
});

afterEach(async function () {
await cleanDb();
});
it("Should update status of orphan tasks to BACKLOG", async function () {
const res = await chai.request(app).post("/tasks/orphanTasks").set("Authorization", `Bearer ${jwtToken}`).send({
lastOrphanTasksFilterationTimestamp: 1712040715000,
});

expect(res).to.have.status(200);
expect(res.body).to.deep.equal({
message: "Orphan tasks filtered successfully",
updatedTasksData: {
orphanTasksUpdatedCount: 2,
},
});
}).timeout(10000);

it("Should return 400 if not cron worker", async function () {
const nonSuperUserId = await addUser(appOwner);
const nonSuperUserJwt = authService.generateAuthToken({ userId: nonSuperUserId });
const res = await chai
.request(app)
.post("/tasks/orphanTasks")
.set("Authorization", `Bearer ${nonSuperUserJwt}`)
.send({
lastOrphanTasksFilterationTimestamp: 1712040715000,
});

expect(res).to.have.status(400);
expect(res.body).to.deep.equal({
statusCode: 400,
error: "Bad Request",
message: "Unauthorized Cron Worker",
});
});
});
});

0 comments on commit 0a199eb

Please sign in to comment.