Skip to content
Merged
38 changes: 37 additions & 1 deletion backend/controllers/project.controller.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { Project } = require('../models');
const { Project, User } = require('../models');

const ProjectController = {};

Expand Down Expand Up @@ -66,4 +66,40 @@ ProjectController.destroy = async function (req, res) {
}
};

ProjectController.updateManagedByUsers = async function (req, res) {
const { ProjectId } = req.params;
const { action, userId } = req.body; // action - 'add' or 'remove'

try {
// Update project's managedByUsers and the user's managedProjects
const project = await Project.findById(ProjectId);
let managedByUsers = project.managedByUsers || [];

const user = await User.findById(userId);
let managedProjects = user.managedProjects || [];

if (action === 'add') {
managedByUsers = [...new Set([...managedByUsers, userId])];
managedProjects = [...new Set([...managedProjects, ProjectId])];
} else {
// remove case
managedByUsers = managedByUsers.filter((id) => id !== userId);
managedProjects = managedProjects.filter((id) => id !== ProjectId);
}

// Update project's managedByUsers
project.managedByUsers = managedByUsers;
await project.save({ validateBeforeSave: false });

// Update user's managedProjects
user.managedProjects = managedProjects;
await user.save({ validateBeforeSave: false });

return res.status(200).send({ project, user });
} catch (err) {
console.log(err);
return res.sendStatus(400);
}
};

module.exports = ProjectController;
43 changes: 43 additions & 0 deletions backend/controllers/user.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,4 +266,47 @@ UserController.logout = async function (req, res) {
return res.clearCookie('token').status(200).send('Successfully logged out.');
};

// Update user's managedProjects
UserController.updateManagedProjects = async function (req, res) {
const { headers } = req;
const { UserId } = req.params;
const { action, projectId } = req.body; // action - 'add' or 'remove'
// console.log('action:', action, 'projectId:', projectId);

if (headers['x-customrequired-header'] !== expectedHeader) {
return res.sendStatus(403);
}

try {
// Update user's managedProjects and the project's managedByUsers
const user = await User.findById(UserId);
let managedProjects = user.managedProjects || [];

const project = await Project.findById(projectId);
let managedByUsers = project.managedByUsers || [];

if (action === 'add') {
managedProjects = [...managedProjects, projectId];
managedByUsers = [...managedByUsers, UserId];
} else {
// remove case
managedProjects = managedProjects.filter((id) => id !== projectId);
managedByUsers = managedByUsers.filter((id) => id !== UserId);
}

// Update user's managedProjects
user.managedProjects = managedProjects;
await user.save({ validateBeforeSave: false });

// Update project's managedByUsers
project.managedByUsers = managedByUsers;
await project.save({ validateBeforeSave: false });

return res.status(200).send({ user, project });
} catch (err) {
console.log(err);
return res.sendStatus(400);
}
};

module.exports = UserController;
3 changes: 2 additions & 1 deletion backend/routers/projects.router.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ router.get('/:ProjectId', ProjectController.project_by_id);

router.put('/:ProjectId', AuthUtil.verifyCookie, ProjectController.update);

router.patch('/:ProjectId', AuthUtil.verifyCookie, ProjectController.update);
// Update project's managedByUsers in db
router.patch('/:ProjectId', AuthUtil.verifyCookie, ProjectController.updateManagedByUsers);

module.exports = router;
88 changes: 84 additions & 4 deletions backend/routers/projects.router.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ describe('Unit testing for Projects router', () => {

// Tests
expect(ProjectController.create).toHaveBeenCalledWith(
expect.objectContaining({ body: newProject }), // Check if newProject in body is parsed
expect.objectContaining({ body: newProject }), // Check if newProject in body is parsed
expect.anything(), // Mock response
expect.anything(), // Mock next
);
Expand Down Expand Up @@ -234,7 +234,7 @@ describe('Unit testing for Projects router', () => {
});

const updatedProject = {
id: '1',
id: 'projectId1',
name: 'updated project1',
description: 'updated testing',
githubIdentifier: 'gitHubTest3',
Expand All @@ -251,7 +251,7 @@ describe('Unit testing for Projects router', () => {
lookingDescription: 'n/a',
recruitingCategories: ['n/a'],
partners: ['n/a'],
managedByUsers: ['n/a'],
managedByUsers: ['userId1'],
};

const ProjectId = updatedProject.id;
Expand All @@ -274,7 +274,7 @@ describe('Unit testing for Projects router', () => {

// Tests
expect(ProjectController.update).toHaveBeenCalledWith(
expect.objectContaining({ params: { ProjectId }}), // Check if ProjectId is parsed from params
expect.objectContaining({ params: { ProjectId } }), // Check if ProjectId is parsed from params
expect.anything(), // Mock response
expect.anything(), // Mock next
);
Expand All @@ -284,5 +284,85 @@ describe('Unit testing for Projects router', () => {
// Marks completion of tests
done();
});

const updatedUser = {
id: 'userId1',
name: 'Updated User',
email: '[email protected]',
managedProjects: ['projectId1'],
};

const userId = updatedUser.id;

it("should add to the project's managedByUsers and the user's managedProjects fields with PATCH /api/projects/:ProjectId", async (done) => {
// Mock ProjectController.updateManagedByUsers method when this route is called
ProjectController.updateManagedByUsers.mockImplementationOnce((req, res) => {
res.status(200).send({ project: updatedProject, user: updatedUser });
});

// Mock PUT API call
const response = await request
.patch(`/api/projects/${ProjectId}`)
.send({ action: 'add', userId });

// Middlware assertions
expect(mockVerifyCookie).toHaveBeenCalledWith(
expect.any(Object),
expect.any(Object),
expect.any(Function),
);

// Tests
expect(ProjectController.updateManagedByUsers).toHaveBeenCalledWith(
expect.objectContaining({
params: { ProjectId },
body: { action: 'add', userId },
}), // Check if ProjectId is parsed from params
expect.anything(), // Mock response
expect.anything(), // Mock next
);
expect(response.status).toBe(200);
expect(response.body).toEqual({ project: updatedProject, user: updatedUser });

// Marks completion of tests
done();
});

it("should remove user from the project's managedByUsers and remove project from the user's managedProjects fields with PATCH /api/projects/:ProjectId", async (done) => {
updatedProject.managedByUsers = [];
updatedUser.managedProjects = [];

// Mock ProjectController.updateManagedByUsers method when this route is called
ProjectController.updateManagedByUsers.mockImplementationOnce((req, res) => {
res.status(200).send({ project: updatedProject, user: updatedUser });
});

// Mock PUT API call
const response = await request
.patch(`/api/projects/${ProjectId}`)
.send({ action: 'remove', userId });

// Middlware assertions
expect(mockVerifyCookie).toHaveBeenCalledWith(
expect.any(Object),
expect.any(Object),
expect.any(Function),
);

// Tests
expect(ProjectController.updateManagedByUsers).toHaveBeenCalledWith(
expect.objectContaining({
params: { ProjectId },
body: { action: 'remove', userId },
}), // Check if ProjectId is parsed from params
expect.anything(), // Mock response
expect.anything(), // Mock next
);
expect(response.status).toBe(200);
expect(response.body).toEqual({ project: updatedProject, user: updatedUser });

// Marks completion of tests
done();
});
});
});
3 changes: 3 additions & 0 deletions backend/routers/users.router.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ router.get('/:UserId', UserController.user_by_id);

router.patch('/:UserId', UserController.update);

// Update user projects in db
router.patch('/:UserId/managedProjects', UserController.updateManagedProjects);

router.delete('/:UserId', UserController.delete);

module.exports = router;
Loading
Loading