Skip to content
Merged
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
3 changes: 2 additions & 1 deletion constants/role.constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ const hackerRole = {

Constants.Routes.teamRoutes.join,
Constants.Routes.teamRoutes.post,
Constants.Routes.teamRoutes.get
Constants.Routes.teamRoutes.get,
Constants.Routes.teamRoutes.leave
]
};

Expand Down
4 changes: 4 additions & 0 deletions constants/routes.constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ const teamRoutes = {
requestType: Constants.REQUEST_TYPES.PATCH,
uri: "/api/team/join/",
},
"leave": {
requestType: Constants.REQUEST_TYPES.PATCH,
uri: "/api/team/leave/",
},
};

const volunteerRoutes = {
Expand Down
2 changes: 2 additions & 0 deletions constants/success.constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const TEAM_GET_BY_ID = "Team found by id.";
const TEAM_CREATE = "Team creation successful.";
const TEAM_JOIN = "Team join successful.";
const TEAM_READ = "Team retrieval successful.";
const TEAM_HACKER_LEAVE = "Removal from team successful.";

const VOLUNTEER_CREATE = "Volunteer creation successful.";

Expand Down Expand Up @@ -79,6 +80,7 @@ module.exports = {
TEAM_CREATE: TEAM_CREATE,
TEAM_JOIN: TEAM_JOIN,
TEAM_READ: TEAM_READ,
TEAM_HACKER_LEAVE: TEAM_HACKER_LEAVE,

VOLUNTEER_CREATE: VOLUNTEER_CREATE,
};
16 changes: 16 additions & 0 deletions controllers/team.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,24 @@ function createdTeam(req, res) {
});
}

/**
* @function leftTeam
* @param {*} req
* @param {*} res
* @return {JSON} Success status
* @description return success message of removing self from team.
*/

function leftTeam(req, res) {
return res.status(200).json({
message: Constants.Success.TEAM_HACKER_LEAVE,
data: {},
});
}

module.exports = {
joinedTeam: joinedTeam,
createdTeam: createdTeam,
showTeam: showTeam,
leftTeam: leftTeam,
};
40 changes: 37 additions & 3 deletions docs/api/api_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -2261,6 +2261,43 @@ define({
"url": "https://api.mchacks.ca/api/team/"
}]
},
{
"type": "patch",
"url": "/team/leave/",
"title": "Allows a logged in hacker to leave current team",
"name": "deleteSelfFromTeam",
"group": "Team",
"version": "1.1.1",
"success": {
"fields": {
"Success 200": [{
"group": "Success 200",
"type": "string",
"optional": false,
"field": "message",
"description": "<p>Success message</p>"
},
{
"group": "Success 200",
"type": "object",
"optional": false,
"field": "data",
"description": "<p>{}</p>"
}
]
},
"examples": [{
"title": "Success-Response: ",
"content": "{\n \"message\": \"Removal from team successful.\", \n \"data\": {}\n}",
"type": "object"
}]
},
"filename": "routes/api/team.js",
"groupTitle": "Team",
"sampleRequest": [{
"url": "https://api.mchacks.ca/api/team/leave/"
}]
},
{
"type": "get",
"url": "/team/:id",
Expand Down Expand Up @@ -2375,9 +2412,6 @@ define({
"type": "object"
}]
},
"permission": [{
"name": "Administrator"
}],
"filename": "routes/api/team.js",
"groupTitle": "Team",
"sampleRequest": [{
Expand Down
40 changes: 37 additions & 3 deletions docs/api/api_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -2260,6 +2260,43 @@
"url": "https://api.mchacks.ca/api/team/"
}]
},
{
"type": "patch",
"url": "/team/leave/",
"title": "Allows a logged in hacker to leave current team",
"name": "deleteSelfFromTeam",
"group": "Team",
"version": "1.1.1",
"success": {
"fields": {
"Success 200": [{
"group": "Success 200",
"type": "string",
"optional": false,
"field": "message",
"description": "<p>Success message</p>"
},
{
"group": "Success 200",
"type": "object",
"optional": false,
"field": "data",
"description": "<p>{}</p>"
}
]
},
"examples": [{
"title": "Success-Response: ",
"content": "{\n \"message\": \"Removal from team successful.\", \n \"data\": {}\n}",
"type": "object"
}]
},
"filename": "routes/api/team.js",
"groupTitle": "Team",
"sampleRequest": [{
"url": "https://api.mchacks.ca/api/team/leave/"
}]
},
{
"type": "get",
"url": "/team/:id",
Expand Down Expand Up @@ -2374,9 +2411,6 @@
"type": "object"
}]
},
"permission": [{
"name": "Administrator"
}],
"filename": "routes/api/team.js",
"groupTitle": "Team",
"sampleRequest": [{
Expand Down
30 changes: 15 additions & 15 deletions docs/api/api_project.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
define({
"name": "hackerAPI",
"version": "0.0.8",
"description": "Documentation for the API used for mchacks",
"defaultVersion": "0.0.8",
"title": "hackerAPI documentation",
"url": "https://api.mchacks.ca/api",
"sampleUrl": "https://api.mchacks.ca/api",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2019-01-07T03:26:47.443Z",
"url": "http://apidocjs.com",
"version": "0.17.7"
}
define({
"name": "hackerAPI",
"version": "0.0.8",
"description": "Documentation for the API used for mchacks",
"defaultVersion": "0.0.8",
"title": "hackerAPI documentation",
"url": "https://api.mchacks.ca/api",
"sampleUrl": "https://api.mchacks.ca/api",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2019-01-08T22:07:07.661Z",
"url": "http://apidocjs.com",
"version": "0.17.7"
}
});
30 changes: 15 additions & 15 deletions docs/api/api_project.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"name": "hackerAPI",
"version": "0.0.8",
"description": "Documentation for the API used for mchacks",
"defaultVersion": "0.0.8",
"title": "hackerAPI documentation",
"url": "https://api.mchacks.ca/api",
"sampleUrl": "https://api.mchacks.ca/api",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2019-01-07T03:26:47.443Z",
"url": "http://apidocjs.com",
"version": "0.17.7"
}
{
"name": "hackerAPI",
"version": "0.0.8",
"description": "Documentation for the API used for mchacks",
"defaultVersion": "0.0.8",
"title": "hackerAPI documentation",
"url": "https://api.mchacks.ca/api",
"sampleUrl": "https://api.mchacks.ca/api",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2019-01-08T22:07:07.661Z",
"url": "http://apidocjs.com",
"version": "0.17.7"
}
}
31 changes: 30 additions & 1 deletion middlewares/team.middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,34 @@ async function findById(req, res, next) {
return next();
}

/**
* @async
* @function deleteUserFromTeam
* @param {{user: {id: ObjectId}} req
* @param {*} res
* @return {JSON} Success or error status
* @description Removes the hacker associated with req.user.id from the team under teamId. If hacker is not part of a team, it does nothing.
*/
async function deleteUserFromTeam(req, res, next) {
const hacker = await Services.Hacker.findByAccountId(req.user.id);

if (!hacker) {
return next({
status: 404,
message: Constants.Error.HACKER_404_MESSAGE,
data: {
id: req.user.id
}
});
}
const oldTeamId = hacker.teamId;
if (oldTeamId) {
await Services.Team.removeMember(oldTeamId, hacker._id);
await Services.Team.removeTeamIfEmpty(oldTeamId);
}
next();
Copy link
Member

Choose a reason for hiding this comment

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

Should we put in some kind of error if oldTeamId doesn't exist? What do you think should be the output if the hacker was never part of a team?

Copy link
Member Author

Choose a reason for hiding this comment

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

Since the resulting database state is the same, returning success should be fine.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good

}

/**
* @async
* @function updateHackerTeam
Expand Down Expand Up @@ -343,7 +371,7 @@ async function parseNewTeam(req, res, next) {
}

// hacker should not be in another team
if (hacker.teamId !== undefined) {
if (hacker.teamId !== undefined && hacker.teamId !== null) {
return next({
status: 409,
message: Constants.Error.TEAM_MEMBER_409_MESSAGE,
Expand All @@ -366,4 +394,5 @@ module.exports = {
parseNewTeam: Util.asyncMiddleware(parseNewTeam),
ensureFreeTeamName: Util.asyncMiddleware(ensureFreeTeamName),
populateMemberAccountsById: Util.asyncMiddleware(populateMemberAccountsById),
deleteUserFromTeam: Util.asyncMiddleware(deleteUserFromTeam),
};
22 changes: 21 additions & 1 deletion routes/api/team.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ module.exports = {
* "message": "Team join successful.",
* "data": {}
* }
* @apiPermission Administrator
*/
teamRouter.route("/join/").patch(
Middleware.Auth.ensureAuthenticated(),
Expand All @@ -87,6 +86,27 @@ module.exports = {
Controllers.Team.joinedTeam
);

/**
* @api {patch} /team/leave/ Allows a logged in hacker to leave current team
* @apiName deleteSelfFromTeam
* @apiGroup Team
* @apiVersion 1.1.1
*
* @apiSuccess {string} message Success message
* @apiSuccess {object} data {}
* @apiSuccessExample {object} Success-Response:
* {
* "message": "Removal from team successful.",
* "data": {}
* }
*/
teamRouter.route("/leave").patch(
Middleware.Auth.ensureAuthenticated(),
Middleware.Auth.ensureAuthorized(),
Middleware.Team.deleteUserFromTeam,
Controllers.Team.leftTeam
);

/**
* @api {get} /team/:id get a team's information
* @apiName getTeam
Expand Down
4 changes: 2 additions & 2 deletions services/team.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ async function removeMember(teamId, hackerId) {
return null;
}

return Team.update({
return Team.findOneAndUpdate({
_id: teamId
}, {
$pull: {
members: [hackerId]
members: hackerId
}
});
}
Expand Down
34 changes: 34 additions & 0 deletions tests/team.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,4 +358,38 @@ describe("PATCH change team", function () {
});
});
});

it("should SUCCEED and leave a team.", function (done) {
util.auth.login(agent, util.account.Account1, (error) => {
if (error) {
agent.close();
return done(error);
}
return agent
.patch(`/api/team/leave/`)
.type("application/json")
.end(function (err, res) {
res.should.have.status(200);
res.should.be.json;
res.body.should.have.property("message");
res.body.message.should.equal(Constants.Success.TEAM_HACKER_LEAVE);
res.body.should.have.property("data");
done();
});
});
});
it("should FAIL to leave a team due to invalid authentication.", function (done) {
chai.request(server.app)
.patch(`/api/team/leave/`)
.type("application/json")
.end(function (err, res) {
res.should.have.status(401);
res.should.be.json;
res.body.should.have.property("message");
res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE);
res.body.should.have.property("data");

done();
});
});
});