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
4 changes: 4 additions & 0 deletions constants/routes.constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ const hackerRoutes = {
requestType: Constants.REQUEST_TYPES.PATCH,
uri: "/api/hacker/accept/" + Constants.ROLE_CATEGORIES.ALL,
},
patchAcceptHackerByEmail: {
requestType: Constants.REQUEST_TYPES.PATCH,
uri: "/api/hacker/acceptEmail/" + Constants.ROLE_CATEGORIES.ALL,
},
postAnySendWeekOfEmail: {
requestType: Constants.REQUEST_TYPES.POST,
uri: "/api/hacker/email/weekOf/" + Constants.ROLE_CATEGORIES.ALL
Expand Down
86 changes: 86 additions & 0 deletions middlewares/hacker.middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,40 @@ async function sendStatusUpdateEmail(req, res, next) {
}
}

/**
* Sends a preset email to a user if a status change occured with email params.
* @param {{body: {status?: string}, params: {email: string}}} req
* @param {*} res
* @param {(err?:*)=>void} next
*/
async function completeStatusUpdateEmail(req, res, next) {
//skip if the status doesn't exist
if (!req.body.hacker.status) {
return next();
} else {
// send it to the hacker that is being updated.
const hacker = await Services.Hacker.findById(req.body.hacker._id);
const account = await Services.Account.findById(hacker.accountId);
if (!hacker) {
return next({
status: 404,
message: Constants.Error.HACKER_404_MESSAGE
});
} else if (!account) {
return next({
status: 500,
message: Constants.Error.GENERIC_500_MESSAGE
});
}
Services.Email.sendStatusUpdate(
account.firstName,
account.email,
req.body.hacker.status,
next
);
}
}

/**
* Sends an email telling the user that they have applied. This is used exclusively when we POST a hacker.
* @param {{body: {hacker: {accountId: string}}}} req
Expand Down Expand Up @@ -523,6 +557,40 @@ async function updateHacker(req, res, next) {
}
}

/**
* Updates a hacker that is specified by req.body.hacker._id, and then sets req.email
* to the email of the hacker, found in Account.
* @param {{params:{_id: string}, body: *}} req
* @param {*} res
* @param {*} next
*/
async function obtainEmailByHackerId(req, res, next) {
const hacker = await Services.Hacker.findById(req.body.hacker._id);
if (hacker) {
const acct = await Services.Account.findById(hacker.accountId);
if (!acct) {
return next({
status: 500,
message: Constants.Error.HACKER_UPDATE_500_MESSAGE,
data: {
hackerId: hacker.id,
accountId: hacker.accountId
}
});
}
req.email = acct.email;
return next();
} else {
return next({
status: 404,
message: Constants.Error.HACKER_404_MESSAGE,
data: {
id: req.params.id
}
});
}
}

/**
* Sets req.body.status to Accepted for next middleware.
* @param {{params:{id: string}, body: *}} req
Expand All @@ -531,6 +599,19 @@ async function updateHacker(req, res, next) {
*/
function parseAccept(req, res, next) {
req.body.status = Constants.General.HACKER_STATUS_ACCEPTED;
req.hackerId = req.params.id;
next();
}

/**
* Sets req.body.hacker.status to Accepted for next middleware.
* @param {{params:{email: string}, body: *}} req
* @param {*} res
* @param {*} next
*/
function parseAcceptEmail(req, res, next) {
req.body.hacker.status = Constants.General.HACKER_STATUS_ACCEPTED;

Choose a reason for hiding this comment

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

Is there a reason this is req.body.hacker.status, i think the update middleware uses req.body.status right?

Copy link
Member

Choose a reason for hiding this comment

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

agreed, the comment says req.body.status ^^ Double check it unless you changed it.

req.hackerId = req.body.hacker._id;
next();
}

Expand Down Expand Up @@ -652,7 +733,9 @@ module.exports = {
sendAppliedStatusEmail
),
updateHacker: Middleware.Util.asyncMiddleware(updateHacker),
obtainEmailByHackerId: Middleware.Util.asyncMiddleware(obtainEmailByHackerId),
parseAccept: parseAccept,
parseAcceptEmail: parseAcceptEmail,
validateConfirmedStatusFromAccountId: Middleware.Util.asyncMiddleware(
validateConfirmedStatusFromAccountId
),
Expand All @@ -662,6 +745,9 @@ module.exports = {
validateConfirmedStatusFromObject: Middleware.Util.asyncMiddleware(
validateConfirmedStatusFromObject
),
completeStatusUpdateEmail: Middleware.Util.asyncMiddleware(
completeStatusUpdateEmail
),
checkDuplicateAccountLinks: Middleware.Util.asyncMiddleware(
checkDuplicateAccountLinks
),
Expand Down
31 changes: 31 additions & 0 deletions routes/api/hacker.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,37 @@ module.exports = {
Middleware.Hacker.sendStatusUpdateEmail,
Controllers.Hacker.updatedHacker
);

/**
* @api {patch} /hacker/acceptEmail/:email accept a Hacker by email
* @apiName acceptHacker

Choose a reason for hiding this comment

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

Nit: update to be for accept by email

* @apiGroup Hacker
* @apiVersion 2.0.0
*
* @apiSuccess {string} message Success message
* @apiSuccess {object} data Hacker object
* @apiSuccessExample {object} Success-Response:
* {
* "message": "Changed hacker information",
* "data": {
* "status": "Accepted"
* }
* }
* @apiPermission Administrator
*/
hackerRouter
.route("/acceptEmail/:email")
.patch(
Middleware.Auth.ensureAuthenticated(),
Middleware.Auth.ensureAuthorized([Services.Hacker.findByEmail]),
Middleware.Validator.RouteParam.emailValidator,
Middleware.parseBody.middleware,
Middleware.Hacker.findByEmail,
Copy link
Member

Choose a reason for hiding this comment

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

is middleware hacker findByEmail and then calling middleware findByEmail duplicate work?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I believe so I'll fix it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Edit: req.body.hacker is changed by findByEmail so no it is not duplicated work

Middleware.Hacker.parseAcceptEmail,
Middleware.Hacker.obtainEmailByHackerId,
Middleware.Hacker.completeStatusUpdateEmail,
Controllers.Hacker.updatedHacker
);

/**
* @api {patch} /hacker/checkin/:id update a hacker's status to be 'Checked-in'. Note that the Hacker must eitehr be Accepted or Confirmed.
Expand Down
92 changes: 92 additions & 0 deletions tests/hacker.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const volunteerAccount0 = util.account.volunteerAccounts.stored[0];

const newHackerAccount0 = util.account.hackerAccounts.new[0];
const newHacker0 = util.hacker.newHacker0;
const invalidHackerAccount0 = util.account.hackerAccounts.invalid;
const invalidHacker0 = util.hacker.invalidHacker0;
const invalidHacker2 = util.hacker.invalidHacker2;
const newHacker1 = util.hacker.newHacker1;
Expand Down Expand Up @@ -638,6 +639,7 @@ describe("PATCH update one hacker", function() {
});
});

//should FAIL on authentication
it("should FAIL to accept a hacker on /api/hacker/accept/:id due to authentication", function(done) {
chai.request(server.app)
.patch(`/api/hacker/accept/${TeamHacker0._id}`)
Expand Down Expand Up @@ -730,6 +732,96 @@ describe("PATCH update one hacker", function() {
});
});

it("should FAIL to accept a hacker on /api/hacker/acceptEmail/:email due to authentication", function(done) {
chai.request(server.app)
.patch(`/api/hacker/acceptEmail/${teamHackerAccount0.email}`)
.type("application/json")
.send()
.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);
done();
});
});

// should FAIL due to authorization
it("should FAIL to accept hacker info due to lack of authorization on /api/hacker/acceptEmail/:email", function(done) {
util.auth.login(agent, noTeamHackerAccount0, (error) => {
if (error) {
agent.close();
return done(error);
}
return agent
.patch(`/api/hacker/acceptEmail/${teamHackerAccount0.email}`)
.type("application/json")
.send()
.end(function(err, res) {
res.should.have.status(403);
res.should.be.json;
res.body.should.have.property("message");
res.body.message.should.equal(
Constants.Error.AUTH_403_MESSAGE
);
res.body.should.have.property("data");

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

it("should FAIL to accept an invalid hacker's info on /api/hacker/acceptEmail/:email", function(done) {
util.auth.login(agent, Admin0, (error) => {
if (error) {
agent.close();
return done(error);
}
return agent
.patch(`/api/hacker/acceptEmail/${invalidHackerAccount0[0].email}`)
.type("application/json")
.send()
.end(function(err, res) {
res.should.have.status(404);
res.should.be.json;
res.body.should.have.property("message");
res.body.message.should.equal(
Constants.Error.ACCOUNT_404_MESSAGE
);
res.body.should.have.property("data");

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

it("should SUCCEED and accept a hacker on /api/hacker/acceptEmail/:email as an Admin", function(done) {
util.auth.login(agent, Admin0, (error) => {
if (error) {
agent.close();
return done(error);
}
return agent
.patch(`/api/hacker/acceptEmail/${teamHackerAccount0.email}`)
.type("application/json")
.send()
.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.HACKER_UPDATE
);
res.body.should.have.property("data");
chai.assert.equal(
res.body.data.hacker.status,
"Accepted"
);
done();
});
});
});

// should succeed on admin case
it("should SUCCEED and update a hacker using admin power", function(done) {
util.auth.login(agent, Admin0, (error) => {
Expand Down