Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 50 additions & 4 deletions src/controllers/taskController.js
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@
return currentwbs.save();
});
const saveProject = WBS.findById(wbsId).then((currentwbs) => {
Project.findById(currentwbs.projectId).then((currentProject) => {

Check warning on line 537 in src/controllers/taskController.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Avoid nesting promises.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HGNRest&issues=AaD6G5VswsOQLHqD06CC&open=AaD6G5VswsOQLHqD06CC&pullRequest=2031
currentProject.modifiedDatetime = Date.now();
return currentProject.save();
});
Expand Down Expand Up @@ -693,8 +693,8 @@
});

Promise.all(queries)
.then(() => res.status(200).send('Success!'))

Check warning on line 696 in src/controllers/taskController.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Avoid nesting promises.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HGNRest&issues=AaD6G5VswsOQLHqD06CD&open=AaD6G5VswsOQLHqD06CD&pullRequest=2031
.catch((err) => res.status(400).send(err));

Check warning on line 697 in src/controllers/taskController.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Avoid nesting promises.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HGNRest&issues=AaD6G5VswsOQLHqD06CE&open=AaD6G5VswsOQLHqD06CE&pullRequest=2031
});
};

Expand Down Expand Up @@ -1282,15 +1282,15 @@
const updateTaskStatus = async (req, res) => {
const { taskId } = req.params;
Task.findById(taskId).then((currentTask) => {
WBS.findById(currentTask.wbsId).then((currentwbs) => {

Check warning on line 1285 in src/controllers/taskController.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Avoid nesting promises.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HGNRest&issues=AaD6G5VswsOQLHqD06CF&open=AaD6G5VswsOQLHqD06CF&pullRequest=2031
currentwbs.modifiedDatetime = Date.now();
return currentwbs.save();
});
});

Task.findById(taskId).then((currentTask) => {
WBS.findById(currentTask.wbsId).then((currentwbs) => {

Check warning on line 1292 in src/controllers/taskController.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Avoid nesting promises.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HGNRest&issues=AaD6G5VswsOQLHqD06CG&open=AaD6G5VswsOQLHqD06CG&pullRequest=2031
Project.findById(currentwbs.projectId).then((currentProject) => {

Check warning on line 1293 in src/controllers/taskController.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Avoid nesting promises.

See more on https://sonarcloud.io/project/issues?id=OneCommunityGlobal_HGNRest&issues=AaD6G5VswsOQLHqD06CH&open=AaD6G5VswsOQLHqD06CH&pullRequest=2031
currentProject.modifiedDatetime = Date.now();
return currentProject.save();
});
Expand Down Expand Up @@ -1433,8 +1433,8 @@
const getTaskChangeLogs = async (req, res) => {
try {
const { taskId } = req.params;
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 50;
const page = Number.parseInt(req.query.page, 10) || 1;
const limit = Number.parseInt(req.query.limit, 10) || 50;
const skip = (page - 1) * limit;

const changeLogs = await TaskChangeLog.find({ taskId })
Expand All @@ -1461,12 +1461,57 @@
}
};

const getResolvedTasks = async (req, res) => {
try {
const page = Number.parseInt(req.query.page, 10) || 1;
const limit = Number.parseInt(req.query.limit, 10) || 50;
const skip = (page - 1) * limit;

const changeLogs = await TaskChangeLog.find({
changeType: 'status_change',
newValue: { $in: ['Complete', 'Resolved', 'Closed'] },
})
.sort({ timestamp: -1 })
.skip(skip)
.limit(limit)
.populate({
path: 'taskId',
select: 'taskName num resources projectId',
populate: {
path: 'resources.userID',
model: 'userProfile',
select: 'email',
},
})
.populate('userId', 'firstName lastName email')
.lean();

const total = await TaskChangeLog.countDocuments({
changeType: 'status_change',
newValue: { $in: ['Complete', 'Resolved', 'Closed'] },
});

res.status(200).json({
tasks: changeLogs,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit),
},
});
} catch (error) {
console.error('Error fetching resolved tasks:', error);
res.status(500).json({ error: error.message });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning raw error messages in production can leak implementation details

}
};

// New endpoint to get change logs for a user across all tasks
const getUserTaskChangeLogs = async (req, res) => {
try {
const { userId } = req.params;
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 50;
const page = Number.parseInt(req.query.page, 10) || 1;
const limit = Number.parseInt(req.query.limit, 10) || 50;
const skip = (page - 1) * limit;

const changeLogs = await TaskChangeLog.find({ userId })
Expand Down Expand Up @@ -1528,6 +1573,7 @@
fixTaskOverrides,
getTaskChangeLogs,
getUserTaskChangeLogs,
getResolvedTasks,
replicateTasks,
};
};
Expand Down
62 changes: 62 additions & 0 deletions src/controllers/taskController.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const emailSender = require('../utilities/emailSender');

// MongoDB Model imports
const Task = require('../models/task');
const TaskChangeLog = require('../models/taskChangeLog');
const Project = require('../models/project');
const UserProfile = require('../models/userProfile');
const TaskChangeTracker = require('../middleware/taskChangeTracker');
Expand Down Expand Up @@ -79,6 +80,7 @@ const makeSut = () => {
sendReviewReq,
getTasksForTeamsByUser,
updateTaskStatus,
getResolvedTasks,
} = taskController(Task);

return {
Expand All @@ -99,6 +101,7 @@ const makeSut = () => {
sendReviewReq,
getTasksForTeamsByUser,
updateTaskStatus,
getResolvedTasks,
};
};

Expand Down Expand Up @@ -1446,4 +1449,63 @@ describe('Unit Tests for taskController.js', () => {
expect(projectFindByIdSpy).toHaveBeenCalled();
});
});

describe('getResolvedTasks function()', () => {
beforeEach(() => {
jest.clearAllMocks();
});

test('Returns resolved tasks with pagination', async () => {
const { getResolvedTasks } = makeSut();

mockReq.query = {
page: '2',
limit: '10',
};

const resolvedTasks = [
{
_id: 'log-1',
changeType: 'status_change',
newValue: 'Resolved',
},
];

const findChain = {
sort: jest.fn().mockReturnThis(),
skip: jest.fn().mockReturnThis(),
limit: jest.fn().mockReturnThis(),
populate: jest.fn().mockReturnThis(),
lean: jest.fn().mockResolvedValue(resolvedTasks),
};

const findSpy = jest.spyOn(TaskChangeLog, 'find').mockReturnValue(findChain);
const countSpy = jest.spyOn(TaskChangeLog, 'countDocuments').mockResolvedValue(25);

await getResolvedTasks(mockReq, mockRes);

expect(findSpy).toHaveBeenCalledWith({
changeType: 'status_change',
newValue: { $in: ['Complete', 'Resolved', 'Closed'] },
});
expect(findChain.sort).toHaveBeenCalledWith({ timestamp: -1 });
expect(findChain.skip).toHaveBeenCalledWith(10);
expect(findChain.limit).toHaveBeenCalledWith(10);
expect(countSpy).toHaveBeenCalledWith({
changeType: 'status_change',
newValue: { $in: ['Complete', 'Resolved', 'Closed'] },
});

expect(mockRes.status).toHaveBeenCalledWith(200);
expect(mockRes.json).toHaveBeenCalledWith({
tasks: resolvedTasks,
pagination: {
page: 2,
limit: 10,
total: 25,
pages: 3,
},
});
});
});
});
1 change: 1 addition & 0 deletions src/routes/taskRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const routes = function (task, userProfile) {

taskRouter.route('/tasks/replicate/:id').post(controller.replicateTasks);
taskRouter.route('/tasks/fix-overrides/:wbsId').post(controller.fixTaskOverrides);
taskRouter.route('/tasks/resolved').get(controller.getResolvedTasks);

// New routes for task change logs
taskRouter.route('/task/:taskId/changeLogs').get(controller.getTaskChangeLogs);
Expand Down
Loading