From eb64c3704ccc5a66fb6e9c8bd5138b712b23572b Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Fri, 29 Nov 2019 01:21:27 -0500 Subject: [PATCH 01/28] Fixed validation for new and improved hacker object --- middlewares/hacker.middleware.js | 832 ++++++------- middlewares/parse-body.middleware.js | 38 +- middlewares/validators/hacker.validator.js | 82 +- middlewares/validators/validator.helper.js | 1226 +++++++++++++------- models/hacker.model.js | 216 ++-- services/account.service.js | 93 +- services/hacker.service.js | 231 ++-- 7 files changed, 1600 insertions(+), 1118 deletions(-) diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index 93b49ca3..684f1e19 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -3,31 +3,31 @@ const TAG = `[ HACKER.MIDDLEWARE.js ]`; const mongoose = require("mongoose"); const Services = { - Hacker: require("../services/hacker.service"), - Storage: require("../services/storage.service"), - Email: require("../services/email.service"), - Account: require("../services/account.service"), - Env: require("../services/env.service"), + Hacker: require("../services/hacker.service"), + Storage: require("../services/storage.service"), + Email: require("../services/email.service"), + Account: require("../services/account.service"), + Env: require("../services/env.service") }; const Middleware = { - Util: require("./util.middleware") + Util: require("./util.middleware") }; const Constants = { - General: require("../constants/general.constant"), - Error: require("../constants/error.constant"), + General: require("../constants/general.constant"), + Error: require("../constants/error.constant") }; /** * @function parsePatch - * @param {body: {id: ObjectId}} req - * @param {*} res - * @param {(err?) => void} next + * @param {body: {id: ObjectId}} req + * @param {*} res + * @param {(err?) => void} next * @return {void} * @description Delete the req.body.id that was added by the validation of route parameter. */ function parsePatch(req, res, next) { - delete req.body.id; - return next(); + delete req.body.id; + return next(); } /** @@ -36,45 +36,45 @@ function parsePatch(req, res, next) { * @param {*} res * @param {(err?)=>void} next * @return {void} - * @description - * Moves accountId, school, degree, gender, needsBus, application from req.body to req.body.hackerDetails. + * @description + * Moves accountId, school, degree, gender, needsBus, application from req.body to req.body.hackerDetails. * Adds _id to hackerDetails. */ function parseHacker(req, res, next) { - const hackerDetails = { - _id: mongoose.Types.ObjectId(), - accountId: req.body.accountId, - school: req.body.school, - degree: req.body.degree, - gender: req.body.gender, - needsBus: req.body.needsBus, - application: req.body.application, - - ethnicity: req.body.ethnicity, - major: req.body.major, - graduationYear: req.body.graduationYear, - codeOfConduct: req.body.codeOfConduct, - - teamId: req.body.teamId, - }; - req.body.token = req.body.authorization; - - delete req.body.accountId; - delete req.body.school; - delete req.body.degree; - delete req.body.gender; - delete req.body.needsBus; - delete req.body.application; - delete req.body.authorization; - delete req.body.ethnicity; - delete req.body.major; - delete req.body.graduationYear; - delete req.body.codeOfConduct; - delete req.body.teamId; - - req.body.hackerDetails = hackerDetails; - - return next(); + const hackerDetails = { + _id: mongoose.Types.ObjectId(), + accountId: req.body.accountId, + school: req.body.school, + degree: req.body.degree, + gender: req.body.gender, + needsBus: req.body.needsBus, + application: req.body.application, + + ethnicity: req.body.ethnicity, + major: req.body.major, + graduationYear: req.body.graduationYear, + codeOfConduct: req.body.codeOfConduct, + + teamId: req.body.teamId + }; + req.body.token = req.body.authorization; + + delete req.body.accountId; + delete req.body.school; + delete req.body.degree; + delete req.body.gender; + delete req.body.needsBus; + delete req.body.application; + delete req.body.authorization; + delete req.body.ethnicity; + delete req.body.major; + delete req.body.graduationYear; + delete req.body.codeOfConduct; + delete req.body.teamId; + + req.body.hackerDetails = hackerDetails; + + return next(); } /** @@ -83,13 +83,13 @@ function parseHacker(req, res, next) { * @param {*} res * @param {(err?)=>void} next * @return {void} - * @description + * @description * Adds the checked-in status to req.body */ function parseCheckIn(req, res, next) { - req.body.status = Constants.General.HACKER_STATUS_CHECKED_IN; + req.body.status = Constants.General.HACKER_STATUS_CHECKED_IN; - return next(); + return next(); } /** @@ -98,21 +98,21 @@ function parseCheckIn(req, res, next) { * @param {*} res * @param {(err?)=>void} next * @return {void} - * @description + * @description * Changes req.body.status to confirmed or cancelled depending on whether req.body.confirm is true or false respectively. * Deletes req.body.confirm afterwards */ function parseConfirmation(req, res, next) { - const confirm = req.body.confirm; + const confirm = req.body.confirm; - if (confirm) { - req.body.status = Constants.General.HACKER_STATUS_CONFIRMED; - } else { - req.body.status = Constants.General.HACKER_STATUS_CANCELLED; - } + if (confirm) { + req.body.status = Constants.General.HACKER_STATUS_CONFIRMED; + } else { + req.body.status = Constants.General.HACKER_STATUS_CANCELLED; + } - delete req.body.confirm; - return next(); + delete req.body.confirm; + return next(); } /** @@ -124,38 +124,38 @@ function parseConfirmation(req, res, next) { * @description Adds status to hackerDetails. */ function addDefaultStatus(req, res, next) { - req.body.hackerDetails.status = "Applied"; - return next(); + req.body.hackerDetails.status = "Applied"; + return next(); } /** * Verifies that account is confirmed and of proper type from the account ID passed in req.body.accountId - * @param {{body: {accountId: ObjectId}}} req - * @param {*} res - * @param {(err?) => void} next + * @param {{body: {accountId: ObjectId}}} req + * @param {*} res + * @param {(err?) => void} next */ async function validateConfirmedStatus(req, res, next) { - const account = await Services.Account.findById(req.body.accountId); - if (!account) { - return next({ - status: 404, - message: Constants.Error.ACCOUNT_404_MESSAGE, - error: {} - }); - } else if (!account.confirmed) { - return next({ - status: 403, - message: Constants.Error.ACCOUNT_403_MESSAGE, - error: {} - }); - } else if (account.accountType !== Constants.General.HACKER) { - return next({ - status: 409, - message: Constants.Error.ACCOUNT_TYPE_409_MESSAGE - }); - } else { - return next(); - } + const account = await Services.Account.findById(req.body.accountId); + if (!account) { + return next({ + status: 404, + message: Constants.Error.ACCOUNT_404_MESSAGE, + error: {} + }); + } else if (!account.confirmed) { + return next({ + status: 403, + message: Constants.Error.ACCOUNT_403_MESSAGE, + error: {} + }); + } else if (account.accountType !== Constants.General.HACKER) { + return next({ + status: 409, + message: Constants.Error.ACCOUNT_TYPE_409_MESSAGE + }); + } else { + return next(); + } } /** @@ -166,228 +166,268 @@ async function validateConfirmedStatus(req, res, next) { * @description Retrieves a hacker's information via req.body.id, moving result to req.body.hacker if succesful. */ async function findById(req, res, next) { - const hacker = await Services.Hacker.findById(req.body.id); + const hacker = await Services.Hacker.findById(req.body.id); - if (!hacker) { - return next({ - status: 404, - message: Constants.Error.HACKER_404_MESSAGE - }); - } + if (!hacker) { + return next({ + status: 404, + message: Constants.Error.HACKER_404_MESSAGE + }); + } - req.body.hacker = hacker; - next(); + req.body.hacker = hacker; + next(); } async function findByEmail(req, res, next) { - const account = await Services.Account.findByEmail(req.body.email); - if (!account) { - return next({ - status: 404, - message: Constants.Error.ACCOUNT_404_MESSAGE, - error: {} - }); - } - const hacker = await Services.Hacker.findByAccountId(account._id); - if (!hacker) { - return next({ - status: 404, - message: Constants.Error.HACKER_404_MESSAGE, - error: {} - }); - } + const account = await Services.Account.findByEmail(req.body.email); + if (!account) { + return next({ + status: 404, + message: Constants.Error.ACCOUNT_404_MESSAGE, + error: {} + }); + } + const hacker = await Services.Hacker.findByAccountId(account._id); + if (!hacker) { + return next({ + status: 404, + message: Constants.Error.HACKER_404_MESSAGE, + error: {} + }); + } - req.body.hacker = hacker; - next(); + req.body.hacker = hacker; + next(); } /** * Verifies that the current signed in user is linked to the hacker passed in via req.body.id - * @param {{body: {id: ObjectId}}} req - * @param {*} res + * @param {{body: {id: ObjectId}}} req + * @param {*} res * @param {(err?)=>void} next */ // must check that the account id is in the hacker schema. function ensureAccountLinkedToHacker(req, res, next) { - Services.Hacker.findById(req.body.id).then( - (hacker) => { - req.hacker = hacker; - if (hacker && req.user && String.toString(hacker.accountId) === String.toString(req.user.id)) { - return next(); - } else { - return next({ - status: 403, - message: Constants.Error.AUTH_403_MESSAGE, - error: {} - }); - } - } - ).catch(next); + Services.Hacker.findById(req.body.id) + .then(hacker => { + req.hacker = hacker; + if ( + hacker && + req.user && + String.toString(hacker.accountId) === String.toString(req.user.id) + ) { + return next(); + } else { + return next({ + status: 403, + message: Constants.Error.AUTH_403_MESSAGE, + error: {} + }); + } + }) + .catch(next); } /** - * Uploads resume via the storage service. Assumes there is a resume in req, and a hacker id in req.body. - * @param {{body: {id: ObjectId}, resume: [Buffer]}} req - * @param {*} res + * Uploads resume via the storage service. Assumes there is a resume in req, and a hacker id in req.body. + * @param {{body: {id: ObjectId}, resume: [Buffer]}} req + * @param {*} res * @param {(err?)=>void} next */ async function uploadResume(req, res, next) { - const gcfilename = `resumes/${Date.now()}-${req.hacker.id}`; - await Services.Storage.upload(req.file, gcfilename); - req.body.gcfilename = gcfilename; - await Services.Hacker.updateOne(req.hacker.id, { - $set: { - "application.portfolioURL.resume": gcfilename - } - }); - return next(); + const gcfilename = `resumes/${Date.now()}-${req.hacker.id}`; + await Services.Storage.upload(req.file, gcfilename); + req.body.gcfilename = gcfilename; + await Services.Hacker.updateOne(req.hacker.id, { + $set: { + "application.portfolioURL.resume": gcfilename + } + }); + return next(); } /** * Attaches the resume of a hacker to req.body.resume. Assumes req.body.id exists. - * @param {{body: {id: ObjectId}}} req - * @param {*} res + * @param {{body: {id: ObjectId}}} req + * @param {*} res * @param {(err?)=>void} next */ async function downloadResume(req, res, next) { - const hacker = await Services.Hacker.findById(req.body.id); - if (hacker && hacker.application && hacker.application.portfolioURL && hacker.application.portfolioURL.resume) { - req.body.resume = await Services.Storage.download(hacker.application.portfolioURL.resume); - } else { - return next({ - status: 404, - message: Constants.Error.RESUME_404_MESSAGE, - error: {} - }); - } - return next(); + const hacker = await Services.Hacker.findById(req.body.id); + if ( + hacker && + hacker.application && + hacker.application.portfolioURL && + hacker.application.portfolioURL.resume + ) { + req.body.resume = await Services.Storage.download( + hacker.application.portfolioURL.resume + ); + } else { + return next({ + status: 404, + message: Constants.Error.RESUME_404_MESSAGE, + error: {} + }); + } + return next(); } /** * Sends a preset email to a user if a status change occured. - * @param {{body: {status?: string}, params: {id: string}}} req - * @param {*} res - * @param {(err?:*)=>void} next + * @param {{body: {status?: string}, params: {id: string}}} req + * @param {*} res + * @param {(err?:*)=>void} next */ async function sendStatusUpdateEmail(req, res, next) { - //skip if the status doesn't exist - if (!req.body.status) { - return next(); - } else { - // send it to the hacker that is being updated. - const hacker = await Services.Hacker.findById(req.params.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.status, next); + //skip if the status doesn't exist + if (!req.body.status) { + return next(); + } else { + // send it to the hacker that is being updated. + const hacker = await Services.Hacker.findById(req.params.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.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 - * @param {*} res - * @param {(err?:*)=>void} next + * @param {{body: {hacker: {accountId: string}}}} req + * @param {*} res + * @param {(err?:*)=>void} next */ async function sendAppliedStatusEmail(req, res, next) { - const hacker = req.body.hacker; - const account = await Services.Account.findById(hacker.accountId); - if (!account) { - return next({ - status: 500, - message: Constants.Error.GENERIC_500_MESSAGE, - error: {} - }); - } - Services.Email.sendStatusUpdate(account.firstName, account.email, Constants.General.HACKER_STATUS_APPLIED, next); + const hacker = req.body.hacker; + const account = await Services.Account.findById(hacker.accountId); + if (!account) { + return next({ + status: 500, + message: Constants.Error.GENERIC_500_MESSAGE, + error: {} + }); + } + Services.Email.sendStatusUpdate( + account.firstName, + account.email, + Constants.General.HACKER_STATUS_APPLIED, + 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 - * @param {*} res - * @param {(err?:*)=>void} next + * @param {{body: {hacker: {accountId: string}}}} req + * @param {*} res + * @param {(err?:*)=>void} next */ async function sendWeekOfEmail(req, res, next) { - const hacker = req.body.hacker; - const address = Services.Env.isProduction() ? process.env.FRONTEND_ADDRESS_DEPLOY : process.env.FRONTEND_ADDRESS_DEV; - const httpOrHttps = (address.includes("localhost")) ? "http" : "https"; - const singleHackerViewLink = Services.Hacker.generateHackerViewLink(httpOrHttps, address, hacker._id.toString()); - const ticketSVG = await Services.Hacker.generateQRCode(singleHackerViewLink); - const account = await Services.Account.findById(hacker.accountId); - if (!account || !ticketSVG) { - return next({ - status: 500, - message: Constants.Error.GENERIC_500_MESSAGE, - error: {} - }); - } - Services.Email.sendWeekOfEmail(account.firstName, account.email, ticketSVG, next); + const hacker = req.body.hacker; + const address = Services.Env.isProduction() + ? process.env.FRONTEND_ADDRESS_DEPLOY + : process.env.FRONTEND_ADDRESS_DEV; + const httpOrHttps = address.includes("localhost") ? "http" : "https"; + const singleHackerViewLink = Services.Hacker.generateHackerViewLink( + httpOrHttps, + address, + hacker._id.toString() + ); + const ticketSVG = await Services.Hacker.generateQRCode(singleHackerViewLink); + const account = await Services.Account.findById(hacker.accountId); + if (!account || !ticketSVG) { + return next({ + status: 500, + message: Constants.Error.GENERIC_500_MESSAGE, + error: {} + }); + } + Services.Email.sendWeekOfEmail( + account.firstName, + account.email, + ticketSVG, + 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 - * @param {*} res - * @param {(err?:*)=>void} next + * @param {{body: {hacker: {accountId: string}}}} req + * @param {*} res + * @param {(err?:*)=>void} next */ async function sendDayOfEmail(req, res, next) { - const hacker = req.body.hacker; - const account = await Services.Account.findById(hacker.accountId); - if (!account) { - return next({ - status: 500, - message: Constants.Error.GENERIC_500_MESSAGE, - error: {} - }); - } - Services.Email.sendDayOfEmail(account.firstName, account.email, next); + const hacker = req.body.hacker; + const account = await Services.Account.findById(hacker.accountId); + if (!account) { + return next({ + status: 500, + message: Constants.Error.GENERIC_500_MESSAGE, + error: {} + }); + } + Services.Email.sendDayOfEmail(account.firstName, account.email, next); } /** * If the current hacker's status is Constants.HACKER_STATUS_NONE, and the hacker's application is completed, - * then it will change the status of the hacker to Constants.General.HACKER_STATUS_APPLIED, and then email the hacker to + * then it will change the status of the hacker to Constants.General.HACKER_STATUS_APPLIED, and then email the hacker to * confirm that they applied. - * @param {{body: {status?: string}, params: {id: string}}} req - * @param {*} res - * @param {(err?:*)=>void} next + * @param {{body: {status?: string}, params: {id: string}}} req + * @param {*} res + * @param {(err?:*)=>void} next */ async function updateStatusIfApplicationCompleted(req, res, next) { - const hacker = await Services.Hacker.findById(req.params.id); - if (hacker) { - if (hacker.status === Constants.General.HACKER_STATUS_NONE && hacker.isApplicationComplete()) { - await Services.Hacker.updateOne(req.params.id, { - status: Constants.General.HACKER_STATUS_APPLIED - }); - const account = await Services.Account.findById(hacker.accountId); - if (!account) { - return next({ - status: 500, - message: Constants.Error.GENERIC_500_MESSAGE, - error: {} - }); - } - Services.Email.sendStatusUpdate(account.firstName, account.email, Constants.General.HACKER_STATUS_APPLIED, next); - } else { - return next(); - } - } else { + const hacker = await Services.Hacker.findById(req.params.id); + if (hacker) { + if ( + hacker.status === Constants.General.HACKER_STATUS_NONE && + hacker.isApplicationComplete() + ) { + await Services.Hacker.updateOne(req.params.id, { + status: Constants.General.HACKER_STATUS_APPLIED + }); + const account = await Services.Account.findById(hacker.accountId); + if (!account) { return next({ - status: 404, - message: Constants.Error.HACKER_404_MESSAGE, - data: { - id: req.params.id - } + status: 500, + message: Constants.Error.GENERIC_500_MESSAGE, + error: {} }); + } + Services.Email.sendStatusUpdate( + account.firstName, + account.email, + Constants.General.HACKER_STATUS_APPLIED, + next + ); + } else { + return next(); } + } else { + return next({ + status: 404, + message: Constants.Error.HACKER_404_MESSAGE, + data: { + id: req.params.id + } + }); + } } /** @@ -396,190 +436,196 @@ async function updateStatusIfApplicationCompleted(req, res, next) { * @returns {(req, res, next) => {}} the middleware that will check hacker's status */ function checkStatus(statuses) { - return Middleware.Util.asyncMiddleware(async (req, res, next) => { - - let hacker = await Services.Hacker.findById(req.params.id); - - if (!!hacker) { - const status = hacker.status; - // makes sure the hacker's status is in the accepted statuses list - if (statuses.indexOf(status) === -1) { - return next({ - status: 409, - message: Constants.Error.HACKER_STATUS_409_MESSAGE, - data: { - id: req.params.id, - validStatuses: statuses - } - }); - } - - return next(); - } else { - return next({ - status: 404, - message: Constants.Error.HACKER_404_MESSAGE, - data: { - id: req.params.id - } - }); + return Middleware.Util.asyncMiddleware(async (req, res, next) => { + let hacker = await Services.Hacker.findById(req.params.id); + + if (!!hacker) { + const status = hacker.status; + // makes sure the hacker's status is in the accepted statuses list + if (statuses.indexOf(status) === -1) { + return next({ + status: 409, + message: Constants.Error.HACKER_STATUS_409_MESSAGE, + data: { + id: req.params.id, + validStatuses: statuses + } + }); + } + + return next(); + } else { + return next({ + status: 404, + message: Constants.Error.HACKER_404_MESSAGE, + data: { + id: req.params.id } - }); + }); + } + }); } /** - * Updates a hacker that is specified by req.params.id, and then sets req.email + * Updates a hacker that is specified by req.params.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 + * @param {{params:{id: string}, body: *}} req + * @param {*} res + * @param {*} next */ async function updateHacker(req, res, next) { - const hacker = await Services.Hacker.updateOne(req.params.id, req.body); - 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 - } - }); + const hacker = await Services.Hacker.updateOne(req.params.id, req.body); + 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 - } - }); + }); } + req.email = acct.email; + return next(); + } else { + return next({ + status: 404, + message: Constants.Error.HACKER_404_MESSAGE, + data: { + id: req.params.id + } + }); + } } /** * @function createhacker - * @param {{body: {hackerDetails: object}}} req - * @param {*} res - * @param {(err?)=>void} next + * @param {{body: {hackerDetails: object}}} req + * @param {*} res + * @param {(err?)=>void} next * @return {void} * @description * Creates hacker document after making sure there is no other hacker with the same linked accountId */ async function createHacker(req, res, next) { - const hackerDetails = req.body.hackerDetails; + const hackerDetails = req.body.hackerDetails; - const exists = await Services.Hacker.findByAccountId(hackerDetails.accountId); + const exists = await Services.Hacker.findByAccountId(hackerDetails.accountId); - if (exists) { - return next({ - status: 422, - message: Constants.Error.ACCOUNT_DUPLICATE_422_MESSAGE, - data: { - id: hackerDetails.accountId - } - }); - } + if (exists) { + return next({ + status: 422, + message: Constants.Error.ACCOUNT_DUPLICATE_422_MESSAGE, + data: { + id: hackerDetails.accountId + } + }); + } - const hacker = await Services.Hacker.createHacker(hackerDetails); + const hacker = await Services.Hacker.createHacker(hackerDetails); - if (!!hacker) { - req.body.hacker = hacker; - return next(); - } else { - return next({ - status: 500, - message: Constants.Error.HACKER_CREATE_500_MESSAGE, - data: {} - }) - } + if (!!hacker) { + req.body.hacker = hacker; + return next(); + } else { + return next({ + status: 500, + message: Constants.Error.HACKER_CREATE_500_MESSAGE, + data: {} + }); + } } /** * Checks that there are no other hackers with the same account id as the one passed into req.body.accountId - * @param {{body:{accountId: ObjectId}}} req - * @param {*} res + * @param {{body:{accountId: ObjectId}}} req + * @param {*} res * @param {*} next */ async function checkDuplicateAccountLinks(req, res, next) { - const hacker = await Services.Hacker.findByAccountId(req.body.accountId); - if (!hacker) { - return next(); - } else { - return next({ - status: 409, - message: Constants.Error.HACKER_ID_409_MESSAGE, - data: { - id: req.body.accountId - } - }); - } + const hacker = await Services.Hacker.findByAccountId(req.body.accountId); + if (!hacker) { + return next(); + } else { + return next({ + status: 409, + message: Constants.Error.HACKER_ID_409_MESSAGE, + data: { + id: req.body.accountId + } + }); + } } /** * Finds the hacker information of the logged in user - * @param {{user: {id: string}}} req - * @param {*} res - * @param {(err?)=>void} next + * @param {{user: {id: string}}} req + * @param {*} res + * @param {(err?)=>void} next */ async function findSelf(req, res, next) { - if (req.user.accountType != Constants.General.HACKER) { - return next({ - status: 409, - message: Constants.Error.ACCOUNT_TYPE_409_MESSAGE, - error: { - id: req.user.id, - } - }); - } - - const hacker = await Services.Hacker.findByAccountId(req.user.id); + if (req.user.accountType != Constants.General.HACKER) { + return next({ + status: 409, + message: Constants.Error.ACCOUNT_TYPE_409_MESSAGE, + error: { + id: req.user.id + } + }); + } + const hacker = await Services.Hacker.findByAccountId(req.user.id); - if (!!hacker) { - req.body.hacker = hacker; - return next(); - } else { - return next({ - status: 409, - message: Constants.Error.HACKER_404_MESSAGE, - error: { - id: req.user.id, - } - }); - } + if (!!hacker) { + req.body.hacker = hacker; + return next(); + } else { + return next({ + status: 409, + message: Constants.Error.HACKER_404_MESSAGE, + error: { + id: req.user.id + } + }); + } } async function getStats(req, res, next) { - const stats = await Services.Hacker.getStats(req.body.results); - req.body.stats = stats; - next(); + const stats = await Services.Hacker.getStats(req.body.results); + req.body.stats = stats; + next(); } module.exports = { - parsePatch: parsePatch, - parseHacker: parseHacker, - addDefaultStatus: addDefaultStatus, - ensureAccountLinkedToHacker: ensureAccountLinkedToHacker, - uploadResume: Middleware.Util.asyncMiddleware(uploadResume), - downloadResume: Middleware.Util.asyncMiddleware(downloadResume), - sendWeekOfEmail: Middleware.Util.asyncMiddleware(sendWeekOfEmail), - sendDayOfEmail: Middleware.Util.asyncMiddleware(sendDayOfEmail), - sendStatusUpdateEmail: Middleware.Util.asyncMiddleware(sendStatusUpdateEmail), - sendAppliedStatusEmail: Middleware.Util.asyncMiddleware(sendAppliedStatusEmail), - updateHacker: Middleware.Util.asyncMiddleware(updateHacker), - validateConfirmedStatus: Middleware.Util.asyncMiddleware(validateConfirmedStatus), - checkDuplicateAccountLinks: Middleware.Util.asyncMiddleware(checkDuplicateAccountLinks), - updateStatusIfApplicationCompleted: Middleware.Util.asyncMiddleware(updateStatusIfApplicationCompleted), - checkStatus: checkStatus, - parseCheckIn: parseCheckIn, - parseConfirmation: parseConfirmation, - createHacker: Middleware.Util.asyncMiddleware(createHacker), - findSelf: Middleware.Util.asyncMiddleware(findSelf), - getStats: Middleware.Util.asyncMiddleware(getStats), - findById: Middleware.Util.asyncMiddleware(findById), - findByEmail: Middleware.Util.asyncMiddleware(findByEmail), -}; \ No newline at end of file + parsePatch: parsePatch, + parseHacker: parseHacker, + addDefaultStatus: addDefaultStatus, + ensureAccountLinkedToHacker: ensureAccountLinkedToHacker, + uploadResume: Middleware.Util.asyncMiddleware(uploadResume), + downloadResume: Middleware.Util.asyncMiddleware(downloadResume), + sendWeekOfEmail: Middleware.Util.asyncMiddleware(sendWeekOfEmail), + sendDayOfEmail: Middleware.Util.asyncMiddleware(sendDayOfEmail), + sendStatusUpdateEmail: Middleware.Util.asyncMiddleware(sendStatusUpdateEmail), + sendAppliedStatusEmail: Middleware.Util.asyncMiddleware( + sendAppliedStatusEmail + ), + updateHacker: Middleware.Util.asyncMiddleware(updateHacker), + validateConfirmedStatus: Middleware.Util.asyncMiddleware( + validateConfirmedStatus + ), + checkDuplicateAccountLinks: Middleware.Util.asyncMiddleware( + checkDuplicateAccountLinks + ), + updateStatusIfApplicationCompleted: Middleware.Util.asyncMiddleware( + updateStatusIfApplicationCompleted + ), + checkStatus: checkStatus, + parseCheckIn: parseCheckIn, + parseConfirmation: parseConfirmation, + createHacker: Middleware.Util.asyncMiddleware(createHacker), + findSelf: Middleware.Util.asyncMiddleware(findSelf), + getStats: Middleware.Util.asyncMiddleware(getStats), + findById: Middleware.Util.asyncMiddleware(findById), + findByEmail: Middleware.Util.asyncMiddleware(findByEmail) +}; diff --git a/middlewares/parse-body.middleware.js b/middlewares/parse-body.middleware.js index c69539dc..8f3e4e9d 100644 --- a/middlewares/parse-body.middleware.js +++ b/middlewares/parse-body.middleware.js @@ -1,33 +1,29 @@ "use strict"; -const { - validationResult -} = require("express-validator/check"); -const { - matchedData -} = require("express-validator/filter"); +const { validationResult } = require("express-validator/check"); +const { matchedData } = require("express-validator/filter"); const Constants = { - Error: require("../constants/error.constant"), + Error: require("../constants/error.constant") }; module.exports = { - middleware: middleware + middleware: middleware }; /** * Moves matched data to req.body, and fails if any validation fails. - * @param {*} req - * @param {*} res + * @param {*} req + * @param {*} res * @param {(err?)=>void} next */ function middleware(req, res, next) { - const errors = validationResult(req); - if (!errors.isEmpty()) { - return next({ - status: 422, - message: Constants.Error.VALIDATION_422_MESSAGE, - data: errors.mapped() - }); - } - req.body = matchedData(req); - return next(); -} \ No newline at end of file + const errors = validationResult(req); + if (!errors.isEmpty()) { + return next({ + status: 422, + message: Constants.Error.VALIDATION_422_MESSAGE, + data: errors.mapped() + }); + } + req.body = matchedData(req); + return next(); +} diff --git a/middlewares/validators/hacker.validator.js b/middlewares/validators/hacker.validator.js index 6728a9f6..90fc942a 100644 --- a/middlewares/validators/hacker.validator.js +++ b/middlewares/validators/hacker.validator.js @@ -3,49 +3,43 @@ const VALIDATOR = require("./validator.helper"); const Constants = require("../../constants/general.constant"); module.exports = { - newHackerValidator: [ - // status will be added automatically - VALIDATOR.mongoIdValidator("body", "accountId", false), - VALIDATOR.stringValidator("body", "school", false), - VALIDATOR.stringValidator("body", "degree", false), - VALIDATOR.stringValidator("body", "gender", false), - VALIDATOR.booleanValidator("body", "needsBus", false), - VALIDATOR.applicationValidator("body", "application", false), - VALIDATOR.alphaArrayValidator("body", "ethnicity", false), - VALIDATOR.alphaArrayValidator("body", "major", false), - VALIDATOR.integerValidator("body", "graduationYear", false, 2019, 2030), - VALIDATOR.booleanValidator("body", "codeOfConduct", false, true), - VALIDATOR.mongoIdValidator("body", "teamId", true) - ], + newHackerValidator: [ + // status will be added automatically + VALIDATOR.mongoIdValidator("body", "accountId", false), + // VALIDATOR.stringValidator("body", "school", false), + // VALIDATOR.stringValidator("body", "degree", false), + // VALIDATOR.stringValidator("body", "gender", false), + // VALIDATOR.booleanValidator("body", "needsBus", false), + VALIDATOR.applicationValidator("body", "application", false), + // VALIDATOR.alphaArrayValidator("body", "ethnicity", false), + // VALIDATOR.alphaArrayValidator("body", "major", false), + // VALIDATOR.integerValidator("body", "graduationYear", false, 2019, 2030), + // VALIDATOR.booleanValidator("body", "codeOfConduct", false, true), + VALIDATOR.mongoIdValidator("body", "teamId", true) + ], - updateConfirmationValidator: [ - VALIDATOR.booleanValidator("body", "confirm", false), - ], + updateConfirmationValidator: [ + VALIDATOR.booleanValidator("body", "confirm", false) + ], - updateHackerValidator: [ - VALIDATOR.stringValidator("body", "school", true), - VALIDATOR.stringValidator("body", "degree", true), - VALIDATOR.stringValidator("body", "gender", true), - VALIDATOR.booleanValidator("body", "needsBus", true), - VALIDATOR.applicationValidator("body", "application", true), - VALIDATOR.alphaArrayValidator("body", "ethnicity", true), - VALIDATOR.alphaArrayValidator("body", "major", true), - VALIDATOR.integerValidator("body", "graduationYear", true, 2019, 2030), - ], - updateStatusValidator: [ - VALIDATOR.enumValidator("body", "status", Constants.HACKER_STATUSES, false), - ], - checkInStatusValidator: [ - VALIDATOR.enumValidator("body", "status", Constants.HACKER_STATUS_CHECKED_IN, false) - ], - uploadResumeValidator: [ - VALIDATOR.mongoIdValidator("param", "id", false) - ], - downloadResumeValidator: [ - VALIDATOR.mongoIdValidator("param", "id", false) - ], - statsValidator: [ - VALIDATOR.searchModelValidator("query", "model", false), - VALIDATOR.searchValidator("query", "q") - ] -}; \ No newline at end of file + updateHackerValidator: [ + VALIDATOR.applicationValidator("body", "application", false) + ], + updateStatusValidator: [ + VALIDATOR.enumValidator("body", "status", Constants.HACKER_STATUSES, false) + ], + checkInStatusValidator: [ + VALIDATOR.enumValidator( + "body", + "status", + Constants.HACKER_STATUS_CHECKED_IN, + false + ) + ], + uploadResumeValidator: [VALIDATOR.mongoIdValidator("param", "id", false)], + downloadResumeValidator: [VALIDATOR.mongoIdValidator("param", "id", false)], + statsValidator: [ + VALIDATOR.searchModelValidator("query", "model", false), + VALIDATOR.searchValidator("query", "q") + ] +}; diff --git a/middlewares/validators/validator.helper.js b/middlewares/validators/validator.helper.js index 3fff34b4..7548369d 100644 --- a/middlewares/validators/validator.helper.js +++ b/middlewares/validators/validator.helper.js @@ -1,9 +1,10 @@ "use strict"; const { - body, - query, - header, - param + body, + query, + header, + param, + check } = require("express-validator/check"); const logger = require("../../services/logger.service"); const mongoose = require("mongoose"); @@ -11,53 +12,84 @@ const TAG = `[ VALIDATOR.HELPER.js ]`; const jwt = require("jsonwebtoken"); const Constants = require("../../constants/general.constant"); const Models = { - Hacker: require("../../models/hacker.model") + Hacker: require("../../models/hacker.model") }; /** * Validates that field is a valid integer - * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found + * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found * @param {string} fieldname Name of the field that needs to be validated. * @param {boolean} optional Whether the field is optional or not. * @param {number} lowerBound Lower bound for a valid integer. * @param {number} upperBound Lpper bound for a valid integer. */ -function integerValidator(fieldLocation, fieldname, optional = true, lowerBound = -Infinity, upperBound = Infinity) { - const value = setProperValidationChainBuilder(fieldLocation, fieldname, "invalid integer"); - - if (optional) { - return value.optional({ - checkFalsy: true - }) - .isInt().withMessage(`${fieldname} must be an integer.`) - .custom((value) => { - return value >= lowerBound && value <= upperBound; - }).withMessage(`${fieldname} must be between ${lowerBound} and ${upperBound}`); - } else { - return value.exists().withMessage("tier must exist") - .isInt().withMessage(`${fieldname} must be an integer.`) - .custom((value) => { - return value >= lowerBound && value <= upperBound; - }).withMessage(`${fieldname} must be between ${lowerBound} and ${upperBound}`); - } +function integerValidator( + fieldLocation, + fieldname, + optional = true, + lowerBound = -Infinity, + upperBound = Infinity +) { + const value = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "invalid integer" + ); + + if (optional) { + return value + .optional({ + checkFalsy: true + }) + .isInt() + .withMessage(`${fieldname} must be an integer.`) + .custom(value => { + return value >= lowerBound && value <= upperBound; + }) + .withMessage( + `${fieldname} must be between ${lowerBound} and ${upperBound}` + ); + } else { + return value + .exists() + .withMessage("tier must exist") + .isInt() + .withMessage(`${fieldname} must be an integer.`) + .custom(value => { + return value >= lowerBound && value <= upperBound; + }) + .withMessage( + `${fieldname} must be between ${lowerBound} and ${upperBound}` + ); + } } /** * Validates that field is a valid mongoID - * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found + * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found * @param {string} fieldname Name of the field that needs to be validated. * @param {boolean} optional Whether the field is optional or not. */ function mongoIdValidator(fieldLocation, fieldname, optional = true) { - const mongoId = setProperValidationChainBuilder(fieldLocation, fieldname, "invalid mongoID"); - - if (optional) { - return mongoId.optional({ - checkFalsy: true - }).isMongoId().withMessage("must be a valid mongoID"); - } else { - return mongoId.exists().isMongoId().withMessage("must be a valid mongoID"); - } + const mongoId = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "invalid mongoID" + ); + + if (optional) { + return mongoId + .optional({ + checkFalsy: true + }) + .isMongoId() + .withMessage("must be a valid mongoID"); + } else { + return mongoId + .exists() + .isMongoId() + .withMessage("must be a valid mongoID"); + } } /** @@ -67,135 +99,224 @@ function mongoIdValidator(fieldLocation, fieldname, optional = true) { * @param {boolean} optional Whether the field is optional or not. */ function mongoIdArrayValidator(fieldLocation, fieldname, optional = true) { - const arr = setProperValidationChainBuilder(fieldLocation, fieldname, "invalid mongoID array"); - - if (optional) { - return arr.optional({ - checkFalsy: true - }) - .custom(isMongoIdArray).withMessage("Value must be an array of mongoIDs"); - } else { - return arr.exists() - .custom(isMongoIdArray).withMessage("Value must be an array of mongoIDs"); - } + const arr = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "invalid mongoID array" + ); + + if (optional) { + return arr + .optional({ + checkFalsy: true + }) + .custom(isMongoIdArray) + .withMessage("Value must be an array of mongoIDs"); + } else { + return arr + .exists() + .custom(isMongoIdArray) + .withMessage("Value must be an array of mongoIDs"); + } } /** * Validates that field must be boolean. Optionally checks for desired boolean - * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found + * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found * @param {string} fieldname name of the field that needs to be validated. * @param {boolean} optional whether the field is optional or not. */ -function booleanValidator(fieldLocation, fieldname, optional = true, desire = null) { - const booleanField = setProperValidationChainBuilder(fieldLocation, fieldname, "invalid boolean"); - - if (optional) { - // do not use check falsy option as a 'false' boolean will be skipped - return booleanField.optional().isBoolean().withMessage("must be boolean").custom((val) => { - if (desire !== null) { - return desire === val; - } - return true; - }).withMessage(`Must be equal to ${desire}`); - } else { - return booleanField.exists().isBoolean().withMessage("must be boolean").custom((val) => { - if (desire !== null) { - return desire === val; - } - return true; - }).withMessage(`Must be equal to ${desire}`); - } +function booleanValidator( + fieldLocation, + fieldname, + optional = true, + desire = null +) { + const booleanField = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "invalid boolean" + ); + + if (optional) { + // do not use check falsy option as a 'false' boolean will be skipped + return booleanField + .optional() + .isBoolean() + .withMessage("must be boolean") + .custom(val => { + if (desire !== null) { + return desire === val; + } + return true; + }) + .withMessage(`Must be equal to ${desire}`); + } else { + return booleanField + .exists() + .isBoolean() + .withMessage("must be boolean") + .custom(val => { + if (desire !== null) { + return desire === val; + } + return true; + }) + .withMessage(`Must be equal to ${desire}`); + } } /** * Validates that field name is ascii only. - * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found + * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found * @param {string} fieldname name of the field that needs to be validated. * @param {boolean} optional whether the field is optional or not. */ function asciiValidator(fieldLocation, fieldname, optional = true) { - const name = setProperValidationChainBuilder(fieldLocation, fieldname, "invalid name"); - if (optional) { - return name.optional({ - checkFalsy: true - }).isAscii().withMessage("must contain only ascii characters"); - } else { - return name.exists().withMessage("name must exist").isAscii().withMessage("must contain only ascii characters"); - } + const name = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "invalid name" + ); + if (optional) { + return name + .optional({ + checkFalsy: true + }) + .isAscii() + .withMessage("must contain only ascii characters"); + } else { + return name + .exists() + .withMessage("name must exist") + .isAscii() + .withMessage("must contain only ascii characters"); + } } /** * Validates that field name is string only. - * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found + * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found * @param {string} fieldname name of the field that needs to be validated. * @param {boolean} optional whether the field is optional or not. */ function stringValidator(fieldLocation, fieldname, optional = true) { - const name = setProperValidationChainBuilder(fieldLocation, fieldname, "invalid string"); - if (optional) { - return name.optional({ - checkFalsy: true - }).isString().withMessage("must be a string"); - } else { - return name.exists().withMessage("name must exist").isString().withMessage("must be a string"); - } + const name = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "Invalid String" + ); + + if (optional) { + return name + .optional({ + checkFalsy: true + }) + .isString() + .withMessage("must be a string"); + } else { + return name + .exists() + .withMessage("name must exist") + .isString() + .withMessage("must be a string"); + } } /** * Validates the field against a regex - * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found + * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found * @param {string} fieldname Name of the field that needs to be validated. * @param {boolean} optional Whether the field is optional or not. * @param {regex} desire The regex to match against * @description The default regex to match against accepts anything */ -function regexValidator(fieldLocation, fieldname, optional = true, desire = Constants.ANY_REGEX) { - const match = setProperValidationChainBuilder(fieldLocation, fieldname, "invalid name"); - - if (optional) { - return match.optional({ - checkFalsy: true - }) - .matches(desire) - .withMessage("must be valid url"); - } else { - return match.exists().withMessage("url must exist") - .matches(desire) - .withMessage("must be valid url"); - } +function regexValidator( + fieldLocation, + fieldname, + optional = true, + desire = Constants.ANY_REGEX +) { + const match = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "invalid name" + ); + + if (optional) { + return match + .optional({ + checkFalsy: true + }) + .matches(desire) + .withMessage("must be valid url"); + } else { + return match + .exists() + .withMessage("url must exist") + .matches(desire) + .withMessage("must be valid url"); + } } /** * Validates that field must be alphabetical. - * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found + * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found * @param {string} fieldname name of the field that needs to be validated. * @param {boolean} optional whether the field is optional or not. */ function alphaValidator(fieldLocation, fieldname, optional = true) { - const name = setProperValidationChainBuilder(fieldLocation, fieldname, "invalid alpha string"); - if (optional) { - return name.optional({ - checkFalsy: true - }).isAlpha().withMessage("must contain alphabet characters"); - } else { - return name.exists().withMessage("must exist").isAlpha().withMessage("must contain alphabet characters"); - } + const name = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "invalid alpha string" + ); + if (optional) { + return name + .optional({ + checkFalsy: true + }) + .isAlpha() + .withMessage("must contain alphabet characters"); + } else { + return name + .exists() + .withMessage("must exist") + .isAlpha() + .withMessage("must contain alphabet characters"); + } } /** * Validates that field must be an array with alphabetical characters. - * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found + * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found * @param {string} fieldname name of the field that needs to be validated. * @param {boolean} optional whether the field is optional or not. */ function alphaArrayValidator(fieldLocation, fieldname, optional = true) { - const name = setProperValidationChainBuilder(fieldLocation, fieldname, "invalid alpha array"); - if (optional) { - return name.optional({ - checkFalsy: true - }).custom(alphaArrayValidationHelper).withMessage("must contain alphabet characters in each element of the array"); - } else { - return name.exists().withMessage("must exist").custom(alphaArrayValidationHelper).withMessage("must contain alphabet characters in each element of the array"); - } + const name = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "invalid alpha array" + ); + + if (optional) { + return name + .optional({ + checkFalsy: true + }) + .custom(alphaArrayValidationHelper) + .withMessage( + "must contain alphabet characters in each element of the array" + ); + } else { + return name + .exists() + .withMessage("must exist") + .custom(alphaArrayValidationHelper) + .withMessage( + "must contain alphabet characters in each element of the array" + ); + } } /** @@ -203,103 +324,285 @@ function alphaArrayValidator(fieldLocation, fieldname, optional = true) { * @param {*} value value to check against */ function alphaArrayValidationHelper(value) { - if (!Array.isArray(value)) { - return false; - } - for (const el of value) { - if (typeof el !== "string") { - return false; - } + if (!Array.isArray(value)) { + return false; + } + for (const el of value) { + if (typeof el !== "string") { + return false; } - return true; + } + return true; } /** - * Validates that field must be at least 6 characters long. + * Validates that field must be at least 6 characters long. * TODO: impose better restrictions. - * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found + * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found * @param {string} fieldname name of the field that needs to be validated. * @param {boolean} optional whether the field is optional or not. */ function passwordValidator(fieldLocation, fieldname, optional = true) { - const password = setProperValidationChainBuilder(fieldLocation, fieldname, "invalid password"); - if (optional) { - return password.optional({ - checkFalsy: true - }).isLength({ - min: 6 - }).withMessage("must be longer than 6 characters"); - } else { - return password.exists().withMessage("password must exist").isLength({ - min: 6 - }).withMessage("must be longer than 6 characters"); - } + const password = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "invalid password" + ); + if (optional) { + return password + .optional({ + checkFalsy: true + }) + .isLength({ + min: 6 + }) + .withMessage("must be longer than 6 characters"); + } else { + return password + .exists() + .withMessage("password must exist") + .isLength({ + min: 6 + }) + .withMessage("must be longer than 6 characters"); + } } - /** * Validates that field must be a valid application. - * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found + * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found * @param {string} fieldname name of the field that needs to be validated. * @param {boolean} optional whether the field is optional or not. */ function applicationValidator(fieldLocation, fieldname, optional = true) { - const application = setProperValidationChainBuilder(fieldLocation, fieldname, "invalid application"); - - //helper object to iterate through the items in the application and track which items are not valid. - const hasValid = { - github: false, - dropler: false, - personal: false, - linkedIn: false, - other: false, - jobInterest: false, - skills: false, - comments: false, - essay: false, - team: false - }; - if (optional) { - return application.optional({ - checkFalsy: true - }).custom(app => { - const jobInterests = Constants.JOB_INTERESTS; - hasValid.github = (!app.portfolioURL.github || typeof (app.portfolioURL.github) === "string"); - hasValid.dropler = (!app.portfolioURL.dropler || typeof (app.portfolioURL.dropler) === "string"); - hasValid.personal = (!app.portfolioURL.personal || typeof (app.portfolioURL.personal) === "string"); - hasValid.linkedIn = (!app.portfolioURL.linkedIn || typeof (app.portfolioURL.linkedIn) === "string"); - hasValid.other = (!app.portfolioURL.other || typeof (app.portfolioURL.other) === "string"); - hasValid.jobInterest = (!app.jobInterest || jobInterests.includes(app.jobInterest)); - hasValid.skills = (!app.skills || alphaArrayValidationHelper(app.skills)); - hasValid.comments = (!app.comments || typeof (app.comments) === "string"); - hasValid.essay = (!app.essay || typeof (app.essay) === "string"); - hasValid.team = (!app.team || mongoose.Types.ObjectId.isValid(app.team)); - return hasValid.comments && hasValid.github && hasValid.dropler && hasValid.personal && - hasValid.linkedIn && hasValid.other && hasValid.jobInterest && hasValid.skills && hasValid.team; - }).withMessage({ - message: "Not all items of the application are valid", - isValid: hasValid - }); - } else { - return application.custom(app => { - const jobInterests = Constants.JOB_INTERESTS; - hasValid.github = (!app.portfolioURL.github || typeof (app.portfolioURL.github) === "string"); - hasValid.dropler = (!app.portfolioURL.dropler || typeof (app.portfolioURL.dropler) === "string"); - hasValid.personal = (!app.portfolioURL.personal || typeof (app.portfolioURL.personal) === "string"); - hasValid.linkedIn = (!app.portfolioURL.linkedIn || typeof (app.portfolioURL.linkedIn) === "string"); - hasValid.other = (!app.portfolioURL.other || typeof (app.portfolioURL.other) === "string"); - hasValid.jobInterest = (jobInterests.includes(app.jobInterest)); - hasValid.skills = (!app.skills || alphaArrayValidationHelper(app.skills)); - hasValid.comments = (!app.comments || typeof (app.comments) === "string"); - hasValid.essay = (!app.essay || typeof (app.essay) === "string"); - hasValid.team = (!app.team || mongoose.Types.ObjectId.isValid(app.team)); - return hasValid.comments && hasValid.github && hasValid.dropler && hasValid.personal && - hasValid.linkedIn && hasValid.other && hasValid.jobInterest && hasValid.skills && hasValid.team; - }).withMessage({ - message: "Not all items of the application are valid", - isValid: hasValid - }); - } + const application = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "invalid application" + ); + + //helper object to iterate through the items in the application and track which items are not valid. + const hasValid = { + school: false, + degree: false, + fieldOfStudy: false, + gender: false, + ethnicity: false, + bus: false, + graduationYear: false, + codeOfConduct: false, + github: false, + dribbble: false, + personal: false, + linkedIn: false, + other: false, + jobInterest: false, + skills: false, + comments: false, + question1: false, + question2: false, + team: false + }; + if (optional) { + return application + .optional({ + checkFalsy: true + }) + .custom(app => { + hasValid.school = stringValidator( + "body", + "application.general.school", + false + ); + hasValid.degree = stringValidator( + "body", + "application.general.degree", + false + ); + hasValid.fieldOfStudy = alphaArrayValidator( + "body", + "application.general.fieldOfStudy", + false + ); + hasValid.general = stringValidator( + "body", + "application.other.gender", + false + ); + hasValid.bus = booleanValidator( + "body", + "application.accomodation.needsBus", + false + ); + hasValid.ethnicity = alphaArrayValidator( + "body", + "application.other.ethnicity", + false + ); + hasValid.graduationYear = integerValidator( + "body", + "application.general.graduationYear", + false, + 2019, + 2030 + ); + hasValid.codeOfConduct = booleanValidator( + "body", + "application.accomodation.needsBus", + false + ); + const jobInterests = Constants.JOB_INTERESTS; + hasValid.github = + !app.general.URL.github || typeof app.general.URL.github === "string"; + hasValid.dribbble = + !app.general.URL.dribbble || + typeof app.general.URL.dribbble === "string"; + hasValid.personal = + !app.general.URL.personal || + typeof app.general.URL.personal === "string"; + hasValid.linkedIn = + !app.general.URL.linkedIn || + typeof app.general.URL.linkedIn === "string"; + hasValid.other = + !app.general.URL.other || typeof app.general.URL.other === "string"; + hasValid.jobInterest = + !app.general.jobInterest || + jobInterests.includes(app.general.jobInterest); + hasValid.skills = + !app.shortAnswer.skills || + alphaArrayValidationHelper(app.shortAnswer.skills); + hasValid.comments = + !app.shortAnswer.comments || + typeof app.shortAnswer.comments === "string"; + hasValid.question1 = + !app.shortAnswer.question1 || + typeof app.shortAnswer.question1 === "string"; + hasValid.question2 = + !app.shortAnswer.question2 || + typeof app.shortAnswer.question2 === "string"; + hasValid.team = !app.team || mongoose.Types.ObjectId.isValid(app.team); + return ( + hasValid.comments && + hasValid.school && + hasValid.degree && + hasValid.fieldOfStudy && + hasValid.graduationYear && + hasValid.ethnicity && + hasValid.bus && + hasValid.codeOfConduct && + hasValid.github && + hasValid.dribbble && + hasValid.personal && + hasValid.linkedIn && + hasValid.other && + hasValid.jobInterest && + hasValid.skills && + hasValid.team + ); + }) + .withMessage({ + message: "Not all items of the application are valid", + isValid: hasValid + }); + } else { + return application + .custom(app => { + hasValid.school = stringValidator( + "body", + "application.general.school", + false + ); + hasValid.degree = stringValidator( + "body", + "application.general.degree", + false + ); + hasValid.fieldOfStudy = alphaArrayValidator( + "body", + "application.general.fieldOfStudy", + false + ); + hasValid.general = stringValidator( + "body", + "application.other.gender", + false + ); + hasValid.bus = booleanValidator( + "body", + "application.accomodation.needsBus", + false + ); + hasValid.ethnicity = alphaArrayValidator( + "body", + "application.other.ethnicity", + false + ); + hasValid.graduationYear = integerValidator( + "body", + "application.general.graduationYear", + false, + 2019, + 2030 + ); + hasValid.codeOfConduct = booleanValidator( + "body", + "application.accomodation.needsBus", + false + ); + const jobInterests = Constants.JOB_INTERESTS; + hasValid.github = + !app.general.URL.github || typeof app.general.URL.github === "string"; + hasValid.dribbble = + !app.general.URL.dribbble || + typeof app.general.URL.dribbble === "string"; + hasValid.personal = + !app.general.URL.personal || + typeof app.general.URL.personal === "string"; + hasValid.linkedIn = + !app.general.URL.linkedIn || + typeof app.general.URL.linkedIn === "string"; + hasValid.other = + !app.general.URL.other || typeof app.general.URL.other === "string"; + hasValid.jobInterest = + !app.general.jobInterest || + jobInterests.includes(app.general.jobInterest); + hasValid.skills = + !app.shortAnswer.skills || + alphaArrayValidationHelper(app.shortAnswer.skills); + hasValid.comments = + !app.shortAnswer.comments || + typeof app.shortAnswer.comments === "string"; + hasValid.question1 = + !app.shortAnswer.question1 || + typeof app.shortAnswer.question1 === "string"; + hasValid.question2 = + !app.shortAnswer.question2 || + typeof app.shortAnswer.question2 === "string"; + hasValid.team = !app.team || mongoose.Types.ObjectId.isValid(app.team); + return ( + hasValid.comments && + hasValid.school && + hasValid.degree && + hasValid.fieldOfStudy && + hasValid.graduationYear && + hasValid.ethnicity && + hasValid.bus && + hasValid.codeOfConduct && + hasValid.github && + hasValid.dribbble && + hasValid.personal && + hasValid.linkedIn && + hasValid.other && + hasValid.jobInterest && + hasValid.skills && + hasValid.team + ); + }) + .withMessage({ + message: "Not all items of the application are valid", + isValid: hasValid + }); + } } /** @@ -308,49 +611,58 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { * @returns {boolean} whether the array contains only mongoIds */ function isMongoIdArray(arr) { - if (!Array.isArray(arr)) { - return false; - } + if (!Array.isArray(arr)) { + return false; + } - for (var ele of arr) { - if (!mongoose.Types.ObjectId.isValid(ele)) { - return false; - } + for (var ele of arr) { + if (!mongoose.Types.ObjectId.isValid(ele)) { + return false; } + } - return true; + return true; } /** * Validates that field must be a valid json web token. - * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found + * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found * @param {string} fieldname name of the field that needs to be validated. * @param {*} jwtSecret The secret that the json web token was encrypted with. * @param {boolean} optional whether the field is optional or not. */ function jwtValidator(fieldLocation, fieldname, jwtSecret, optional = true) { - const jwtValidationChain = setProperValidationChainBuilder(fieldLocation, fieldname, "Must be vali jwt"); - if (optional) { - return jwtValidationChain.optional({ - checkFalsy: true - }) - .custom(value => { - const token = jwt.verify(value, jwtSecret); - if (typeof token !== "undefined") { - return true; - } - return false; - }).withMessage(`must be valid jwt`); - } else { - return jwtValidationChain.exists().withMessage("Token must be provided") - .custom(value => { - const token = jwt.verify(value, jwtSecret); - if (typeof token !== "undefined") { - return true; - } - return false; - }).withMessage(`must be valid jwt`); - } + const jwtValidationChain = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "Must be vali jwt" + ); + if (optional) { + return jwtValidationChain + .optional({ + checkFalsy: true + }) + .custom(value => { + const token = jwt.verify(value, jwtSecret); + if (typeof token !== "undefined") { + return true; + } + return false; + }) + .withMessage(`must be valid jwt`); + } else { + return jwtValidationChain + .exists() + .withMessage("Token must be provided") + .custom(value => { + const token = jwt.verify(value, jwtSecret); + if (typeof token !== "undefined") { + return true; + } + return false; + }) + .withMessage(`must be valid jwt`); + } } /** @@ -359,11 +671,19 @@ function jwtValidator(fieldLocation, fieldname, jwtSecret, optional = true) { * @param {string} fieldname name of the field that needs to be validated. */ function searchModelValidator(fieldLocation, fieldName) { - const paramChain = setProperValidationChainBuilder(fieldLocation, fieldName, "Must be a valid searchable model"); - return paramChain.exists().withMessage("Model must be provided") - .isLowercase().withMessage("Model must be lower case") - .isAlpha().withMessage("must contain alphabet characters") - .isIn([Constants.HACKER.toLowerCase()]) + const paramChain = setProperValidationChainBuilder( + fieldLocation, + fieldName, + "Must be a valid searchable model" + ); + return paramChain + .exists() + .withMessage("Model must be provided") + .isLowercase() + .withMessage("Model must be lower case") + .isAlpha() + .withMessage("must contain alphabet characters") + .isIn([Constants.HACKER.toLowerCase()]); } /**== @@ -372,153 +692,198 @@ function searchModelValidator(fieldLocation, fieldName) { * @param {string} fieldname name of the field that needs to be validated. */ function searchValidator(fieldLocation, fieldname) { - const search = setProperValidationChainBuilder(fieldLocation, fieldname, "Invalid search query"); - - return search.exists().withMessage("Search query must be provided") - .custom((value, { - req - }) => { - //value is a serialized JSON - value = JSON.parse(value); - let modelString = req.query.model - //Supported models for searching - let model; - if (modelString === Constants.HACKER.toLowerCase()) { - model = Models.Hacker; - } else { - return false; + const search = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "Invalid search query" + ); + + return search + .exists() + .withMessage("Search query must be provided") + .custom((value, { req }) => { + //value is a serialized JSON + value = JSON.parse(value); + let modelString = req.query.model; + //Supported models for searching + let model; + if (modelString === Constants.HACKER.toLowerCase()) { + model = Models.Hacker; + } else { + return false; + } + //Validates each clause in the query + for (var q in value) { + //Checks that each clause has Param, Value, and Operation + var clause = value[q]; + if ( + !( + clause.hasOwnProperty("param") || + clause.hasOwnProperty("value") || + clause.hasOwnProperty("operation") + ) + ) { + return false; + } + var schemaPath = model.searchableField(clause.param); + if (!schemaPath) return false; + //Validates that operation corresponding to each clause is valid + switch (schemaPath) { + case "String": + if (!["equals", "ne", "regex", "in"].includes(clause.operation)) { + return false; } - //Validates each clause in the query - for (var q in value) { - //Checks that each clause has Param, Value, and Operation - var clause = value[q]; - if (!(clause.hasOwnProperty("param") || clause.hasOwnProperty("value") || clause.hasOwnProperty("operation"))) { - return false; - } - var schemaPath = model.searchableField(clause.param); - if (!schemaPath) return false; - //Validates that operation corresponding to each clause is valid - switch (schemaPath) { - case "String": - if (!["equals", "ne", "regex", "in"].includes(clause.operation)) { - return false; - } - break; - case "Number": - if (!["equals", "ne", "gte", "lte", "le", "ge", "in"].includes(clause.operation)) { - return false; - } - break; - case "Boolean": - if (!["equals", "ne"].includes(clause.operation)) { - return false; - } - break; - } + break; + case "Number": + if ( + !["equals", "ne", "gte", "lte", "le", "ge", "in"].includes( + clause.operation + ) + ) { + return false; } - return true; - }); + break; + case "Boolean": + if (!["equals", "ne"].includes(clause.operation)) { + return false; + } + break; + } + } + return true; + }); } function searchSortValidator(fieldLocation, fieldName) { - const searchSort = setProperValidationChainBuilder(fieldLocation, fieldName, "Invalid sort criteria") - return searchSort.optional({ - checkFalsy: true - }) - .custom((value, { - req - }) => { - let modelString = req.query.model - if (modelString.equals("hacker")) { - model = Models.Hacker; - } else { - return false; - } - if (!!model.searchableField(value)) { - let sortOrder = param("sort", "Sorting order not found"); - if (!sortOrder.equals("asc") || !sortOrder.equals("desc")) { - return false; - } - } else { - return false; - } - return true; - }) + const searchSort = setProperValidationChainBuilder( + fieldLocation, + fieldName, + "Invalid sort criteria" + ); + return searchSort + .optional({ + checkFalsy: true + }) + .custom((value, { req }) => { + let modelString = req.query.model; + if (modelString.equals("hacker")) { + model = Models.Hacker; + } else { + return false; + } + if (!!model.searchableField(value)) { + let sortOrder = param("sort", "Sorting order not found"); + if (!sortOrder.equals("asc") || !sortOrder.equals("desc")) { + return false; + } + } else { + return false; + } + return true; + }); } /** * Validates that field must be a valid date according to javascript Date.parse - * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found + * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found * @param {string} fieldname name of the field that needs to be validated. * @param {boolean} optional whether the field is optional or not. */ function dateValidator(fieldLocation, fieldname, optional = true) { - const date = setProperValidationChainBuilder(fieldLocation, fieldname, "invalid date"); - if (optional) { - return date.optional({ - checkFalsy: true - }).custom(value => { - return !isNaN(Date.parse(value)); - }).withMessage({ - message: "Date is not valid.", - isValid: date - }); - } else { - return date.exists().withMessage("Date field must be specified").custom(value => { - return !isNaN(Date.parse(value)); - }).withMessage({ - message: "Date is not valid.", - isValid: date - }); - } + const date = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "invalid date" + ); + if (optional) { + return date + .optional({ + checkFalsy: true + }) + .custom(value => { + return !isNaN(Date.parse(value)); + }) + .withMessage({ + message: "Date is not valid.", + isValid: date + }); + } else { + return date + .exists() + .withMessage("Date field must be specified") + .custom(value => { + return !isNaN(Date.parse(value)); + }) + .withMessage({ + message: "Date is not valid.", + isValid: date + }); + } } /** * Validates that field must be a valid phone number, having numbers and having a minimum length of 8 * Regex was not chosen because the number is not restricted to a particular country or location * The smallest number length without country code is 5 digits. With the country code, it makes 8. - * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found + * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found * @param {string} fieldname name of the field that needs to be validated. * @param {boolean} optional whether the field is optional or not. */ function phoneNumberValidator(fieldLocation, fieldname, optional = true) { - const number = setProperValidationChainBuilder(fieldLocation, fieldname, "invalid phone number"); - if (optional) { - return number.optional({ - checkFalsy: true - }).isLength({ - min: 8 - }).isInt().withMessage("Phone number must consist of numbers."); - } else { - return number.exists().withMessage( - "Phone number must be specified." - ).isLength({ - min: 8 - }).isInt().withMessage("Phone number must consist of numbers."); - } + const number = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "invalid phone number" + ); + if (optional) { + return number + .optional({ + checkFalsy: true + }) + .isLength({ + min: 8 + }) + .isInt() + .withMessage("Phone number must consist of numbers."); + } else { + return number + .exists() + .withMessage("Phone number must be specified.") + .isLength({ + min: 8 + }) + .isInt() + .withMessage("Phone number must consist of numbers."); + } } /** * Validates that field must be an array of routes - * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found + * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found * @param {string} fieldname name of the field that needs to be validated. * @param {boolean} optional whether the field is optional or not. */ function routesValidator(fieldLocation, fieldname, optional = true) { - const routes = setProperValidationChainBuilder(fieldLocation, fieldname, "Invalid routes"); - - if (optional) { - return routes - .optional({ - checkFalsy: true - }) - .custom(routesArrayValidationHelper).withMessage("The value must be a route"); - } else { - return routes - .exists() - .withMessage("The route must exist.") - .custom(routesArrayValidationHelper).withMessage("The value must be a route"); - } + const routes = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "Invalid routes" + ); + + if (optional) { + return routes + .optional({ + checkFalsy: true + }) + .custom(routesArrayValidationHelper) + .withMessage("The value must be a route"); + } else { + return routes + .exists() + .withMessage("The route must exist.") + .custom(routesArrayValidationHelper) + .withMessage("The value must be a route"); + } } /** @@ -526,61 +891,70 @@ function routesValidator(fieldLocation, fieldname, optional = true) { * @param {*} routes value to check against */ function routesArrayValidationHelper(routes) { - if (!Array.isArray(routes)) { - return false; + if (!Array.isArray(routes)) { + return false; + } + for (const route of routes) { + if (route.uri === null || typeof route.uri !== "string") { + return false; } - for (const route of routes) { - if (route.uri === null || typeof route.uri !== "string") { - return false; - } - if (route.requestType === null || !checkEnum(route.requestType, Constants.REQUEST_TYPES)) { - return false; - } + if ( + route.requestType === null || + !checkEnum(route.requestType, Constants.REQUEST_TYPES) + ) { + return false; } - return true; + } + return true; } /** * Validates that field must be a value within the enum passed in through parameter 'enums' * @param {"query" | "body" | "header" | "param"} fieldLocation The location where the field should be found. * @param {string} fieldname The name of the field that needs to be validated. - * @param {Object} enums The enum object that the field must be part of. + * @param {Object} enums The enum object that the field must be part of. * @param {boolean} optional Whether the field is optional or not. */ function enumValidator(fieldLocation, fieldname, enums, optional = true) { - const enumValue = setProperValidationChainBuilder(fieldLocation, fieldname, "Invalid enums"); - - if (optional) { - return enumValue - .optional({ - checkFalsy: true - }) - .custom((val) => { - return checkEnum(val, enums); - }).withMessage("The value must be part of the enum"); - } else { - return enumValue - .exists() - .withMessage("The value being checked agains the enums must exist.") - .custom((val) => { - return checkEnum(val, enums); - }).withMessage("The value must be part of the enum"); - } + const enumValue = setProperValidationChainBuilder( + fieldLocation, + fieldname, + "Invalid enums" + ); + + if (optional) { + return enumValue + .optional({ + checkFalsy: true + }) + .custom(val => { + return checkEnum(val, enums); + }) + .withMessage("The value must be part of the enum"); + } else { + return enumValue + .exists() + .withMessage("The value being checked agains the enums must exist.") + .custom(val => { + return checkEnum(val, enums); + }) + .withMessage("The value must be part of the enum"); + } } /** * Checks that 'value' is part of 'enums'. 'enums' should be an enum dict. * @param {*} value Should be of the same type as the values of the enum - * @param {Object} enums An object that represents an enum. They keys are the keys of the enum, and the values are the enum values. + * @param {Object} enums An object that represents an enum. They keys are the keys of the enum, and the values are the enum values. * @return {boolean} Returns true if the value is part of the enum, false otherwise. */ function checkEnum(value, enums) { - for (var enumKey in enums) { - if (value === enums[enumKey]) { - return true; - } + for (var enumKey in enums) { + if (value === enums[enumKey]) { + return true; } - return false; + } + return false; } /** @@ -589,47 +963,51 @@ function checkEnum(value, enums) { * @param {string} fieldname name of the field that needs to be validated. * @param {*} errorString the string that is sent back to the user if the field is invalid */ -function setProperValidationChainBuilder(fieldLocation, fieldName, errorString) { - /** - * check: ValidationChainBuilder; - * body: ValidationChainBuilder; - * cookie: ValidationChainBuilder; - * header: ValidationChainBuilder; - * param: ValidationChainBuilder; - * query: ValidationChainBuilder; - */ - switch (fieldLocation) { - case "query": - return query(fieldName, errorString); - case "body": - return body(fieldName, errorString); - case "header": - return header(fieldName, errorString); - case "param": - return param(fieldName, errorString); - default: - logger.error(`${TAG} Invalid field location: ${fieldLocation}`); - } +function setProperValidationChainBuilder( + fieldLocation, + fieldName, + errorString +) { + /** + * check: ValidationChainBuilder; + * body: ValidationChainBuilder; + * cookie: ValidationChainBuilder; + * header: ValidationChainBuilder; + * param: ValidationChainBuilder; + * query: ValidationChainBuilder; + */ + switch (fieldLocation) { + case "query": + return query(fieldName, errorString); + case "body": + return body(fieldName, errorString); + case "header": + return header(fieldName, errorString); + case "param": + return param(fieldName, errorString); + default: + logger.error(`${TAG} Invalid field location: ${fieldLocation}`); + } } module.exports = { - regexValidator: regexValidator, - integerValidator: integerValidator, - mongoIdValidator: mongoIdValidator, - mongoIdArrayValidator: mongoIdArrayValidator, - asciiValidator: asciiValidator, - alphaValidator: alphaValidator, - alphaArrayValidator: alphaArrayValidator, - passwordValidator: passwordValidator, - booleanValidator: booleanValidator, - applicationValidator: applicationValidator, - jwtValidator: jwtValidator, - searchValidator: searchValidator, - searchModelValidator: searchModelValidator, - searchSortValidator: searchSortValidator, - phoneNumberValidator: phoneNumberValidator, - dateValidator: dateValidator, - enumValidator: enumValidator, - routesValidator: routesValidator, - stringValidator: stringValidator -}; \ No newline at end of file + regexValidator: regexValidator, + integerValidator: integerValidator, + mongoIdValidator: mongoIdValidator, + mongoIdArrayValidator: mongoIdArrayValidator, + asciiValidator: asciiValidator, + alphaValidator: alphaValidator, + alphaArrayValidator: alphaArrayValidator, + passwordValidator: passwordValidator, + booleanValidator: booleanValidator, + applicationValidator: applicationValidator, + jwtValidator: jwtValidator, + searchValidator: searchValidator, + searchModelValidator: searchModelValidator, + searchSortValidator: searchSortValidator, + phoneNumberValidator: phoneNumberValidator, + dateValidator: dateValidator, + enumValidator: enumValidator, + routesValidator: routesValidator, + stringValidator: stringValidator +}; diff --git a/models/hacker.model.js b/models/hacker.model.js index 26316e6b..df7e5408 100644 --- a/models/hacker.model.js +++ b/models/hacker.model.js @@ -4,116 +4,136 @@ const Constants = require("../constants/general.constant"); const mongoose = require("mongoose"); //describes the data type const HackerSchema = new mongoose.Schema({ - //account id - accountId: { - type: mongoose.Schema.Types.ObjectId, - ref: "Account", - required: true - }, - status: { - type: String, - enum: Constants.HACKER_STATUSES, - required: true, - default: "None" - }, - school: { + //account id + accountId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Account", + required: true + }, + status: { + type: String, + enum: Constants.HACKER_STATUSES, + required: true, + default: "None" + }, + application: { + general: { + school: { type: String, required: true - }, - degree: { + }, + degree: { type: String, required: true - }, - //no enum for this - gender: { - type: String - }, - needsBus: Boolean, - application: { - portfolioURL: { - //gcloud bucket link - resume: { - type: String, - default: "" - }, - github: { - type: String - }, - dropler: { - type: String - }, - personal: { - type: String - }, - linkedIn: { - type: String - }, - other: { - type: String - } + }, + fieldOfStudy: [ + { + type: String, + required: true + } + ], + graduationYear: { + type: Number, + required: true + }, + jobInterest: { + type: String, + enum: Constants.JOB_INTERESTS, + required: true, + default: "None" + }, + URL: { + //gcloud bucket link + resume: { + type: String, + default: "" }, - jobInterest: { - type: String, - enum: Constants.JOB_INTERESTS, - required: true, - default: "None" + github: { + type: String }, - skills: [{ - type: String - }], - //any miscelaneous comments that the user has - comments: { - type: String, - default: "" + dribbble: { + type: String }, - //"Why do you want to come to our hackathon?" - essay: { - type: String, - default: "" + personal: { + type: String + }, + linkedIn: { + type: String }, - team: { - type: mongoose.Schema.Types.ObjectId, - ref: "Team" + other: { + type: String } + } }, - ethnicity: { - type: [{ + shortAnswer: { + skills: [ + { + type: String + } + ], + //any miscelaneous comments that the user has + comments: { + type: String, + default: "" + }, + //"Why do you want to come to our hackathon?" + question1: { + type: String, + default: "" + }, + // "Some Q" + question2: { + type: String, + default: "" + } + }, + other: { + ethnicity: { + type: [ + { type: String, required: true - }], + } + ], required: true - }, - major: [{ - type: String, - required: true - }], - graduationYear: { - type: Number, - required: true - }, - codeOfConduct: { + }, + //no enum for this + gender: { + type: String + }, + codeOfConduct: { type: Boolean, required: true + } }, - teamId: { - type: mongoose.Schema.Types.ObjectId, - ref: "Team" + accomodation: { + needsBus: Boolean + }, + team: { + type: mongoose.Schema.Types.ObjectId, + ref: "Team" } + }, + teamId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Team" + } }); -HackerSchema.methods.toJSON = function () { - const hs = this.toObject(); - delete hs.__v; - hs.id = hs._id; - delete hs._id; - return hs; +HackerSchema.methods.toJSON = function() { + const hs = this.toObject(); + delete hs.__v; + hs.id = hs._id; + delete hs._id; + return hs; }; -HackerSchema.methods.isApplicationComplete = function () { - const hs = this.toObject(); - const portfolioDone = !!hs.application.portfolioURL.resume; - const jobInterestDone = !!hs.application.jobInterest; - const essayDone = !!hs.application.essay; - return portfolioDone && jobInterestDone && essayDone; +HackerSchema.methods.isApplicationComplete = function() { + const hs = this.toObject(); + const portfolioDone = !!hs.application.general.URL.resume; + const jobInterestDone = !!hs.application.general.jobInterest; + const question1Done = !!hs.application.shortAnswer.question1; + const question2Done = !!hs.application.shortAnswer.question2; + return portfolioDone && jobInterestDone && question1Done && question2Done; }; /** @@ -121,14 +141,14 @@ HackerSchema.methods.isApplicationComplete = function () { * @returns {String} type of the field being queried * @description return the type of the field(if it exists and is allowed to be searched on) */ -HackerSchema.statics.searchableField = function (field) { - const schemaField = HackerSchema.path(field) - if (schemaField != undefined) { - return schemaField.instance - } else { - return null; - } +HackerSchema.statics.searchableField = function(field) { + const schemaField = HackerSchema.path(field); + if (schemaField != undefined) { + return schemaField.instance; + } else { + return null; + } }; //export the model -module.exports = mongoose.model("Hacker", HackerSchema); \ No newline at end of file +module.exports = mongoose.model("Hacker", HackerSchema); diff --git a/services/account.service.js b/services/account.service.js index 511331e1..599696d3 100644 --- a/services/account.service.js +++ b/services/account.service.js @@ -10,26 +10,29 @@ const bcrypt = require("bcrypt"); * @description Finds an account by mongoID. */ function findById(id) { - const TAG = `[Account Service # findById]:`; - const query = { - _id: id - }; - - return Account.findById(query, logger.queryCallbackFactory(TAG, "account", query)); + const TAG = `[Account Service # findById]:`; + const query = { + _id: id + }; + + return Account.findById( + query, + logger.queryCallbackFactory(TAG, "account", query) + ); } /** * @function findByEmail - * @param {String} email + * @param {String} email * @return {DocumentQuery} The document query will resolve to either account or null. * @description Find an account by email. */ function findByEmail(email) { - const query = { - email: email - }; + const query = { + email: email + }; - return findOne(query); + return findOne(query); } /** @@ -38,12 +41,12 @@ function findByEmail(email) { * @return {Account | null} either account or null */ async function getAccountIfValid(email, password) { - const account = await findByEmail(email); + const account = await findByEmail(email); - if (!!account && account.comparePassword(password)) { - return account; - } - return null; + if (!!account && account.comparePassword(password)) { + return account; + } + return null; } /** @@ -53,7 +56,7 @@ async function getAccountIfValid(email, password) { * @description Hashes password with bcrypt. */ function hashPassword(password) { - return bcrypt.hashSync(password, 10); + return bcrypt.hashSync(password, 10); } /** @@ -63,9 +66,12 @@ function hashPassword(password) { * @description Finds an account by some query. */ function findOne(query) { - const TAG = `[Account Service # findOne ]:`; + const TAG = `[Account Service # findOne ]:`; - return Account.findOne(query, logger.queryCallbackFactory(TAG, "account", query)); + return Account.findOne( + query, + logger.queryCallbackFactory(TAG, "account", query) + ); } /** @@ -75,28 +81,32 @@ function findOne(query) { * @description Adds a new account to database. */ function addOneAccount(accountDetails) { - const TAG = `[Account Service # addOneAccount ]:`; + const TAG = `[Account Service # addOneAccount ]:`; - const account = new Account(accountDetails); + const account = new Account(accountDetails); - return account.save(); + return account.save(); } /** * @function updateOne * @param {ObjectId} id - * @param {{_id?: ObjectId, firstName?: string, lastName?: string, email?: string, password?: string, dietaryRestrictions?: string, shirtSize?: string}} accountDetails + * @param {{_id?: ObjectId, firstName?: string, lastName?: string, email?: string, password?: string, dietaryRestrictions?: string, shirtSize?: string}} accountDetails * @return {DocumentQuery} The document query will resolve to either account or null. * @description Changes account information to the specified information in accountDetails. */ function updateOne(id, accountDetails) { - const TAG = `[Account Service # updateOne ]:`; + const TAG = `[Account Service # updateOne ]:`; - const query = { - _id: id - }; + const query = { + _id: id + }; - return Account.findOneAndUpdate(query, accountDetails, logger.updateCallbackFactory(TAG, "account")); + return Account.findOneAndUpdate( + query, + accountDetails, + logger.updateCallbackFactory(TAG, "account") + ); } /** @@ -105,20 +115,19 @@ function updateOne(id, accountDetails) { * @param {string} newPassword the new password for the account (in plain-text). */ function updatePassword(id, newPassword) { - const hashed = hashPassword(newPassword); - return updateOne(id, { - password: hashed - }); + const hashed = hashPassword(newPassword); + return updateOne(id, { + password: hashed + }); } - module.exports = { - findOne: findOne, - findById: findById, - findByEmail: findByEmail, - addOneAccount: addOneAccount, - getAccountIfValid: getAccountIfValid, - hashPassword: hashPassword, - updateOne: updateOne, - updatePassword: updatePassword -}; \ No newline at end of file + findOne: findOne, + findById: findById, + findByEmail: findByEmail, + addOneAccount: addOneAccount, + getAccountIfValid: getAccountIfValid, + hashPassword: hashPassword, + updateOne: updateOne, + updatePassword: updatePassword +}; diff --git a/services/hacker.service.js b/services/hacker.service.js index 5993b8b0..6324d612 100644 --- a/services/hacker.service.js +++ b/services/hacker.service.js @@ -15,11 +15,11 @@ const QRCode = require("qrcode"); * @description Adds a new hacker to database. */ function createHacker(hackerDetails) { - const TAG = `[Hacker Service # createHacker]:`; + const TAG = `[Hacker Service # createHacker]:`; - const hacker = new Hacker(hackerDetails); + const hacker = new Hacker(hackerDetails); - return hacker.save(); + return hacker.save(); } /** @@ -30,13 +30,17 @@ function createHacker(hackerDetails) { * @description Update an account specified by its mongoId with information specified by hackerDetails. */ function updateOne(id, hackerDetails) { - const TAG = `[Hacker Service # update ]:`; + const TAG = `[Hacker Service # update ]:`; - const query = { - _id: id - }; + const query = { + _id: id + }; - return Hacker.findOneAndUpdate(query, hackerDetails, logger.updateCallbackFactory(TAG, "hacker")); + return Hacker.findOneAndUpdate( + query, + hackerDetails, + logger.updateCallbackFactory(TAG, "hacker") + ); } /** @@ -46,9 +50,9 @@ function updateOne(id, hackerDetails) { * @description Finds an hacker by the id, which is the mongoId. */ function findById(id) { - const TAG = `[Hacker Service # findById ]:`; + const TAG = `[Hacker Service # findById ]:`; - return Hacker.findById(id, logger.queryCallbackFactory(TAG, "hacker", id)); + return Hacker.findById(id, logger.queryCallbackFactory(TAG, "hacker", id)); } /** @@ -59,14 +63,18 @@ function findById(id) { * @description Finds an hacker by some query. */ async function findIds(queries) { - const TAG = `[Hacker Service # findIds ]:`; - let ids = []; - - for (const query of queries) { - let currId = await Hacker.findOne(query, "_id", logger.queryCallbackFactory(TAG, "hacker", query)); - ids.push(currId); - } - return ids; + const TAG = `[Hacker Service # findIds ]:`; + let ids = []; + + for (const query of queries) { + let currId = await Hacker.findOne( + query, + "_id", + logger.queryCallbackFactory(TAG, "hacker", query) + ); + ids.push(currId); + } + return ids; } /** @@ -75,26 +83,29 @@ async function findIds(queries) { * @return {DocumentQuery} A hacker document queried by accountId */ function findByAccountId(accountId) { - const TAG = `[ Hacker Service # findByAccountId ]:`; - - const query = { - accountId: accountId - }; + const TAG = `[ Hacker Service # findByAccountId ]:`; + const query = { + accountId: accountId + }; + console.log(query); - return Hacker.findOne(query, logger.updateCallbackFactory(TAG, "hacker")); + return Hacker.findOne(query, logger.updateCallbackFactory(TAG, "hacker")); } async function getStatsAllHackersCached() { - const TAG = `[ hacker Service # getStatsAll ]`; - if (cache.get(Constants.CACHE_KEY_STATS) !== null) { - logger.info(`${TAG} Getting cached stats`); - return cache.get(Constants.CACHE_KEY_STATS); - } - const allHackers = await Hacker.find({}, logger.updateCallbackFactory(TAG, "hacker")).populate({ - path: "accountId", - }); - cache.put(Constants.CACHE_KEY_STATS, stats, Constants.CACHE_TIMEOUT_STATS); //set a time-out of 5 minutes - return getStats(allHackers); + const TAG = `[ hacker Service # getStatsAll ]`; + if (cache.get(Constants.CACHE_KEY_STATS) !== null) { + logger.info(`${TAG} Getting cached stats`); + return cache.get(Constants.CACHE_KEY_STATS); + } + const allHackers = await Hacker.find( + {}, + logger.updateCallbackFactory(TAG, "hacker") + ).populate({ + path: "accountId" + }); + cache.put(Constants.CACHE_KEY_STATS, stats, Constants.CACHE_TIMEOUT_STATS); //set a time-out of 5 minutes + return getStats(allHackers); } /** @@ -102,10 +113,10 @@ async function getStatsAllHackersCached() { * @param {string} str The string to be encoded in the QR code. */ async function generateQRCode(str) { - const response = await QRCode.toDataURL(str, { - scale: 4 - }); - return response; + const response = await QRCode.toDataURL(str, { + scale: 4 + }); + return response; } /** @@ -115,67 +126,95 @@ async function generateQRCode(str) { * @param {string} id The ID of the hacker to view */ function generateHackerViewLink(httpOrHttps, domain, id) { - const link = `${httpOrHttps}://${domain}/application/view/${id}`; - return link; + const link = `${httpOrHttps}://${domain}/application/view/${id}`; + return link; } function getStats(hackers) { - const TAG = `[ hacker Service # getStats ]`; - const stats = { - total: 0, - status: {}, - school: {}, - degree: {}, - gender: {}, - needsBus: {}, - ethnicity: {}, - jobInterest: {}, - major: {}, - graduationYear: {}, - dietaryRestrictions: {}, - shirtSize: {}, - age: {} - }; - - hackers.forEach((hacker) => { - if (!hacker.accountId) { - // user is no longer with us for some reason :( - return; - } - stats.total += 1; - stats.status[hacker.status] = (stats.status[hacker.status]) ? stats.status[hacker.status] + 1 : 1; - stats.school[hacker.school] = (stats.school[hacker.school]) ? stats.school[hacker.school] + 1 : 1; - stats.degree[hacker.degree] = (stats.degree[hacker.degree]) ? stats.degree[hacker.degree] + 1 : 1; - stats.gender[hacker.gender] = (stats.gender[hacker.gender]) ? stats.gender[hacker.gender] + 1 : 1; - stats.needsBus[hacker.needsBus] = (stats.needsBus[hacker.needsBus]) ? stats.needsBus[hacker.needsBus] + 1 : 1; - - for (const ethnicity of hacker.ethnicity) { - stats.ethnicity[ethnicity] = (stats.ethnicity[ethnicity]) ? stats.ethnicity[ethnicity] + 1 : 1; - } - - stats.jobInterest[hacker.application.jobInterest] = (stats.jobInterest[hacker.application.jobInterest]) ? stats.jobInterest[hacker.application.jobInterest] + 1 : 1; - stats.major[hacker.major] = (stats.major[hacker.major]) ? stats.major[hacker.major] + 1 : 1; - stats.graduationYear[hacker.graduationYear] = (stats.graduationYear[hacker.graduationYear]) ? stats.graduationYear[hacker.graduationYear] + 1 : 1; - - for (const dietaryRestrictions of hacker.accountId.dietaryRestrictions) { - stats.dietaryRestrictions[dietaryRestrictions] = (stats.dietaryRestrictions[dietaryRestrictions]) ? stats.dietaryRestrictions[dietaryRestrictions] + 1 : 1; - } - stats.shirtSize[hacker.accountId.shirtSize] = (stats.shirtSize[hacker.accountId.shirtSize]) ? stats.shirtSize[hacker.accountId.shirtSize] + 1 : 1; - const age = hacker.accountId.getAge(); - stats.age[age] = (stats.age[age]) ? stats.age[age] + 1 : 1; - }); - return stats; -} + const TAG = `[ hacker Service # getStats ]`; + const stats = { + total: 0, + status: {}, + school: {}, + degree: {}, + gender: {}, + needsBus: {}, + ethnicity: {}, + jobInterest: {}, + major: {}, + graduationYear: {}, + dietaryRestrictions: {}, + shirtSize: {}, + age: {} + }; + + hackers.forEach(hacker => { + if (!hacker.accountId) { + // user is no longer with us for some reason :( + return; + } + stats.total += 1; + stats.status[hacker.status] = stats.status[hacker.status] + ? stats.status[hacker.status] + 1 + : 1; + stats.school[hacker.school] = stats.school[hacker.school] + ? stats.school[hacker.school] + 1 + : 1; + stats.degree[hacker.degree] = stats.degree[hacker.degree] + ? stats.degree[hacker.degree] + 1 + : 1; + stats.gender[hacker.gender] = stats.gender[hacker.gender] + ? stats.gender[hacker.gender] + 1 + : 1; + stats.needsBus[hacker.needsBus] = stats.needsBus[hacker.needsBus] + ? stats.needsBus[hacker.needsBus] + 1 + : 1; + + for (const ethnicity of hacker.ethnicity) { + stats.ethnicity[ethnicity] = stats.ethnicity[ethnicity] + ? stats.ethnicity[ethnicity] + 1 + : 1; + } + stats.jobInterest[hacker.application.jobInterest] = stats.jobInterest[ + hacker.application.jobInterest + ] + ? stats.jobInterest[hacker.application.jobInterest] + 1 + : 1; + stats.major[hacker.major] = stats.major[hacker.major] + ? stats.major[hacker.major] + 1 + : 1; + stats.graduationYear[hacker.graduationYear] = stats.graduationYear[ + hacker.graduationYear + ] + ? stats.graduationYear[hacker.graduationYear] + 1 + : 1; + + for (const dietaryRestrictions of hacker.accountId.dietaryRestrictions) { + stats.dietaryRestrictions[dietaryRestrictions] = stats + .dietaryRestrictions[dietaryRestrictions] + ? stats.dietaryRestrictions[dietaryRestrictions] + 1 + : 1; + } + stats.shirtSize[hacker.accountId.shirtSize] = stats.shirtSize[ + hacker.accountId.shirtSize + ] + ? stats.shirtSize[hacker.accountId.shirtSize] + 1 + : 1; + const age = hacker.accountId.getAge(); + stats.age[age] = stats.age[age] ? stats.age[age] + 1 : 1; + }); + return stats; +} module.exports = { - createHacker: createHacker, - findById: findById, - updateOne: updateOne, - findIds: findIds, - findByAccountId: findByAccountId, - getStats: getStats, - getStatsAllHackersCached: getStatsAllHackersCached, - generateQRCode: generateQRCode, - generateHackerViewLink: generateHackerViewLink, -}; \ No newline at end of file + createHacker: createHacker, + findById: findById, + updateOne: updateOne, + findIds: findIds, + findByAccountId: findByAccountId, + getStats: getStats, + getStatsAllHackersCached: getStatsAllHackersCached, + generateQRCode: generateQRCode, + generateHackerViewLink: generateHackerViewLink +}; From 795832d7178d29bc7216d32489647cae8cee34e4 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Fri, 29 Nov 2019 20:39:43 -0500 Subject: [PATCH 02/28] minor changes to validation schema --- middlewares/validators/hacker.validator.js | 8 ------- middlewares/validators/validator.helper.js | 27 ++++++++++++++++------ models/hacker.model.js | 6 ++++- services/hacker.service.js | 1 - 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/middlewares/validators/hacker.validator.js b/middlewares/validators/hacker.validator.js index 90fc942a..27fd7d2d 100644 --- a/middlewares/validators/hacker.validator.js +++ b/middlewares/validators/hacker.validator.js @@ -6,15 +6,7 @@ module.exports = { newHackerValidator: [ // status will be added automatically VALIDATOR.mongoIdValidator("body", "accountId", false), - // VALIDATOR.stringValidator("body", "school", false), - // VALIDATOR.stringValidator("body", "degree", false), - // VALIDATOR.stringValidator("body", "gender", false), - // VALIDATOR.booleanValidator("body", "needsBus", false), VALIDATOR.applicationValidator("body", "application", false), - // VALIDATOR.alphaArrayValidator("body", "ethnicity", false), - // VALIDATOR.alphaArrayValidator("body", "major", false), - // VALIDATOR.integerValidator("body", "graduationYear", false, 2019, 2030), - // VALIDATOR.booleanValidator("body", "codeOfConduct", false, true), VALIDATOR.mongoIdValidator("body", "teamId", true) ], diff --git a/middlewares/validators/validator.helper.js b/middlewares/validators/validator.helper.js index 7548369d..c674e2d1 100644 --- a/middlewares/validators/validator.helper.js +++ b/middlewares/validators/validator.helper.js @@ -390,7 +390,8 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { ethnicity: false, bus: false, graduationYear: false, - codeOfConduct: false, + codeOfConduct_MLH: false, + codeOfConduct_MCHACKS: false, github: false, dribbble: false, personal: false, @@ -446,9 +447,14 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { 2019, 2030 ); - hasValid.codeOfConduct = booleanValidator( + hasValid.codeOfConduct_MLH = booleanValidator( "body", - "application.accomodation.needsBus", + "application.other.codeOfConduct_MLH", + false + ); + hasValid.codeOfConduct_MCHACKS = booleanValidator( + "body", + "appliction.other.codeOfConduct_MCHACKS", false ); const jobInterests = Constants.JOB_INTERESTS; @@ -489,7 +495,8 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { hasValid.graduationYear && hasValid.ethnicity && hasValid.bus && - hasValid.codeOfConduct && + hasValid.codeOfConduct_MLH && + hasValid.codeOfConduct_MCHACKS && hasValid.github && hasValid.dribbble && hasValid.personal && @@ -544,9 +551,14 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { 2019, 2030 ); - hasValid.codeOfConduct = booleanValidator( + hasValid.codeOfConduct_MLH = booleanValidator( "body", - "application.accomodation.needsBus", + "application.other.codeOfConduct_MLH", + false + ); + hasValid.codeOfConduct_MCHACKS = booleanValidator( + "body", + "appliction.other.codeOfConduct_MCHACKS", false ); const jobInterests = Constants.JOB_INTERESTS; @@ -587,7 +599,8 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { hasValid.graduationYear && hasValid.ethnicity && hasValid.bus && - hasValid.codeOfConduct && + hasValid.codeOfConduct_MLH && + hasValid.codeOfConduct_MCHACKS && hasValid.github && hasValid.dribbble && hasValid.personal && diff --git a/models/hacker.model.js b/models/hacker.model.js index df7e5408..dd9a75ac 100644 --- a/models/hacker.model.js +++ b/models/hacker.model.js @@ -101,7 +101,11 @@ const HackerSchema = new mongoose.Schema({ gender: { type: String }, - codeOfConduct: { + codeOfConduct_MLH: { + type: Boolean, + required: true + }, + codeOfConduct_MCHACKS: { type: Boolean, required: true } diff --git a/services/hacker.service.js b/services/hacker.service.js index 6324d612..14b33fbb 100644 --- a/services/hacker.service.js +++ b/services/hacker.service.js @@ -87,7 +87,6 @@ function findByAccountId(accountId) { const query = { accountId: accountId }; - console.log(query); return Hacker.findOne(query, logger.updateCallbackFactory(TAG, "hacker")); } From 8391fba2cff127b7131c815b0c8d925a913391e4 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Sat, 30 Nov 2019 11:40:27 -0500 Subject: [PATCH 03/28] Changed parsing of hacker and validation of application --- controllers/hacker.controller.js | 105 ++-- middlewares/hacker.middleware.js | 29 +- middlewares/validators/validator.helper.js | 13 + models/hacker.model.js | 15 +- routes/api/hacker.js | 673 +++++++++++---------- services/hacker.service.js | 4 +- services/storage.service.js | 157 ++--- 7 files changed, 498 insertions(+), 498 deletions(-) diff --git a/controllers/hacker.controller.js b/controllers/hacker.controller.js index fb2182c6..cecd9449 100644 --- a/controllers/hacker.controller.js +++ b/controllers/hacker.controller.js @@ -1,7 +1,7 @@ "use strict"; const Constants = { - Success: require("../constants/success.constant"), - Error: require("../constants/error.constant"), + Success: require("../constants/success.constant"), + Error: require("../constants/error.constant") }; /** @@ -12,24 +12,24 @@ const Constants = { * @description Returns the JSON of hacker object located in req.body.hacker */ function showHacker(req, res) { - return res.status(200).json({ - message: Constants.Success.HACKER_READ, - data: req.body.hacker.toJSON() - }); + return res.status(200).json({ + message: Constants.Success.HACKER_READ, + data: req.body.hacker.toJSON() + }); } /** * @function createdHacker - * @param {{body: {hacker: {_id: ObjectId, accountId: ObjectId, school: string, gender: string, needsBus: boolean, application: {Object}}}}} req + * @param {{body: {hacker: {_id: ObjectId, accountId: ObjectId, status: string, application: {Object}}}}} req * @param {*} res * @return {JSON} Success status * @description returns success message */ function createdHacker(req, res) { - return res.status(200).json({ - message: Constants.Success.HACKER_CREATE, - data: req.body.hacker.toJSON() - }); + return res.status(200).json({ + message: Constants.Success.HACKER_CREATE, + data: req.body.hacker.toJSON() + }); } /** @@ -37,69 +37,68 @@ function createdHacker(req, res) { * @param {{params: {id: ObjectId}, body: {Object}}} req * @param {*} res * @return {JSON} Success or error status - * @description + * @description * Change a hacker's information based on the hacker's mongoID specified in req.params.id. * The id is moved to req.body.id from req.params.id by validation. * Returns a 200 status for an updated hacker. * The new information is located in req.body. */ function updatedHacker(req, res) { - return res.status(200).json({ - message: Constants.Success.HACKER_UPDATE, - data: req.body - }); + return res.status(200).json({ + message: Constants.Success.HACKER_UPDATE, + data: req.body + }); } function uploadedResume(req, res) { - return res.status(200).json({ - message: Constants.Success.RESUME_UPLOAD, - data: { - filename: req.body.gcfilename - } - }); + return res.status(200).json({ + message: Constants.Success.RESUME_UPLOAD, + data: { + filename: req.body.gcfilename + } + }); } function downloadedResume(req, res) { - return res.status(200).json({ - message: Constants.Success.RESUME_DOWNLOAD, - data: { - id: req.body.id, - resume: req.body.resume - } - }); + return res.status(200).json({ + message: Constants.Success.RESUME_DOWNLOAD, + data: { + id: req.body.id, + resume: req.body.gcfilename + } + }); } function gotStats(req, res) { - return res.status(200).json({ - message: "Retrieved stats", - data: { - stats: req.body.stats, - } - }); - + return res.status(200).json({ + message: "Retrieved stats", + data: { + stats: req.body.stats + } + }); } function sentWeekOfEmail(req, res) { - return res.status(200).json({ - message: Constants.Success.HACKER_SENT_WEEK_OF, - data: {} - }); + return res.status(200).json({ + message: Constants.Success.HACKER_SENT_WEEK_OF, + data: {} + }); } function sentDayOfEmail(req, res) { - return res.status(200).json({ - message: Constants.Success.HACKER_SENT_DAY_OF, - data: {} - }); + return res.status(200).json({ + message: Constants.Success.HACKER_SENT_DAY_OF, + data: {} + }); } module.exports = { - updatedHacker: updatedHacker, - createdHacker: createdHacker, - uploadedResume: uploadedResume, - downloadedResume: downloadedResume, - showHacker: showHacker, - gotStats: gotStats, - sentWeekOfEmail: sentWeekOfEmail, - sentDayOfEmail: sentDayOfEmail -}; \ No newline at end of file + updatedHacker: updatedHacker, + createdHacker: createdHacker, + uploadedResume: uploadedResume, + downloadedResume: downloadedResume, + showHacker: showHacker, + gotStats: gotStats, + sentWeekOfEmail: sentWeekOfEmail, + sentDayOfEmail: sentDayOfEmail +}; diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index 684f1e19..9d191d4c 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -44,32 +44,13 @@ function parseHacker(req, res, next) { const hackerDetails = { _id: mongoose.Types.ObjectId(), accountId: req.body.accountId, - school: req.body.school, - degree: req.body.degree, - gender: req.body.gender, - needsBus: req.body.needsBus, application: req.body.application, - - ethnicity: req.body.ethnicity, - major: req.body.major, - graduationYear: req.body.graduationYear, - codeOfConduct: req.body.codeOfConduct, - teamId: req.body.teamId }; req.body.token = req.body.authorization; delete req.body.accountId; - delete req.body.school; - delete req.body.degree; - delete req.body.gender; - delete req.body.needsBus; delete req.body.application; - delete req.body.authorization; - delete req.body.ethnicity; - delete req.body.major; - delete req.body.graduationYear; - delete req.body.codeOfConduct; delete req.body.teamId; req.body.hackerDetails = hackerDetails; @@ -241,7 +222,7 @@ async function uploadResume(req, res, next) { req.body.gcfilename = gcfilename; await Services.Hacker.updateOne(req.hacker.id, { $set: { - "application.portfolioURL.resume": gcfilename + "application.general.URL.resume": gcfilename } }); return next(); @@ -258,11 +239,12 @@ async function downloadResume(req, res, next) { if ( hacker && hacker.application && - hacker.application.portfolioURL && - hacker.application.portfolioURL.resume + hacker.application.general && + hacker.application.general.URL && + hacker.application.general.URL.resume ) { req.body.resume = await Services.Storage.download( - hacker.application.portfolioURL.resume + hacker.application.general.URL.resume ); } else { return next({ @@ -523,7 +505,6 @@ async function createHacker(req, res, next) { } }); } - const hacker = await Services.Hacker.createHacker(hackerDetails); if (!!hacker) { diff --git a/middlewares/validators/validator.helper.js b/middlewares/validators/validator.helper.js index c674e2d1..7ca5a91e 100644 --- a/middlewares/validators/validator.helper.js +++ b/middlewares/validators/validator.helper.js @@ -389,6 +389,7 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { gender: false, ethnicity: false, bus: false, + gender: false, graduationYear: false, codeOfConduct_MLH: false, codeOfConduct_MCHACKS: false, @@ -440,6 +441,11 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { "application.other.ethnicity", false ); + hasValid.gender = stringValidator( + "body", + "application.other.gender", + false + ); hasValid.graduationYear = integerValidator( "body", "application.general.graduationYear", @@ -494,6 +500,7 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { hasValid.fieldOfStudy && hasValid.graduationYear && hasValid.ethnicity && + hasValid.gender && hasValid.bus && hasValid.codeOfConduct_MLH && hasValid.codeOfConduct_MCHACKS && @@ -544,6 +551,11 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { "application.other.ethnicity", false ); + hasValid.gender = stringValidator( + "body", + "application.other.gender", + false + ); hasValid.graduationYear = integerValidator( "body", "application.general.graduationYear", @@ -598,6 +610,7 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { hasValid.fieldOfStudy && hasValid.graduationYear && hasValid.ethnicity && + hasValid.gender && hasValid.bus && hasValid.codeOfConduct_MLH && hasValid.codeOfConduct_MCHACKS && diff --git a/models/hacker.model.js b/models/hacker.model.js index dd9a75ac..9c78cefd 100644 --- a/models/hacker.model.js +++ b/models/hacker.model.js @@ -1,5 +1,4 @@ "use strict"; - const Constants = require("../constants/general.constant"); const mongoose = require("mongoose"); //describes the data type @@ -45,8 +44,7 @@ const HackerSchema = new mongoose.Schema({ URL: { //gcloud bucket link resume: { - type: String, - default: "" + type: String }, github: { type: String @@ -79,12 +77,14 @@ const HackerSchema = new mongoose.Schema({ //"Why do you want to come to our hackathon?" question1: { type: String, - default: "" + default: "", + required: true }, // "Some Q" question2: { type: String, - default: "" + default: "", + required: true } }, other: { @@ -97,9 +97,9 @@ const HackerSchema = new mongoose.Schema({ ], required: true }, - //no enum for this gender: { - type: String + type: String, + required: true }, codeOfConduct_MLH: { type: Boolean, @@ -133,6 +133,7 @@ HackerSchema.methods.toJSON = function() { }; HackerSchema.methods.isApplicationComplete = function() { const hs = this.toObject(); + console.log(hs); const portfolioDone = !!hs.application.general.URL.resume; const jobInterestDone = !!hs.application.general.jobInterest; const question1Done = !!hs.application.shortAnswer.question1; diff --git a/routes/api/hacker.js b/routes/api/hacker.js index b0289e35..e3518966 100644 --- a/routes/api/hacker.js +++ b/routes/api/hacker.js @@ -1,32 +1,32 @@ "use strict"; const express = require("express"); const Controllers = { - Hacker: require("../../controllers/hacker.controller") + Hacker: require("../../controllers/hacker.controller") }; const Middleware = { - Validator: { - /* Insert the require statement to the validator file here */ - Hacker: require("../../middlewares/validators/hacker.validator"), - RouteParam: require("../../middlewares/validators/routeParam.validator") - }, - /* Insert all of ther middleware require statements here */ - parseBody: require("../../middlewares/parse-body.middleware"), - Util: require("../../middlewares/util.middleware"), - Hacker: require("../../middlewares/hacker.middleware"), - Auth: require("../../middlewares/auth.middleware"), - Search: require("../../middlewares/search.middleware") + Validator: { + /* Insert the require statement to the validator file here */ + Hacker: require("../../middlewares/validators/hacker.validator"), + RouteParam: require("../../middlewares/validators/routeParam.validator") + }, + /* Insert all of ther middleware require statements here */ + parseBody: require("../../middlewares/parse-body.middleware"), + Util: require("../../middlewares/util.middleware"), + Hacker: require("../../middlewares/hacker.middleware"), + Auth: require("../../middlewares/auth.middleware"), + Search: require("../../middlewares/search.middleware") }; const Services = { - Hacker: require("../../services/hacker.service"), - Account: require("../../services/account.service") + Hacker: require("../../services/hacker.service"), + Account: require("../../services/account.service") }; const CONSTANTS = require("../../constants/general.constant"); module.exports = { - activate: function(apiRouter) { - const hackerRouter = express.Router(); + activate: function(apiRouter) { + const hackerRouter = express.Router(); - /** + /** * @api {get} /hacker/self get information about own hacker * @apiName self * @apiGroup Hacker @@ -70,15 +70,15 @@ module.exports = { * @apiErrorExample {object} Error-Response: * {"message": "Hacker not found", "data": {}} */ - hackerRouter.route("/self").get( - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized(), + hackerRouter.route("/self").get( + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized(), - Middleware.Hacker.findSelf, - Controllers.Hacker.showHacker - ); + Middleware.Hacker.findSelf, + Controllers.Hacker.showHacker + ); - /** + /** * @api {post} /hacker/ create a new hacker * @apiName createHacker * @apiGroup Hacker @@ -121,7 +121,7 @@ module.exports = { "portfolioURL":{ "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91", "github":"https://github.com/abcd", - "dropler":"https://dribbble.com/abcd", + "dribbble":"https://dribbble.com/abcd", "personal":"https://www.hi.com/", "linkedIn":"https://linkedin.com/in/abcd", "other":"https://github.com/hackmcgill/hackerAPI/issues/168" @@ -148,28 +148,28 @@ module.exports = { * @apiErrorExample {object} Error-Response: * {"message": "Error while creating hacker", "data": {}} */ - hackerRouter.route("/").post( - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized(), + hackerRouter.route("/").post( + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized(), - Middleware.Validator.Hacker.newHackerValidator, + Middleware.Validator.Hacker.newHackerValidator, - Middleware.parseBody.middleware, - // validate type - Middleware.Hacker.validateConfirmedStatus, - // validate that the accountId is not being used for any other thing - Middleware.Hacker.checkDuplicateAccountLinks, + Middleware.parseBody.middleware, + // validate type + Middleware.Hacker.validateConfirmedStatus, + // validate that the accountId is not being used for any other thing + Middleware.Hacker.checkDuplicateAccountLinks, - Middleware.Hacker.parseHacker, + Middleware.Hacker.parseHacker, - Middleware.Hacker.addDefaultStatus, - Middleware.Auth.createRoleBindings(CONSTANTS.HACKER), - Middleware.Hacker.createHacker, - Middleware.Hacker.sendAppliedStatusEmail, - Controllers.Hacker.createdHacker - ); + Middleware.Hacker.addDefaultStatus, + Middleware.Auth.createRoleBindings(CONSTANTS.HACKER), + Middleware.Hacker.createHacker, + Middleware.Hacker.sendAppliedStatusEmail, + Controllers.Hacker.createdHacker + ); - /** + /** * @api {get} /hacker/stats * Gets the stats of all of the hackers who have applied. * @apiName getHackerStats @@ -204,86 +204,90 @@ module.exports = { * } * */ - hackerRouter.route("/stats").get( - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized(), - Middleware.Validator.Hacker.statsValidator, - Middleware.parseBody.middleware, - Middleware.Search.setExpandTrue, - Middleware.Search.parseQuery, - Middleware.Search.executeQuery, - Middleware.Hacker.getStats, - Controllers.Hacker.gotStats - ); - - /** - * @api {patch} /hacker/status/:id update a hacker's status - * @apiName patchHackerStatus - * @apiGroup Hacker - * @apiVersion 0.0.9 - * - * @apiParam (body) {string} [status] Status of the hacker's application ("None"|"Applied"|"Waitlisted"|"Confirmed"|"Cancelled"|"Checked-in") - * @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("/status/:id").patch( - Middleware.Validator.RouteParam.idValidator, - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - Middleware.Validator.Hacker.updateStatusValidator, - Middleware.parseBody.middleware, - Middleware.Hacker.parsePatch, - Middleware.Hacker.updateHacker, - Middleware.Hacker.sendStatusUpdateEmail, - 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. - * @apiName checkinHacker - * @apiGroup Hacker - * @apiVersion 0.0.9 - * @apiParam (body) {string} [status] Check-in status. "Checked-in" - * @apiSuccess {string} message Success message - * @apiSuccess {object} data Hacker object - * @apiSuccessExample {object} Success-Response: - * { - * "message": "Changed hacker information", - * "data": { - * "status": "Checked-in" - * } - * } - * @apiPermission Administrator - * @apiPermission Volunteer - */ - hackerRouter.route("/checkin/:id").patch( - Middleware.Validator.RouteParam.idValidator, - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - - Middleware.parseBody.middleware, - Middleware.Hacker.parsePatch, - - Middleware.Hacker.checkStatus([ - CONSTANTS.HACKER_STATUS_ACCEPTED, - CONSTANTS.HACKER_STATUS_CONFIRMED - ]), - Middleware.Hacker.parseCheckIn, - Middleware.Hacker.updateHacker, - - Middleware.Hacker.sendStatusUpdateEmail, - Controllers.Hacker.updatedHacker - ); - - /** + hackerRouter + .route("/stats") + .get( + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized(), + Middleware.Validator.Hacker.statsValidator, + Middleware.parseBody.middleware, + Middleware.Search.setExpandTrue, + Middleware.Search.parseQuery, + Middleware.Search.executeQuery, + Middleware.Hacker.getStats, + Controllers.Hacker.gotStats + ); + + /** + * @api {patch} /hacker/status/:id update a hacker's status + * @apiName patchHackerStatus + * @apiGroup Hacker + * @apiVersion 0.0.9 + * + * @apiParam (body) {string} [status] Status of the hacker's application ("None"|"Applied"|"Waitlisted"|"Confirmed"|"Cancelled"|"Checked-in") + * @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("/status/:id") + .patch( + Middleware.Validator.RouteParam.idValidator, + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + Middleware.Validator.Hacker.updateStatusValidator, + Middleware.parseBody.middleware, + Middleware.Hacker.parsePatch, + Middleware.Hacker.updateHacker, + Middleware.Hacker.sendStatusUpdateEmail, + 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. + * @apiName checkinHacker + * @apiGroup Hacker + * @apiVersion 0.0.9 + * @apiParam (body) {string} [status] Check-in status. "Checked-in" + * @apiSuccess {string} message Success message + * @apiSuccess {object} data Hacker object + * @apiSuccessExample {object} Success-Response: + * { + * "message": "Changed hacker information", + * "data": { + * "status": "Checked-in" + * } + * } + * @apiPermission Administrator + * @apiPermission Volunteer + */ + hackerRouter.route("/checkin/:id").patch( + Middleware.Validator.RouteParam.idValidator, + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + + Middleware.parseBody.middleware, + Middleware.Hacker.parsePatch, + + Middleware.Hacker.checkStatus([ + CONSTANTS.HACKER_STATUS_ACCEPTED, + CONSTANTS.HACKER_STATUS_CONFIRMED + ]), + Middleware.Hacker.parseCheckIn, + Middleware.Hacker.updateHacker, + + Middleware.Hacker.sendStatusUpdateEmail, + Controllers.Hacker.updatedHacker + ); + + /** * @api {patch} /hacker/:id update a hacker's information. * @apiDescription This route only contains the ability to update a subset of a hacker's information. If you want to update a status, you must have Admin priviledges and use PATCH /hacker/status/:id. * @apiName patchHacker @@ -302,7 +306,7 @@ module.exports = { "portfolioURL":{ "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91", "github":"https://github.com/abcd", - "dropler":"https://dribbble.com/abcd", + "dribbble":"https://dribbble.com/abcd", "personal":"https://www.hi.com/", "linkedIn":"https://linkedin.com/in/abcd", "other":"https://github.com/hackmcgill/hackerAPI/issues/168" @@ -324,7 +328,7 @@ module.exports = { "portfolioURL":{ "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91", "github":"https://github.com/abcd", - "dropler":"https://dribbble.com/abcd", + "dribbble":"https://dribbble.com/abcd", "personal":"https://www.hi.com/", "linkedIn":"https://linkedin.com/in/abcd", "other":"https://github.com/hackmcgill/hackerAPI/issues/168" @@ -349,22 +353,22 @@ module.exports = { * @apiErrorExample {object} Error-Response: * {"message": "Error while updating hacker", "data": {}} */ - hackerRouter.route("/:id").patch( - Middleware.Validator.RouteParam.idValidator, - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + hackerRouter.route("/:id").patch( + Middleware.Validator.RouteParam.idValidator, + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - Middleware.Validator.Hacker.updateHackerValidator, + Middleware.Validator.Hacker.updateHackerValidator, - Middleware.parseBody.middleware, - Middleware.Hacker.parsePatch, + Middleware.parseBody.middleware, + Middleware.Hacker.parsePatch, - Middleware.Hacker.updateHacker, - Middleware.Hacker.updateStatusIfApplicationCompleted, - Controllers.Hacker.updatedHacker - ); + Middleware.Hacker.updateHacker, + Middleware.Hacker.updateStatusIfApplicationCompleted, + Controllers.Hacker.updatedHacker + ); - /** + /** * @api {get} /hacker/:id get a hacker's information * @apiName getHacker * @apiGroup Hacker @@ -410,18 +414,18 @@ module.exports = { * @apiErrorExample {object} Error-Response: * {"message": "Hacker not found", "data": {}} */ - hackerRouter.route("/:id").get( - Middleware.Validator.RouteParam.idValidator, - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + hackerRouter.route("/:id").get( + Middleware.Validator.RouteParam.idValidator, + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - Middleware.parseBody.middleware, + Middleware.parseBody.middleware, - Middleware.Hacker.findById, - Controllers.Hacker.showHacker - ); + Middleware.Hacker.findById, + Controllers.Hacker.showHacker + ); - /** + /** * @api {get} /hacker/email/:email get a hacker's information * @apiName getHacker * @apiGroup Hacker @@ -467,193 +471,194 @@ module.exports = { * @apiErrorExample {object} Error-Response: * {"message": "Hacker not found", "data": {}} */ - hackerRouter.route("/email/:email").get( - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Account.findByEmail]), - - Middleware.Validator.RouteParam.emailValidator, - Middleware.parseBody.middleware, - - Middleware.Hacker.findByEmail, - Controllers.Hacker.showHacker - ); - - hackerRouter.route("/resume/:id") - /** - * @api {post} /hacker/resume/:id upload or update resume for a hacker. - * @apiName postHackerResume - * @apiGroup Hacker - * @apiVersion 0.0.8 - * @apiDescription NOTE: This must be sent via multipart/form-data POST request - * - * @apiParam (param) {ObjectId} id Hacker id - * @apiParam (body) {File} resume The uploaded file. - * - * @apiSuccess {String} message Success message - * @apiSuccess {Object} data Location in the bucket that the file was stored. - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * message: "Uploaded resume", - * data: { - * filename: "resumes/1535032624768-507f191e810c19729de860ea" - * } - * } - * - * @apiPermission Must be logged in, and the account id must be linked to the hacker. - */ - .post( - //TODO: authenticate middleware - Middleware.Validator.Hacker.uploadResumeValidator, - Middleware.parseBody.middleware, - //verify that the hacker entity contains the account id - Middleware.Hacker.ensureAccountLinkedToHacker, - //load resume into memory - Middleware.Util.Multer.single("resume"), - //upload resume to storage and update hacker profile - Middleware.Hacker.uploadResume, - //controller response - Controllers.Hacker.uploadedResume - ) - /** - * @api {get} /hacker/resume:id get the resume for a hacker. - * @apiName getHackerResume - * @apiGroup Hacker - * @apiVersion 0.0.8 - * - * @apiParam (param) {ObjectId} id Hacker id - * - * @apiSuccess {String} message Success message - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * message: "Downloaded resume", - * data: { - * id: "507f191e810c19729de860ea", - * resume: [Buffer] - * } - * } - * @apiError {String} message "Resume does not exist" - * @apiErrorExample {json} Error-Response: - * HTTP/1.1 404 - * { - * message: "Resume not found", - * data: {} - * } - * @apiSampleRequest off - * @apiPermission Must be logged in, and the account id must be linked to the hacker. - */ - .get( - //TODO: authenticate middleware - Middleware.Validator.Hacker.downloadResumeValidator, - Middleware.parseBody.middleware, - Middleware.Hacker.downloadResume, - Controllers.Hacker.downloadedResume - ); - - /** - * @api {patch} /hacker/confirmation/:id - * Allows confirmation of hacker attendence if they are accepted. Also allows change from 'confirmed' to 'cancelled'. - * @apiName patchHackerConfirmed - * @apiGroup Hacker - * @apiVersion 0.0.9 - * - * @apiParam (body) {string} [status] The new status of the hacker. "Accepted", "Confirmed", or "Cancelled" - * @apiSuccess {string} message Success message - * @apiSuccess {object} data Hacker object - * @apiSuccessExample {object} Success-Response: - * { - * "message": "Changed hacker information", - * "data": { - * "status": "Confirmed" - * } - * } - * @apiPermission Administrator - * @apiPermission Hacker - */ - hackerRouter.route("/confirmation/:id").patch( - Middleware.Validator.RouteParam.idValidator, - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - - Middleware.Validator.Hacker.updateConfirmationValidator, - Middleware.parseBody.middleware, - Middleware.Hacker.parsePatch, - - Middleware.Hacker.checkStatus([ - CONSTANTS.HACKER_STATUS_ACCEPTED, - CONSTANTS.HACKER_STATUS_CONFIRMED, - CONSTANTS.HACKER_STATUS_CANCELLED - ]), - - Middleware.Hacker.parseConfirmation, - Middleware.Hacker.updateHacker, - - Middleware.Hacker.sendStatusUpdateEmail, - Controllers.Hacker.updatedHacker - ); - - /** - * @api {post} /hacker/email/weekOf/:id - * @apiDescription Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in. - * @apiName postHackerSendWeekOfEmail - * @apiGroup Hacker - * @apiVersion 0.0.9 - * - * @apiParam (param) {string} [status] The hacker ID - * @apiSuccess {string} message Success message - * @apiSuccess {object} data empty - * @apiSuccessExample {object} Success-Response: - * { - * "message": "Hacker week-of email sent.", - * "data": {} - * } - * @apiPermission Administrator - */ - hackerRouter.route("/email/weekOf/:id").post( - Middleware.Validator.RouteParam.idValidator, - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - - Middleware.parseBody.middleware, - Middleware.Hacker.findById, - Middleware.Hacker.checkStatus([ - CONSTANTS.HACKER_STATUS_CONFIRMED, - CONSTANTS.HACKER_STATUS_CHECKED_IN - ]), - Middleware.Hacker.sendWeekOfEmail, - Controllers.Hacker.sentWeekOfEmail - ); - - /** - * @api {post} /hacker/email/weekOf/:id - * @apiDescription Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in. - * @apiName postHackerSendWeekOfEmail - * @apiGroup Hacker - * @apiVersion 0.0.9 - * - * @apiParam (param) {string} [status] The hacker ID - * @apiSuccess {string} message Success message - * @apiSuccess {object} data empty - * @apiSuccessExample {object} Success-Response: - * { - * "message": "Hacker week-of email sent.", - * "data": {} - * } - * @apiPermission Administrator - */ - hackerRouter.route("/email/dayOf/:id").post( - Middleware.Validator.RouteParam.idValidator, - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - - Middleware.parseBody.middleware, - Middleware.Hacker.findById, - Middleware.Hacker.checkStatus([CONSTANTS.HACKER_STATUS_CHECKED_IN]), - Middleware.Hacker.sendDayOfEmail, - Controllers.Hacker.sentDayOfEmail - ); - - apiRouter.use("/hacker", hackerRouter); - } + hackerRouter.route("/email/:email").get( + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Account.findByEmail]), + + Middleware.Validator.RouteParam.emailValidator, + Middleware.parseBody.middleware, + + Middleware.Hacker.findByEmail, + Controllers.Hacker.showHacker + ); + + hackerRouter + .route("/resume/:id") + /** + * @api {post} /hacker/resume/:id upload or update resume for a hacker. + * @apiName postHackerResume + * @apiGroup Hacker + * @apiVersion 0.0.8 + * @apiDescription NOTE: This must be sent via multipart/form-data POST request + * + * @apiParam (param) {ObjectId} id Hacker id + * @apiParam (body) {File} resume The uploaded file. + * + * @apiSuccess {String} message Success message + * @apiSuccess {Object} data Location in the bucket that the file was stored. + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * { + * message: "Uploaded resume", + * data: { + * filename: "resumes/1535032624768-507f191e810c19729de860ea" + * } + * } + * + * @apiPermission Must be logged in, and the account id must be linked to the hacker. + */ + .post( + //TODO: authenticate middleware + Middleware.Validator.Hacker.uploadResumeValidator, + Middleware.parseBody.middleware, + //verify that the hacker entity contains the account id + Middleware.Hacker.ensureAccountLinkedToHacker, + //load resume into memory + Middleware.Util.Multer.single("resume"), + //upload resume to storage and update hacker profile + Middleware.Hacker.uploadResume, + //controller response + Controllers.Hacker.uploadedResume + ) + /** + * @api {get} /hacker/resume:id get the resume for a hacker. + * @apiName getHackerResume + * @apiGroup Hacker + * @apiVersion 0.0.8 + * + * @apiParam (param) {ObjectId} id Hacker id + * + * @apiSuccess {String} message Success message + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * { + * message: "Downloaded resume", + * data: { + * id: "507f191e810c19729de860ea", + * resume: [Buffer] + * } + * } + * @apiError {String} message "Resume does not exist" + * @apiErrorExample {json} Error-Response: + * HTTP/1.1 404 + * { + * message: "Resume not found", + * data: {} + * } + * @apiSampleRequest off + * @apiPermission Must be logged in, and the account id must be linked to the hacker. + */ + .get( + //TODO: authenticate middleware + Middleware.Validator.Hacker.downloadResumeValidator, + Middleware.parseBody.middleware, + Middleware.Hacker.downloadResume, + Controllers.Hacker.downloadedResume + ); + + /** + * @api {patch} /hacker/confirmation/:id + * Allows confirmation of hacker attendence if they are accepted. Also allows change from 'confirmed' to 'cancelled'. + * @apiName patchHackerConfirmed + * @apiGroup Hacker + * @apiVersion 0.0.9 + * + * @apiParam (body) {string} [status] The new status of the hacker. "Accepted", "Confirmed", or "Cancelled" + * @apiSuccess {string} message Success message + * @apiSuccess {object} data Hacker object + * @apiSuccessExample {object} Success-Response: + * { + * "message": "Changed hacker information", + * "data": { + * "status": "Confirmed" + * } + * } + * @apiPermission Administrator + * @apiPermission Hacker + */ + hackerRouter.route("/confirmation/:id").patch( + Middleware.Validator.RouteParam.idValidator, + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + + Middleware.Validator.Hacker.updateConfirmationValidator, + Middleware.parseBody.middleware, + Middleware.Hacker.parsePatch, + + Middleware.Hacker.checkStatus([ + CONSTANTS.HACKER_STATUS_ACCEPTED, + CONSTANTS.HACKER_STATUS_CONFIRMED, + CONSTANTS.HACKER_STATUS_CANCELLED + ]), + + Middleware.Hacker.parseConfirmation, + Middleware.Hacker.updateHacker, + + Middleware.Hacker.sendStatusUpdateEmail, + Controllers.Hacker.updatedHacker + ); + + /** + * @api {post} /hacker/email/weekOf/:id + * @apiDescription Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in. + * @apiName postHackerSendWeekOfEmail + * @apiGroup Hacker + * @apiVersion 0.0.9 + * + * @apiParam (param) {string} [status] The hacker ID + * @apiSuccess {string} message Success message + * @apiSuccess {object} data empty + * @apiSuccessExample {object} Success-Response: + * { + * "message": "Hacker week-of email sent.", + * "data": {} + * } + * @apiPermission Administrator + */ + hackerRouter.route("/email/weekOf/:id").post( + Middleware.Validator.RouteParam.idValidator, + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + + Middleware.parseBody.middleware, + Middleware.Hacker.findById, + Middleware.Hacker.checkStatus([ + CONSTANTS.HACKER_STATUS_CONFIRMED, + CONSTANTS.HACKER_STATUS_CHECKED_IN + ]), + Middleware.Hacker.sendWeekOfEmail, + Controllers.Hacker.sentWeekOfEmail + ); + + /** + * @api {post} /hacker/email/weekOf/:id + * @apiDescription Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in. + * @apiName postHackerSendWeekOfEmail + * @apiGroup Hacker + * @apiVersion 0.0.9 + * + * @apiParam (param) {string} [status] The hacker ID + * @apiSuccess {string} message Success message + * @apiSuccess {object} data empty + * @apiSuccessExample {object} Success-Response: + * { + * "message": "Hacker week-of email sent.", + * "data": {} + * } + * @apiPermission Administrator + */ + hackerRouter.route("/email/dayOf/:id").post( + Middleware.Validator.RouteParam.idValidator, + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + + Middleware.parseBody.middleware, + Middleware.Hacker.findById, + Middleware.Hacker.checkStatus([CONSTANTS.HACKER_STATUS_CHECKED_IN]), + Middleware.Hacker.sendDayOfEmail, + Controllers.Hacker.sentDayOfEmail + ); + + apiRouter.use("/hacker", hackerRouter); + } }; diff --git a/services/hacker.service.js b/services/hacker.service.js index 14b33fbb..355a579e 100644 --- a/services/hacker.service.js +++ b/services/hacker.service.js @@ -10,7 +10,7 @@ const QRCode = require("qrcode"); /** * @function createHacker - * @param {{_id: ObjectId, accountId: ObjectId, school: string, gender: string, needsBus: boolean, application: {Object}}} hackerDetails + * @param {{_id: ObjectId, accountId: ObjectId, application: {Object}}} hackerDetails * @return {Promise} The promise will resolve to a hacker object if save is successful. * @description Adds a new hacker to database. */ @@ -25,7 +25,7 @@ function createHacker(hackerDetails) { /** * @function updateOne * @param {ObjectId} id - * @param {{_id?: ObjectId, accountId?: ObjectId, school?: string, gender?: string, needsBus?: boolean, application?: {Object}, teamId?: ObjectId}} hackerDetails + * @param {{_id?: ObjectId, accountId?: ObjectId, application?: {Object}, teamId?: ObjectId}} hackerDetails * @return {DocumentQuery} The document query will resolve to hacker or null. * @description Update an account specified by its mongoId with information specified by hackerDetails. */ diff --git a/services/storage.service.js b/services/storage.service.js index 027c44cb..c7614239 100644 --- a/services/storage.service.js +++ b/services/storage.service.js @@ -1,87 +1,88 @@ "use strict"; // Imports the Google Cloud client library -const GStorage = require('@google-cloud/storage'); +const GStorage = require("@google-cloud/storage"); const Logger = require("./logger.service"); class StorageService { - constructor() { - this.bucketName = process.env.BUCKET_NAME; - try { - this.storage = new GStorage.Storage(); - } catch (error) { - Logger.error(error); - } - this.bucket = this.storage.bucket(this.bucketName); + constructor() { + this.bucketName = process.env.BUCKET_NAME; + try { + this.storage = new GStorage.Storage(); + } catch (error) { + Logger.error(error); } + this.bucket = this.storage.bucket(this.bucketName); + } - /** - * Upload a file to storage. - * @param {{mimetype:string,buffer:Buffer}} file Multer file object - * @param {string} gcfilename the location in the bucket that you want the file stored. - * @returns {Promise} the address of the file that was uploaded - */ - upload(file, gcfilename) { - const blob = this.bucket.file(gcfilename); - const blobStream = blob.createWriteStream({ - metadata: { - contentType: file.mimetype - }, - resumable: false - }); - const _this = this; - return new Promise(function (resolve, reject) { - blobStream.on("finish", () => { - resolve(_this.getPublicUrl(gcfilename)); - }); - blobStream.on("error", reject); - //write the file data into the stream, and end it. - blobStream.end(file.buffer); - }); - } - /** - * Download file from storage. - * @param {string} filename path to file in bucket - * @returns {Promise<[Buffer]>} the file data that was returned - */ - download(filename) { - const file = this.bucket.file(filename); - return new Promise((resolve, reject) => { - file.exists().then((doesExist) => { - if (doesExist) { - file.download() - .then(resolve) - .catch(reject); - } else { - reject("file does not exist"); - } - }); - }); - } - /** - * Delete a file - * @param {*} filename the file that you want to delete - * @returns {Promise<[ApiResponse]>} - */ - delete(filename) { - const file = this.bucket.file(filename); - return file.delete(); - } + /** + * Upload a file to storage. + * @param {{mimetype:string,buffer:Buffer}} file Multer file object + * @param {string} gcfilename the location in the bucket that you want the file stored. + * @returns {Promise} the address of the file that was uploaded + */ + upload(file, gcfilename) { + const blob = this.bucket.file(gcfilename); + const blobStream = blob.createWriteStream({ + metadata: { + contentType: file.mimetype + }, + resumable: false + }); + const _this = this; + return new Promise(function(resolve, reject) { + blobStream.on("finish", () => { + resolve(_this.getPublicUrl(gcfilename)); + }); + blobStream.on("error", reject); + //write the file data into the stream, and end it. + blobStream.end(file.buffer); + }); + } + /** + * Download file from storage. + * @param {string} filename path to file in bucket + * @returns {Promise<[Buffer]>} the file data that was returned + */ + download(filename) { + const file = this.bucket.file(filename); + return new Promise((resolve, reject) => { + file.exists().then(doesExist => { + if (doesExist) { + file + .download() + .then(resolve) + .catch(reject); + } else { + reject("file does not exist"); + } + }); + }); + } + /** + * Delete a file + * @param {*} filename the file that you want to delete + * @returns {Promise<[ApiResponse]>} + */ + delete(filename) { + const file = this.bucket.file(filename); + return file.delete(); + } - /** - * - * @param {*} filename the file that you want to check exists - * @returns {Promise<[Boolean]>} - */ - exists(filename) { - const file = this.bucket.file(filename); - return file.exists(); - } - /** - * Get the public URL of the file - * @param {string} filename the path of the file - */ - getPublicUrl(filename) { - return `https://storage.googleapis.com/${this.bucket.name}/${filename}`; - } + /** + * + * @param {*} filename the file that you want to check exists + * @returns {Promise<[Boolean]>} + */ + exists(filename) { + const file = this.bucket.file(filename); + return file.exists(); + } + /** + * Get the public URL of the file + * @param {string} filename the path of the file + */ + getPublicUrl(filename) { + return `https://storage.googleapis.com/${this.bucket.name}/${filename}`; + } } -module.exports = new StorageService(); \ No newline at end of file +module.exports = new StorageService(); From 2cf5f06d1711ac20a9879deabc89d4772a9d30ab Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Sun, 1 Dec 2019 11:43:52 -0500 Subject: [PATCH 04/28] Fixed all tests --- docs/api/api_data.js | 6378 ++++++++++++++++-------------- docs/api/api_data.json | 2047 ++++++---- middlewares/auth.middleware.js | 722 ++-- middlewares/hacker.middleware.js | 5 +- models/hacker.model.js | 3 +- package-lock.json | 2 +- package.json | 100 +- routes/api/hacker.js | 256 +- scripts/batch_scripts.py | 12 +- services/hacker.service.js | 47 +- tests/account.test.js | 1084 ++--- tests/hacker.test.js | 1878 ++++----- tests/search.service.spec.js | 486 +-- tests/util/hacker.test.util.js | 843 ++-- 14 files changed, 7581 insertions(+), 6282 deletions(-) diff --git a/docs/api/api_data.js b/docs/api/api_data.js index 3bd69968..a0b78b6b 100644 --- a/docs/api/api_data.js +++ b/docs/api/api_data.js @@ -1,3086 +1,3530 @@ define({ - "api": [{ - "type": "post", - "url": "/account/", - "title": "create a new account", - "name": "create", - "group": "Account", - "version": "0.0.8", - "parameter": { - "fields": { - "body": [{ - "group": "body", - "type": "String", - "optional": false, - "field": "firstName", - "description": "

First name of the account creator.

" - }, - { - "group": "body", - "type": "String", - "optional": false, - "field": "lastName", - "description": "

Last name of the account creator.

" - }, - { - "group": "body", - "type": "String", - "optional": false, - "field": "pronoun", - "description": "

the pronoun of the account creator.

" - }, - { - "group": "body", - "type": "String", - "optional": false, - "field": "email", - "description": "

Email of the account.

" - }, - { - "group": "body", - "type": "String[]", - "optional": false, - "field": "dietaryRestrictions", - "description": "

Any dietary restrictions for the user. 'None' if there are no restrictions

" - }, - { - "group": "body", - "type": "String", - "optional": false, - "field": "shirtSize", - "description": "

Size of the shirt that the user will receive.

" - }, - { - "group": "body", - "type": "String", - "optional": false, - "field": "password", - "description": "

The password of the account.

" - }, - { - "group": "body", - "type": "String", - "optional": false, - "field": "birthDate", - "description": "

a Date parsable string.

" - }, - { - "group": "body", - "type": "Number", - "optional": false, - "field": "phoneNumber", - "description": "

the user's phone number, represented as a string.

" - } - ], - "header": [{ - "group": "header", - "type": "JWT", - "optional": true, - "field": "token", - "description": "

the user's invite token.

" - }] - }, - "examples": [{ - "title": "Request-Example:", - "content": "{ \n \"firstName\": \"Theo\",\n \"lastName\":\"Klein\",\n \"pronoun\":\"he/him\",\n \"email\":\"theo@klein.com\",\n \"password\":\"hunter2\",\n \"dietaryRestrictions\":[\"Halal\"],\n \"phoneNumber\":1234567890,\n \"shirtSize\":\"S\",\n \"birthDate\":\"10/30/1997\"\n}", - "type": "json" - }] - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Account object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Account creation successful\", \n \"data\": {\n \"id\": ObjectId(\"5bff8b9f3274cf001bc71048\"),\n \t\"firstName\": \"Theo\",\n \"lastName\":\"Klein\",\n \"pronoun\":\"he/him\",\n \"email\":\"theo@klein.com\",\n \"dietaryRestrictions\":[\"Halal\"],\n \"phoneNumber\":1234567890,\n \t\"shirtSize\":\"S\",\n \"birthDate\":Date(\"10/30/1997\")\n }\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "string", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\n \"message\": \"Account already exists\", \n \"data\": {\n \"route\": \"/\"\n }\n}", - "type": "object" - }] - }, - "filename": "routes/api/account.js", - "groupTitle": "Account", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/account/" - }] - }, - { - "type": "get", - "url": "/account/:id", - "title": "gets information from an account with mongoid ':id'", - "name": "getAccount", - "group": "Account", - "version": "0.0.8", - "parameter": { - "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "id", - "description": "

MongoId of an account

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Account object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Account found by user id\", \n \"data\": {\n \"id\": ObjectId(\"5bff8b9f3274cf001bc71048\"),\n \"firstName\": \"Theo\",\n \"lastName\":\"Klein\",\n \"pronoun\":\"he/him\",\n \"email\":\"theo@klein.com\",\n \"dietaryRestrictions\":[\"Halal\"],\n \"phoneNumber\":1234567890,\n \"shirtSize\":\"S\",\n \"birthDate\":Date(\"10/30/1997\")\n }\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "string", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Account not found\", \"data\": {}}", - "type": "object" - }] - }, - "filename": "routes/api/account.js", - "groupTitle": "Account", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/account/:id" - }] - }, - { - "type": "get", - "url": "/account/invite", - "title": "Get all of the invites.", - "name": "getAllInvites", - "group": "Account", - "version": "0.0.8", - "description": "

Get all of the invites that currently exist in the database.

", - "success": { - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Invite retrieval successful.\", \n \"data\": [{\n \"email\":\"abc@def.com\",\n \"accountType\":\"Hacker\"\n }]\n }", - "type": "object" - }] - }, - "filename": "routes/api/account.js", - "groupTitle": "Account", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/account/invite" - }] - }, - { - "type": "post", - "url": "/account/invite", - "title": "invites a user to create an account with the specified accountType", - "name": "inviteAccount", - "group": "Account", - "version": "0.0.8", - "description": "

sends link with token to be used with the account/create route

", - "parameter": { - "fields": { - "body": [{ - "group": "body", - "type": "String", - "optional": true, - "field": "email", - "description": "

email of the account to be created and where to send the link

" - }, - { - "group": "body", - "type": "String", - "optional": true, - "field": "accountType", - "description": "

the type of the account which the user can create, for sponsor this should specify tier as well

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Account object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully invited user\", \n \"data\": {}\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "string", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "object", - "optional": false, - "field": "data", - "description": "

Error object

" - } - ] - }, - "examples": [{ - "title": "Error-Response:", - "content": "{\n \"message\": \"Invalid Authentication\",\n \"data\": {\n \"route\": \"/invite\"\n }\n }", - "type": "object" - }] - }, - "filename": "routes/api/account.js", - "groupTitle": "Account", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/account/invite" - }] - }, - { - "type": "get", - "url": "/account/self", - "title": "get information about own account", - "name": "self", - "group": "Account", - "version": "0.0.8", - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Account object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Account found by user email\", \n \"data\": {\n \t\"id\": ObjectId(\"5bff8b9f3274cf001bc71048\"),\n \t\"firstName\": \"Theo\",\n \"lastName\":\"Klein\",\n \"pronoun\":\"he/him\",\n \"email\":\"theo@klein.com\",\n \"dietaryRestrictions\":[\"Halal\"],\n \"phoneNumber\":1234567890,\n \t\"shirtSize\":\"S\",\n \"birthDate\":Date(\"10/30/1997\")\n }\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "string", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty object

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Account not found\", \"data\": {}}", - "type": "object" - }] - }, - "filename": "routes/api/account.js", - "groupTitle": "Account", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/account/self" - }] - }, + api: [ { - "type": "patch", - "url": "/account/:id", - "title": "update an account's information", - "name": "updateOneUser", - "group": "Account", - "version": "0.0.8", - "parameter": { - "fields": { - "body": [{ - "group": "body", - "type": "String", - "optional": true, - "field": "firstName", - "description": "

First name of the account creator.

" + type: "post", + url: "/account/", + title: "create a new account", + name: "create", + group: "Account", + version: "0.0.8", + parameter: { + fields: { + body: [ + { + group: "body", + type: "String", + optional: false, + field: "firstName", + description: "

First name of the account creator.

" + }, + { + group: "body", + type: "String", + optional: false, + field: "lastName", + description: "

Last name of the account creator.

" + }, + { + group: "body", + type: "String", + optional: false, + field: "pronoun", + description: "

the pronoun of the account creator.

" }, { - "group": "body", - "type": "String", - "optional": true, - "field": "lastName", - "description": "

Last name of the account creator.

" + group: "body", + type: "String", + optional: false, + field: "email", + description: "

Email of the account.

" }, { - "group": "body", - "type": "String", - "optional": true, - "field": "pronoun", - "description": "

the pronoun of the account creator.

" - }, - { - "group": "body", - "type": "String", - "optional": true, - "field": "email", - "description": "

Email of the account.

" - }, - { - "group": "body", - "type": "String[]", - "optional": true, - "field": "dietaryRestrictions", - "description": "

Any dietary restrictions for the user. 'None' if there are no restrictions

" - }, - { - "group": "body", - "type": "String", - "optional": true, - "field": "shirtSize", - "description": "

Size of the shirt that the user will receive.

" - }, - { - "group": "body", - "type": "String", - "optional": true, - "field": "birthDate", - "description": "

a Date parsable string.

" - }, - { - "group": "body", - "type": "Number", - "optional": true, - "field": "phoneNumber", - "description": "

the user's phone number, represented as a string.

" - } - ] - }, - "examples": [{ - "title": "Request-Example:", - "content": "{ \"shirtSize\": \"M\" }", - "type": "json" - }] - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Account object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Changed account information\", \n \"data\": {\n \"id\": ObjectId(\"5bff8b9f3274cf001bc71048\"),\n \t\"firstName\": \"Theo\",\n \"lastName\":\"Klein\",\n \"pronoun\":\"he/him\",\n \"email\":\"theo@klein.com\",\n \"dietaryRestrictions\":[\"Halal\"],\n \"phoneNumber\":1234567890,\n \t\"shirtSize\":\"M\",\n \"birthDate\":Date(\"10/30/1997\")\n }\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "string", - "optional": false, - "field": "message", - "description": "

Error message

" + group: "body", + type: "String[]", + optional: false, + field: "dietaryRestrictions", + description: + "

Any dietary restrictions for the user. 'None' if there are no restrictions

" }, { - "group": "Error 4xx", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" + group: "body", + type: "String", + optional: false, + field: "shirtSize", + description: + "

Size of the shirt that the user will receive.

" + }, + { + group: "body", + type: "String", + optional: false, + field: "password", + description: "

The password of the account.

" + }, + { + group: "body", + type: "String", + optional: false, + field: "birthDate", + description: "

a Date parsable string.

" + }, + { + group: "body", + type: "Number", + optional: false, + field: "phoneNumber", + description: + "

the user's phone number, represented as a string.

" + } + ], + header: [ + { + group: "header", + type: "JWT", + optional: true, + field: "token", + description: "

the user's invite token.

" } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while updating account\", \"data\": {}}", - "type": "object" - }] + examples: [ + { + title: "Request-Example:", + content: + '{ \n "firstName": "Theo",\n "lastName":"Klein",\n "pronoun":"he/him",\n "email":"theo@klein.com",\n "password":"hunter2",\n "dietaryRestrictions":["Halal"],\n "phoneNumber":1234567890,\n "shirtSize":"S",\n "birthDate":"10/30/1997"\n}', + type: "json" + } + ] }, - "filename": "routes/api/account.js", - "groupTitle": "Account", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/account/:id" - }] - }, - { - "type": "patch", - "url": "/auth/password/change", - "title": "change password for logged in user", - "name": "changePassword", - "group": "Authentication", - "version": "0.0.8", - "parameter": { - "fields": { - "Parameter": [{ - "group": "Parameter", - "type": "String", - "optional": false, - "field": "oldPassword", - "description": "

The current password of the user

" - }, - { - "group": "Parameter", - "type": "String", - "optional": false, - "field": "newPassword", - "description": "

The new password of the user

" - } - ] - }, - "examples": [{ - "title": "Request-Example:", - "content": "{ \n \"oldPassword\": \"password12345\",\n \"newPassword\": \"password123456\"\n}", - "type": "json" - }] - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\"message\": \"Successfully reset password\", \"data\": {}}", - "type": "json" - }] - }, - "permission": [{ - "name": ": Must be logged in" - }], - "filename": "routes/api/auth.js", - "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/password/change" - }] - }, - { - "type": "post", - "url": "/auth/confirm/:token", - "title": "confirm account using the JWT in :token", - "name": "confirmAccount", - "group": "Authentication", - "version": "0.0.8", - "parameter": { - "fields": { - "Parameter": [{ - "group": "Parameter", - "type": "String", - "optional": false, - "field": "JWT", - "description": "

for confirming the account

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Success-Response:", - "content": "{\"message\": \"Successfully confirmed account\", \"data\": {}}", - "type": "json" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "string", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Invalid token for confirming account, \"data\": {}}", - "type": "object" - }] - }, - "filename": "routes/api/auth.js", - "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/confirm/:token" - }] - }, - { - "type": "post", - "url": "/auth/password/forgot", - "title": "forgot password route", - "name": "forgotPassword", - "group": "Authentication", - "version": "0.0.8", - "parameter": { - "fields": { - "Parameter": [{ - "group": "Parameter", - "type": "String", - "optional": false, - "field": "email", - "description": "

the email address of the account

" - }] - }, - "examples": [{ - "title": "Request-Example:", - "content": "{ \"email\": \"myemail@mchacks.ca\" }", - "type": "json" - }] - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\"message\": \"Sent reset email\", \"data\": {}}", - "type": "json" - }] - }, - "permission": [{ - "name": ": public" - }], - "filename": "routes/api/auth.js", - "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/password/forgot" - }] - }, - { - "type": "get", - "url": "/auth/rolebindings/:id", - "title": "retrieve rolebindings for a user given by their user id :id", - "name": "getRoleBindings", - "group": "Authentication", - "version": "0.0.8", - "parameter": { - "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "id", - "description": "

MongoId of an account

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Rolebindings object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully retrieved role bindings\",\n \"data\": {\n accountId:\"5beca4ab2e069a34f91697b2\"\n id:\"5beca4ae2e069a34f91698b1\"\n roles: [\n {\n _id:\"5beca4ab2e069a34f91697d9\",\n name:\"hacker\",\n routes: [\n {_id: \"5beca4ae2e069a34f9169852\", requestType: \"POST\", uri: \"/api/auth/login\"},\n {_id: \"5beca4ae2e069a34f9169851\", requestType: \"POST\", uri: \"/api/auth/logout\"},\n {_id: \"5beca4ae2e069a34f9169850\", requestType: \"GET\", uri: \"/api/auth/rolebindings/:self\"},\n {_id: \"5beca4ae2e069a34f916984f\", requestType: \"GET\", uri: \"/api/account/self\"},\n {_id: \"5beca4ae2e069a34f916984e\", requestType: \"GET\", uri: \"/api/account/:self\"},\n {_id: \"5beca4ae2e069a34f916984d\", requestType: \"PATCH\", uri: \"/api/account/:self\"},\n {_id: \"5beca4ae2e069a34f916984c\", requestType: \"POST\", uri: \"/api/hacker/\"},\n {_id: \"5beca4ae2e069a34f916984b\", requestType: \"GET\", uri: \"/api/hacker/:self\"},\n {_id: \"5beca4ae2e069a34f916984a\", requestType: \"GET\", uri: \"/api/hacker/:self/resume\"},\n {_id: \"5beca4ae2e069a34f9169849\", requestType: \"PATCH\", uri: \"/api/hacker/:self\"}\n ]\n }\n ]\n }\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "string", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Role Bindings not found\", \"data\": {}}", - "type": "object" - }] - }, - "filename": "routes/api/auth.js", - "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/rolebindings/:id" - }] - }, - { - "type": "get", - "url": "/auth/roles", - "title": "get roles", - "name": "getRoles", - "description": "

get all roles that exist in the database

", - "group": "Authentication", - "version": "0.0.8", - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Success-Response:", - "content": "{\"message\": \"Sucessfully retrieved all roles\", \"data\":\n[{name: \"GodStaff\", routes: Array(27), id: \"5bee20ef3ca9dd4754382880\"},\n {name: \"Hacker\", routes: Array(10), id: \"5bee20ef3ca9dd4754382881\"},\n {name: \"Volunteer\", routes: Array(4), id: \"5bee20ef3ca9dd4754382882\"}]", - "type": "json" - }] - }, - "filename": "routes/api/auth.js", - "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/roles" - }] - }, - { - "type": "post", - "url": "/auth/login", - "title": "login to the service", - "name": "login", - "group": "Authentication", - "version": "0.0.8", - "parameter": { - "fields": { - "Parameter": [{ - "group": "Parameter", - "type": "string", - "optional": false, - "field": "email", - "description": "

Account email

" - }, - { - "group": "Parameter", - "type": "string", - "optional": false, - "field": "password", - "description": "

Account password

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\"message\": \"Successfully logged in\", \"data\": {}}", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "string", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Invalid Authentication\", \"data\": {}}", - "type": "object" - }] - }, - "permission": [{ - "name": ": public" - }], - "filename": "routes/api/auth.js", - "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/login" - }] - }, - { - "type": "get", - "url": "/auth/logout", - "title": "logout of service", - "name": "logout", - "group": "Authentication", - "version": "0.0.8", - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\"message\": \"Successfully logged out\", \"data\": {}}", - "type": "object" - }] - }, - "permission": [{ - "name": ": public" - }], - "filename": "routes/api/auth.js", - "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/logout" - }] - }, - { - "type": "get", - "url": "/auth/confirm/resend", - "title": "resend confirmation token", - "name": "resendConfirmAccount", - "group": "Authentication", - "version": "0.0.8", - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Success-Response:", - "content": "{\"message\": \"Successfully resent confirmation email\", \"data\": {}}", - "type": "json" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "string", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response:", - "content": " HTTP/1.1 422\n{\"message\": \"Account already confirmed\", \"data\": {}}", - "type": "json" - }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Account object

" + } + ] + }, + examples: [ { - "title": "Error-Response:", - "content": " HTTP/1.1 428\n{\"message\": \"Account confirmation token does not exist\", \"data\": {}}", - "type": "json" + title: "Success-Response: ", + content: + '{\n "message": "Account creation successful", \n "data": {\n "id": ObjectId("5bff8b9f3274cf001bc71048"),\n \t"firstName": "Theo",\n "lastName":"Klein",\n "pronoun":"he/him",\n "email":"theo@klein.com",\n "dietaryRestrictions":["Halal"],\n "phoneNumber":1234567890,\n \t"shirtSize":"S",\n "birthDate":Date("10/30/1997")\n }\n }', + type: "object" } ] }, - "filename": "routes/api/auth.js", - "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/confirm/resend" - }] - }, - { - "type": "post", - "url": "/auth/password/reset", - "title": "reset password", - "name": "resetPassword", - "group": "Authentication", - "version": "0.0.8", - "parameter": { - "fields": { - "Parameter": [{ - "group": "Parameter", - "type": "String", - "optional": false, - "field": "password", - "description": "

the password of the account

" - }] - }, - "examples": [{ - "title": "Request-Example:", - "content": "{ \"password\": \"hunter2\" }", - "type": "json" - }] - }, - "header": { - "fields": { - "Header": [{ - "group": "Header", - "type": "String", - "optional": false, - "field": "Authentication", - "description": "

the token that was provided in the reset password email

" - }] - }, - "examples": [{ - "title": "Header-Example:", - "content": "{\n \"X-Reset-Token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c\"\n}", - "type": "json" - }] - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\"message\": \"Successfully reset password\", \"data\": {}}", - "type": "json" - }] - }, - "permission": [{ - "name": ": must have authentication token" - }], - "filename": "routes/api/auth.js", - "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/password/reset" - }] - }, - { - "type": "patch", - "url": "/hacker/checkin/:id", - "title": "update a hacker's status to be 'Checked-in'. Note that the Hacker must eitehr be Accepted or Confirmed.", - "name": "checkinHacker", - "group": "Hacker", - "version": "0.0.9", - "parameter": { - "fields": { - "body": [{ - "group": "body", - "type": "string", - "optional": true, - "field": "status", - "description": "

Check-in status. "Checked-in"

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Hacker object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Changed hacker information\", \n \"data\": {\n \"status\": \"Checked-in\"\n }\n}", - "type": "object" - }] - }, - "permission": [{ - "name": "Administrator" + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] }, + examples: [ + { + title: "Error-Response: ", + content: + '{\n "message": "Account already exists", \n "data": {\n "route": "/"\n }\n}', + type: "object" + } + ] + }, + filename: "routes/api/account.js", + groupTitle: "Account", + sampleRequest: [ { - "name": "Volunteer" + url: "https://api.mchacks.ca/api/account/" } - ], - "filename": "routes/api/hacker.js", - "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/checkin/:id" - }] + ] }, { - "type": "post", - "url": "/hacker/", - "title": "create a new hacker", - "name": "createHacker", - "group": "Hacker", - "version": "0.0.8", - "parameter": { - "fields": { - "body": [{ - "group": "body", - "type": "MongoID", - "optional": false, - "field": "accountId", - "description": "

ObjectID of the respective account

" - }, - { - "group": "body", - "type": "String", - "optional": false, - "field": "school", - "description": "

Name of the school the hacker goes to

" - }, + type: "get", + url: "/account/:id", + title: "gets information from an account with mongoid ':id'", + name: "getAccount", + group: "Account", + version: "0.0.8", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "id", + description: "

MongoId of an account

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ { - "group": "body", - "type": "String", - "optional": false, - "field": "gender", - "description": "

Gender of the hacker

" + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" }, { - "group": "body", - "type": "Boolean", - "optional": false, - "field": "needsBus", - "description": "

Whether the hacker requires a bus for transportation

" - }, - { - "group": "body", - "type": "String[]", - "optional": false, - "field": "ethnicity", - "description": "

the ethnicities of the hacker

" - }, - { - "group": "body", - "type": "String[]", - "optional": false, - "field": "major", - "description": "

the major of the hacker

" - }, - { - "group": "body", - "type": "Number", - "optional": false, - "field": "graduationYear", - "description": "

the graduation year of the hacker

" - }, - { - "group": "body", - "type": "Boolean", - "optional": false, - "field": "codeOfConduct", - "description": "

acceptance of the code of conduct

" - }, - { - "group": "body", - "type": "Json", - "optional": false, - "field": "application", - "description": "

The hacker's application. Resume and jobInterest fields are required.

" - } - ] - }, - "examples": [{ - "title": "application: ", - "content": "{\n \"application\":{\n \"portfolioURL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n}", - "type": "Json" - }] - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Hacker object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Hacker creation successful\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"application\":{\n \"portfolioURL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n },\n \"status\":\"Applied\",\n \"ethnicity\":[\"White or Caucasian\",\" Asian or Pacific Islander\"],\n \"accountId\":\"5bff2a35e533b0f6562b4998\",\n \"school\":\"McPherson College\",\n \"gender\":\"Female\",\n \"needsBus\":false,\n \"major\":\"Accounting\",\n \"graduationYear\":2019,\n \"codeOfConduct\":true,\n }\n}", - "type": "object" - }] + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Account object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Account found by user id", \n "data": {\n "id": ObjectId("5bff8b9f3274cf001bc71048"),\n "firstName": "Theo",\n "lastName":"Klein",\n "pronoun":"he/him",\n "email":"theo@klein.com",\n "dietaryRestrictions":["Halal"],\n "phoneNumber":1234567890,\n "shirtSize":"S",\n "birthDate":Date("10/30/1997")\n }\n }', + type: "object" + } + ] }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "string", - "optional": false, - "field": "message", - "description": "

Error message

" + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" }, { - "group": "Error 4xx", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while creating hacker\", \"data\": {}}", - "type": "object" - }] + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Account not found", "data": {}}', + type: "object" + } + ] }, - "filename": "routes/api/hacker.js", - "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/" - }] - }, - { - "type": "get", - "url": "/hacker/email/:email", - "title": "get a hacker's information", - "name": "getHacker", - "group": "Hacker", - "version": "0.0.8", - "parameter": { - "fields": { - "param": [{ - "group": "param", - "type": "String", - "optional": false, - "field": "email", - "description": "

a hacker's unique email

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "String", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "Object", - "optional": false, - "field": "data", - "description": "

Hacker object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully retrieved hacker information\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"application\":{\n \"portfolioURL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n },\n \"status\":\"Applied\",\n \"ethnicity\":[\"White or Caucasian\",\" Asian or Pacific Islander\"],\n \"accountId\":\"5bff2a35e533b0f6562b4998\",\n \"school\":\"McPherson College\",\n \"gender\":\"Female\",\n \"needsBus\":false,\n \"major\":\"Accounting\",\n \"graduationYear\":2019,\n \"codeOfConduct\":true,\n }\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "String", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "Object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Hacker not found\", \"data\": {}}", - "type": "object" - }] - }, - "filename": "routes/api/hacker.js", - "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/email/:email" - }] - }, - { - "type": "get", - "url": "/hacker/:id", - "title": "get a hacker's information", - "name": "getHacker", - "group": "Hacker", - "version": "0.0.8", - "parameter": { - "fields": { - "param": [{ - "group": "param", - "type": "String", - "optional": false, - "field": "id", - "description": "

a hacker's unique mongoID

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "String", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "Object", - "optional": false, - "field": "data", - "description": "

Hacker object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully retrieved hacker information\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"application\":{\n \"portfolioURL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n },\n \"status\":\"Applied\",\n \"ethnicity\":[\"White or Caucasian\",\" Asian or Pacific Islander\"],\n \"accountId\":\"5bff2a35e533b0f6562b4998\",\n \"school\":\"McPherson College\",\n \"gender\":\"Female\",\n \"needsBus\":false,\n \"major\":\"Accounting\",\n \"graduationYear\":2019,\n \"codeOfConduct\":true,\n }\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "String", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "Object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Hacker not found\", \"data\": {}}", - "type": "object" - }] - }, - "filename": "routes/api/hacker.js", - "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/:id" - }] - }, - { - "type": "get", - "url": "/hacker/resume:id", - "title": "get the resume for a hacker.", - "name": "getHackerResume", - "group": "Hacker", - "version": "0.0.8", - "parameter": { - "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "id", - "description": "

Hacker id

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "String", - "optional": false, - "field": "message", - "description": "

Success message

" - }] - }, - "examples": [{ - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK \n{ \n message: \"Downloaded resume\", \n data: { \n id: \"507f191e810c19729de860ea\", \n resume: [Buffer] \n } \n}", - "type": "json" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "String", - "optional": false, - "field": "message", - "description": "

"Resume does not exist"

" - }] - }, - "examples": [{ - "title": "Error-Response:", - "content": "HTTP/1.1 404 \n{ \n message: \"Resume not found\", \n data: {} \n}", - "type": "json" - }] - }, - "permission": [{ - "name": "Must be logged in, and the account id must be linked to the hacker." - }], - "filename": "routes/api/hacker.js", - "groupTitle": "Hacker" + filename: "routes/api/account.js", + groupTitle: "Account", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/account/:id" + } + ] }, { - "type": "get", - "url": "/hacker/stats", - "title": "Gets the stats of all of the hackers who have applied.", - "name": "getHackerStats", - "group": "Hacker", - "version": "0.0.9", - "parameter": { - "fields": { - "query": [{ - "group": "query", - "type": "String", - "optional": false, - "field": "model", - "description": "

the model to be searched (Only hacker supported)

" - }, - { - "group": "query", - "type": "Array", - "optional": false, - "field": "q", - "description": "

the query to be executed. For more information on how to format this, please see https://docs.mchacks.ca/architecture/

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Hacker object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Retrieved stats\",\n \"data\": {\n \"stats\" : {\n \"total\": 10,\n \"status\": { \"Applied\": 10 },\n \"school\": { \"McGill University\": 3, \"Harvard University\": 7 },\n degree: { \"Undergraduate\": 10 },\n gender: { \"Male\": 1, \"Female\": 9 },\n needsBus: { \"true\": 7, \"false\": 3 },\n ethnicity: { \"White\": 10, },\n jobInterest: { \"Internship\": 10 },\n major: { \"Computer Science\": 10 },\n graduationYear: { \"2019\": 10 },\n dietaryRestrictions: { \"None\": 10 },\n shirtSize: { \"M\": 3, \"XL\": 7 },\n age: { \"22\": 10 }\n }\n }\n}", - "type": "object" - }] - }, - "filename": "routes/api/hacker.js", - "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/stats" - }] + type: "get", + url: "/account/invite", + title: "Get all of the invites.", + name: "getAllInvites", + group: "Account", + version: "0.0.8", + description: + "

Get all of the invites that currently exist in the database.

", + success: { + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Invite retrieval successful.", \n "data": [{\n "email":"abc@def.com",\n "accountType":"Hacker"\n }]\n }', + type: "object" + } + ] + }, + filename: "routes/api/account.js", + groupTitle: "Account", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/account/invite" + } + ] }, { - "type": "patch", - "url": "/hacker/:id", - "title": "update a hacker's information.", - "description": "

This route only contains the ability to update a subset of a hacker's information. If you want to update a status, you must have Admin priviledges and use PATCH /hacker/status/:id.

", - "name": "patchHacker", - "group": "Hacker", - "version": "0.0.8", - "parameter": { - "fields": { - "body": [{ - "group": "body", - "type": "String", - "optional": true, - "field": "school", - "description": "

Name of the school the hacker goes to

" - }, - { - "group": "body", - "type": "String", - "optional": true, - "field": "gender", - "description": "

Gender of the hacker

" - }, - { - "group": "body", - "type": "Boolean", - "optional": true, - "field": "needsBus", - "description": "

Whether the hacker requires a bus for transportation

" - }, - { - "group": "body", - "type": "String[]", - "optional": true, - "field": "ethnicity", - "description": "

the ethnicities of the hacker

" - }, - { - "group": "body", - "type": "String[]", - "optional": true, - "field": "major", - "description": "

the major of the hacker

" - }, - { - "group": "body", - "type": "Number", - "optional": true, - "field": "graduationYear", - "description": "

the graduation year of the hacker

" - }, - { - "group": "body", - "type": "Json", - "optional": true, - "field": "application", - "description": "

The hacker's application

" - } - ] - }, - "examples": [{ - "title": "application: ", - "content": "{\n \"portfolioURL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n }", - "type": "Json" - }] - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Hacker object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Changed hacker information\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"application\":{\n \"portfolioURL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n },\n \"status\":\"Applied\",\n \"ethnicity\":[\"White or Caucasian\",\" Asian or Pacific Islander\"],\n \"accountId\":\"5bff2a35e533b0f6562b4998\",\n \"school\":\"McPherson College\",\n \"gender\":\"Female\",\n \"needsBus\":false,\n \"major\":\"Accounting\",\n \"graduationYear\":2019,\n \"codeOfConduct\":true,\n}", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "string", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" + type: "post", + url: "/account/invite", + title: + "invites a user to create an account with the specified accountType", + name: "inviteAccount", + group: "Account", + version: "0.0.8", + description: + "

sends link with token to be used with the account/create route

", + parameter: { + fields: { + body: [ + { + group: "body", + type: "String", + optional: true, + field: "email", + description: + "

email of the account to be created and where to send the link

" + }, + { + group: "body", + type: "String", + optional: true, + field: "accountType", + description: + "

the type of the account which the user can create, for sponsor this should specify tier as well

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Account object

" } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while updating hacker\", \"data\": {}}", - "type": "object" - }] + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Successfully invited user", \n "data": {}\n }', + type: "object" + } + ] }, - "filename": "routes/api/hacker.js", - "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/:id" - }] - }, - { - "type": "patch", - "url": "/hacker/confirmation/:id", - "title": "Allows confirmation of hacker attendence if they are accepted. Also allows change from 'confirmed' to 'cancelled'.", - "name": "patchHackerConfirmed", - "group": "Hacker", - "version": "0.0.9", - "parameter": { - "fields": { - "body": [{ - "group": "body", - "type": "string", - "optional": true, - "field": "status", - "description": "

The new status of the hacker. "Accepted", "Confirmed", or "Cancelled"

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Hacker object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Changed hacker information\", \n \"data\": {\n \"status\": \"Confirmed\"\n }\n}", - "type": "object" - }] - }, - "permission": [{ - "name": "Administrator" + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

Error object

" + } + ] }, + examples: [ + { + title: "Error-Response:", + content: + '{\n "message": "Invalid Authentication",\n "data": {\n "route": "/invite"\n }\n }', + type: "object" + } + ] + }, + filename: "routes/api/account.js", + groupTitle: "Account", + sampleRequest: [ { - "name": "Hacker" + url: "https://api.mchacks.ca/api/account/invite" } - ], - "filename": "routes/api/hacker.js", - "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/confirmation/:id" - }] - }, - { - "type": "patch", - "url": "/hacker/status/:id", - "title": "update a hacker's status", - "name": "patchHackerStatus", - "group": "Hacker", - "version": "0.0.9", - "parameter": { - "fields": { - "body": [{ - "group": "body", - "type": "string", - "optional": true, - "field": "status", - "description": "

Status of the hacker's application ("None"|"Applied"|"Waitlisted"|"Confirmed"|"Cancelled"|"Checked-in")

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Hacker object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Changed hacker information\", \n \"data\": {\n \"status\": \"Accepted\"\n }\n}", - "type": "object" - }] - }, - "permission": [{ - "name": "Administrator" - }], - "filename": "routes/api/hacker.js", - "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/status/:id" - }] - }, - { - "type": "post", - "url": "/hacker/resume/:id", - "title": "upload or update resume for a hacker.", - "name": "postHackerResume", - "group": "Hacker", - "version": "0.0.8", - "description": "

NOTE: This must be sent via multipart/form-data POST request

", - "parameter": { - "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "id", - "description": "

Hacker id

" - }], - "body": [{ - "group": "body", - "type": "File", - "optional": false, - "field": "resume", - "description": "

The uploaded file.

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "String", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "Object", - "optional": false, - "field": "data", - "description": "

Location in the bucket that the file was stored.

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "HTTP/1.1 200 OK\n{\n message: \"Uploaded resume\", \n data: {\n filename: \"resumes/1535032624768-507f191e810c19729de860ea\"\n }\n}", - "type": "json" - }] - }, - "permission": [{ - "name": "Must be logged in, and the account id must be linked to the hacker." - }], - "filename": "routes/api/hacker.js", - "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/resume/:id" - }] - }, - { - "type": "post", - "url": "/hacker/email/weekOf/:id", - "title": "", - "description": "

Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in.

", - "name": "postHackerSendWeekOfEmail", - "group": "Hacker", - "version": "0.0.9", - "parameter": { - "fields": { - "param": [{ - "group": "param", - "type": "string", - "optional": true, - "field": "status", - "description": "

The hacker ID

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Hacker week-of email sent.\", \n \"data\": {}\n}", - "type": "object" - }] - }, - "permission": [{ - "name": "Administrator" - }], - "filename": "routes/api/hacker.js", - "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/email/weekOf/:id" - }] - }, - { - "type": "post", - "url": "/hacker/email/weekOf/:id", - "title": "", - "description": "

Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in.

", - "name": "postHackerSendWeekOfEmail", - "group": "Hacker", - "version": "0.0.9", - "parameter": { - "fields": { - "param": [{ - "group": "param", - "type": "string", - "optional": true, - "field": "status", - "description": "

The hacker ID

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Hacker week-of email sent.\", \n \"data\": {}\n}", - "type": "object" - }] - }, - "permission": [{ - "name": "Administrator" - }], - "filename": "routes/api/hacker.js", - "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/email/weekOf/:id" - }] - }, - { - "type": "get", - "url": "/sponsor/self", - "title": "get information about logged in sponsor", - "name": "self", - "group": "Hacker", - "version": "1.4.1", - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "String", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "Object", - "optional": false, - "field": "data", - "description": "

Sponsor object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully retrieved sponsor information\", \n \"data\": {\n \"id\": \"5bff4d736f86be0a41badb91\",\n \"accountId\": \"5bff4d736f86be0a41badb99\",\n \"tier\": 3,\n \"company\": \"companyName\",\n \"contractURL\": \"https://www.contractHere.com\",\n \"nominees\": [\"5bff4d736f86be0a41badb93\",\"5bff4d736f86be0a41badb94\"]\n }\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "String", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "Object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Sponsor not found\", \"data\": {}}", - "type": "object" - }] - }, - "permission": [{ - "name": ": Sponsor" - }], - "filename": "routes/api/sponsor.js", - "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/sponsor/self" - }] - }, - { - "type": "get", - "url": "/hacker/self", - "title": "get information about own hacker", - "name": "self", - "group": "Hacker", - "version": "0.0.8", - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Hacker object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Hacker found by logged in account id\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"application\":{\n \"portfolioURL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n },\n \"status\":\"Applied\",\n \"ethnicity\":[\"White or Caucasian\",\" Asian or Pacific Islander\"],\n \"accountId\":\"5bff2a35e533b0f6562b4998\",\n \"school\":\"McPherson College\",\n \"gender\":\"Female\",\n \"needsBus\":false,\n \"major\":[\"Accounting\"],\n \"graduationYear\":2019,\n \"codeOfConduct\":true,\n } \n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "string", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Hacker not found\", \"data\": {}}", - "type": "object" - }] - }, - "filename": "routes/api/hacker.js", - "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/self" - }] - }, - { - "type": "get", - "url": "/", - "title": "version", - "version": "0.0.8", - "name": "index", - "group": "Index", - "permission": [{ - "name": "public" - }], - "filename": "routes/index.js", - "groupTitle": "Index", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/" - }] + ] }, { - "type": "post", - "url": "/api/role/", - "title": "create a new role", - "name": "createRole", - "group": "Role", - "version": "1.1.1", - "parameter": { - "fields": { - "body": [{ - "group": "body", - "type": "String", - "optional": false, - "field": "name", - "description": "

Name of the route

" - }, - { - "group": "body", - "type": "Route[]", - "optional": false, - "field": "routes", - "description": "

The routes that this role gives access to

" - } - ] - }, - "examples": [{ - "title": "application: ", - "content": "{\n \"name\": \"routename\",\n \"routes\": [\n {\n uri: \"/api/hacker/\"\n requestType: \"POST\"\n }\n ]\n}", - "type": "Json" - }] - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Role object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Role creation successful\", \n \"data\": {\n \"name\": \"routename\",\n \"routes\": [\n {\n uri: \"/api/hacker/\"\n requestType: \"POST\"\n }\n ]\n }\n}", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "string", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while creating role\", \"data\": {}}", - "type": "object" - }] - }, - "filename": "routes/api/role.js", - "groupTitle": "Role", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/api/role/" - }] + type: "get", + url: "/account/self", + title: "get information about own account", + name: "self", + group: "Account", + version: "0.0.8", + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Account object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Account found by user email", \n "data": {\n \t"id": ObjectId("5bff8b9f3274cf001bc71048"),\n \t"firstName": "Theo",\n "lastName":"Klein",\n "pronoun":"he/him",\n "email":"theo@klein.com",\n "dietaryRestrictions":["Halal"],\n "phoneNumber":1234567890,\n \t"shirtSize":"S",\n "birthDate":Date("10/30/1997")\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty object

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Account not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/account.js", + groupTitle: "Account", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/account/self" + } + ] }, { - "type": "get", - "url": "/search/", - "title": "provide a specific query for any defined model", - "name": "search", - "group": "Search", - "version": "0.0.8", - "parameter": { - "fields": { - "query": [{ - "group": "query", - "type": "String", - "optional": false, - "field": "model", - "description": "

the model to be searched

" + type: "patch", + url: "/account/:id", + title: "update an account's information", + name: "updateOneUser", + group: "Account", + version: "0.0.8", + parameter: { + fields: { + body: [ + { + group: "body", + type: "String", + optional: true, + field: "firstName", + description: "

First name of the account creator.

" + }, + { + group: "body", + type: "String", + optional: true, + field: "lastName", + description: "

Last name of the account creator.

" }, { - "group": "query", - "type": "Array", - "optional": false, - "field": "q", - "description": "

the query to be executed. For more information on how to format this, please see https://docs.mchacks.ca/architecture/

" + group: "body", + type: "String", + optional: true, + field: "pronoun", + description: "

the pronoun of the account creator.

" }, { - "group": "query", - "type": "String", - "optional": false, - "field": "sort", - "description": "

either "asc" or "desc"

" + group: "body", + type: "String", + optional: true, + field: "email", + description: "

Email of the account.

" }, { - "group": "query", - "type": "number", - "optional": false, - "field": "page", - "description": "

the page number that you would like

" + group: "body", + type: "String[]", + optional: true, + field: "dietaryRestrictions", + description: + "

Any dietary restrictions for the user. 'None' if there are no restrictions

" }, { - "group": "query", - "type": "number", - "optional": false, - "field": "limit", - "description": "

the maximum number of results that you would like returned

" + group: "body", + type: "String", + optional: true, + field: "shirtSize", + description: + "

Size of the shirt that the user will receive.

" }, { - "group": "query", - "type": "any", - "optional": false, - "field": "sort_by", - "description": "

any parameter you want to sort the results by

" + group: "body", + type: "String", + optional: true, + field: "birthDate", + description: "

a Date parsable string.

" }, { - "group": "query", - "type": "boolean", - "optional": false, - "field": "expand", - "description": "

whether you want to expand sub documents within the results

" + group: "body", + type: "Number", + optional: true, + field: "phoneNumber", + description: + "

the user's phone number, represented as a string.

" } ] - } + }, + examples: [ + { + title: "Request-Example:", + content: '{ "shirtSize": "M" }', + type: "json" + } + ] }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "String", - "optional": false, - "field": "message", - "description": "

Success message

" + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" }, { - "group": "Success 200", - "type": "Object", - "optional": false, - "field": "data", - "description": "

Results

" + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Account object

" } ] }, - "examples": [{ - "title": "Success-Response:", - "content": "{\n \"message\": \"Successfully executed query, returning all results\",\n \"data\": [\n {...}\n ]\n }", - "type": "object" - }, + examples: [ { - "title": "Success-Response:", - "content": "{\n \"message\": \"No results found.\",\n \"data\": {}\n }", - "type": "object" + title: "Success-Response: ", + content: + '{\n "message": "Changed account information", \n "data": {\n "id": ObjectId("5bff8b9f3274cf001bc71048"),\n \t"firstName": "Theo",\n "lastName":"Klein",\n "pronoun":"he/him",\n "email":"theo@klein.com",\n "dietaryRestrictions":["Halal"],\n "phoneNumber":1234567890,\n \t"shirtSize":"M",\n "birthDate":Date("10/30/1997")\n }\n }', + type: "object" } ] }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "String", - "optional": false, - "field": "message", - "description": "

Error message

" + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" }, { - "group": "Error 4xx", - "type": "Object", - "optional": false, - "field": "data", - "description": "

empty

" + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" } ] }, - "examples": [{ - "title": "Error-Response:", - "content": "{\"message\": \"Validation failed\", \"data\": {}}", - "type": "object" - }] + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Error while updating account", "data": {}}', + type: "object" + } + ] }, - "filename": "routes/api/search.js", - "groupTitle": "Search", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/search/" - }] - }, - { - "type": "get", - "url": "/settings/", - "title": "Get the settings for the current hackathon", - "name": "getSettings", - "group": "Settings", - "version": "1.1.1", - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Settings Object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Settings creation successful.\", \n \"data\": {\n \"settings\": {\n openTime: \"Wed Feb 06 2019 00:00:00 GMT-0500 (GMT-05:00)\",\n closeTime: \"Sat Feb 01 2020 00:00:00 GMT-0500 (GMT-05:00)\",\n confirmTime: \"Sat Feb 20 2020 00:00:00 GMT-0500 (GMT-05:00)\"\n }\n }\n}", - "type": "object" - }] - }, - "permission": [{ - "name": "public" - }], - "filename": "routes/api/settings.js", - "groupTitle": "Settings", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/settings/" - }] - }, - { - "type": "patch", - "url": "/settings/", - "title": "Patch the settings for the current hackathon", - "name": "patchSettings", - "group": "Settings", - "version": "1.1.1", - "parameter": { - "fields": { - "body": [{ - "group": "body", - "type": "Date", - "optional": true, - "field": "openTime", - "description": "

The opening time for the hackathon.

" - }, - { - "group": "body", - "type": "Date", - "optional": true, - "field": "closeTime", - "description": "

The closing time for the hackathon.

" - }, - { - "group": "body", - "type": "Date", - "optional": true, - "field": "confirmTime", - "description": "

The deadline for confirmation for the hackathon.

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Settings Object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Settings patch successful.\", \n \"data\": {\n \"settings\": {\n openTime: \"Wed Feb 06 2019 00:00:00 GMT-0500 (GMT-05:00)\",\n closeTime: \"Sat Feb 01 2020 00:00:00 GMT-0500 (GMT-05:00)\",\n confirmTime: \"Sat Feb 20 2020 00:00:00 GMT-0500 (GMT-05:00)\"\n }\n }\n}", - "type": "object" - }] - }, - "permission": [{ - "name": "Administrators" - }], - "filename": "routes/api/settings.js", - "groupTitle": "Settings", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/settings/" - }] + filename: "routes/api/account.js", + groupTitle: "Account", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/account/:id" + } + ] }, { - "type": "post", - "url": "/sponsor/", - "title": "create a new sponsor", - "name": "createSponsor", - "group": "Sponsor", - "version": "0.0.8", - "parameter": { - "fields": { - "body": [{ - "group": "body", - "type": "MongoID", - "optional": false, - "field": "accountId", - "description": "

ObjectID of the respective account.

" - }, + type: "patch", + url: "/auth/password/change", + title: "change password for logged in user", + name: "changePassword", + group: "Authentication", + version: "0.0.8", + parameter: { + fields: { + Parameter: [ { - "group": "body", - "type": "Number", - "optional": false, - "field": "tier", - "description": "

Tier of the sponsor, from 0 to 5. 0 is lowest tier, and 5 is the custom tier.

" + group: "Parameter", + type: "String", + optional: false, + field: "oldPassword", + description: "

The current password of the user

" }, { - "group": "body", - "type": "String", - "optional": false, - "field": "company", - "description": "

Name of the company.

" - }, + group: "Parameter", + type: "String", + optional: false, + field: "newPassword", + description: "

The new password of the user

" + } + ] + }, + examples: [ + { + title: "Request-Example:", + content: + '{ \n "oldPassword": "password12345",\n "newPassword": "password123456"\n}', + type: "json" + } + ] + }, + success: { + fields: { + "Success 200": [ { - "group": "body", - "type": "String", - "optional": false, - "field": "contractURL", - "description": "

URL link to the contract with the company.

" + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" }, { - "group": "body", - "type": "MongoID[]", - "optional": false, - "field": "nominees", - "description": "

Array of accounts that the company wish to nominate as hackers.

" + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: '{"message": "Successfully reset password", "data": {}}', + type: "json" + } + ] + }, + permission: [ + { + name: ": Must be logged in" + } + ], + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/password/change" + } + ] + }, + { + type: "post", + url: "/auth/confirm/:token", + title: "confirm account using the JWT in :token", + name: "confirmAccount", + group: "Authentication", + version: "0.0.8", + parameter: { + fields: { + Parameter: [ + { + group: "Parameter", + type: "String", + optional: false, + field: "JWT", + description: "

for confirming the account

" } ] } }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "String", - "optional": false, - "field": "message", - "description": "

Success message

" + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" }, { - "group": "Success 200", - "type": "Object", - "optional": false, - "field": "data", - "description": "

Sponsor object

" + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Sponsor creation successful\", \n \"data\": {...}\n }", - "type": "object" - }] + examples: [ + { + title: "Success-Response:", + content: + '{"message": "Successfully confirmed account", "data": {}}', + type: "json" + } + ] }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "String", - "optional": false, - "field": "message", - "description": "

Error message

" + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" }, { - "group": "Error 4xx", - "type": "Object", - "optional": false, - "field": "data", - "description": "

empty

" + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while creating sponsor\", \"data\": {}}", - "type": "object" - }] + examples: [ + { + title: "Error-Response: ", + content: + '{"message": "Invalid token for confirming account, "data": {}}', + type: "object" + } + ] }, - "filename": "routes/api/sponsor.js", - "groupTitle": "Sponsor", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/sponsor/" - }] + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/confirm/:token" + } + ] }, { - "type": "get", - "url": "/sponsor/:id", - "title": "get a sponsor's information", - "name": "getSponsor", - "group": "Sponsor", - "version": "0.0.8", - "parameter": { - "fields": { - "param": [{ - "group": "param", - "type": "string", - "optional": false, - "field": "id", - "description": "

a sponsor's unique mongoID

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "String", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "Object", - "optional": false, - "field": "data", - "description": "

Sponsor object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully retrieved sponsor information\", \n \"data\": {\n \"id\": \"5bff4d736f86be0a41badb91\",\n \"accountId\": \"5bff4d736f86be0a41badb99\",\n \"tier\": 3,\n \"company\": \"companyName\",\n \"contractURL\": \"https://www.contractHere.com\",\n \"nominees\": [\"5bff4d736f86be0a41badb93\",\"5bff4d736f86be0a41badb94\"]\n }\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "String", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "Object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Sponsor not found\", \"data\": {}}", - "type": "object" - }] - }, - "filename": "routes/api/sponsor.js", - "groupTitle": "Sponsor", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/sponsor/:id" - }] + type: "post", + url: "/auth/password/forgot", + title: "forgot password route", + name: "forgotPassword", + group: "Authentication", + version: "0.0.8", + parameter: { + fields: { + Parameter: [ + { + group: "Parameter", + type: "String", + optional: false, + field: "email", + description: "

the email address of the account

" + } + ] + }, + examples: [ + { + title: "Request-Example:", + content: '{ "email": "myemail@mchacks.ca" }', + type: "json" + } + ] + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: '{"message": "Sent reset email", "data": {}}', + type: "json" + } + ] + }, + permission: [ + { + name: ": public" + } + ], + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/password/forgot" + } + ] }, { - "type": "patch", - "url": "/sponsor/", - "title": "update a sponsor", - "name": "patchSponsor", - "group": "Sponsor", - "version": "1.3.0", - "parameter": { - "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "id", - "description": "

ObjectID of the sponsor

" - }], - "body": [{ - "group": "body", - "type": "String", - "optional": false, - "field": "company", - "description": "

Name of the company.

" - }, - { - "group": "body", - "type": "String", - "optional": false, - "field": "contractURL", - "description": "

URL link to the contract with the company.

" - }, - { - "group": "body", - "type": "ObjectId[]", - "optional": false, - "field": "nominees", - "description": "

Array of accounts that the company wish to nominate as hackers.

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "String", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "Object", - "optional": false, - "field": "data", - "description": "

Sponsor object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Sponsor update successful\", \n \"data\": {...}\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "String", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "Object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while updating sponsor\", \"data\": {}}", - "type": "object" - }] - }, - "filename": "routes/api/sponsor.js", - "groupTitle": "Sponsor", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/sponsor/" - }] + type: "get", + url: "/auth/rolebindings/:id", + title: "retrieve rolebindings for a user given by their user id :id", + name: "getRoleBindings", + group: "Authentication", + version: "0.0.8", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "id", + description: "

MongoId of an account

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Rolebindings object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Successfully retrieved role bindings",\n "data": {\n accountId:"5beca4ab2e069a34f91697b2"\n id:"5beca4ae2e069a34f91698b1"\n roles: [\n {\n _id:"5beca4ab2e069a34f91697d9",\n name:"hacker",\n routes: [\n {_id: "5beca4ae2e069a34f9169852", requestType: "POST", uri: "/api/auth/login"},\n {_id: "5beca4ae2e069a34f9169851", requestType: "POST", uri: "/api/auth/logout"},\n {_id: "5beca4ae2e069a34f9169850", requestType: "GET", uri: "/api/auth/rolebindings/:self"},\n {_id: "5beca4ae2e069a34f916984f", requestType: "GET", uri: "/api/account/self"},\n {_id: "5beca4ae2e069a34f916984e", requestType: "GET", uri: "/api/account/:self"},\n {_id: "5beca4ae2e069a34f916984d", requestType: "PATCH", uri: "/api/account/:self"},\n {_id: "5beca4ae2e069a34f916984c", requestType: "POST", uri: "/api/hacker/"},\n {_id: "5beca4ae2e069a34f916984b", requestType: "GET", uri: "/api/hacker/:self"},\n {_id: "5beca4ae2e069a34f916984a", requestType: "GET", uri: "/api/hacker/:self/resume"},\n {_id: "5beca4ae2e069a34f9169849", requestType: "PATCH", uri: "/api/hacker/:self"}\n ]\n }\n ]\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Role Bindings not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/rolebindings/:id" + } + ] }, { - "type": "post", - "url": "/team/", - "title": "create a new team consisting of only the logged in user", - "name": "createTeam", - "group": "Team", - "version": "0.0.8", - "parameter": { - "fields": { - "body": [{ - "group": "body", - "type": "String", - "optional": false, - "field": "name", - "description": "

Name of the team.

" - }, - { - "group": "body", - "type": "String", - "optional": true, - "field": "devpostURL", - "description": "

Devpost link to hack. Once the link is sent, the hack will be considered to be submitted.

" - }, - { - "group": "body", - "type": "String", - "optional": true, - "field": "projectName", - "description": "

Name of the team.

" - } - ] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Team object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Team creation successful\", \n \"data\": {...}\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "string", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while creating team\", \"data\": {}}", - "type": "object" - }] - }, - "filename": "routes/api/team.js", - "groupTitle": "Team", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/team/" - }] - }, + type: "get", + url: "/auth/roles", + title: "get roles", + name: "getRoles", + description: "

get all roles that exist in the database

", + group: "Authentication", + version: "0.0.8", + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response:", + content: + '{"message": "Sucessfully retrieved all roles", "data":\n[{name: "GodStaff", routes: Array(27), id: "5bee20ef3ca9dd4754382880"},\n {name: "Hacker", routes: Array(10), id: "5bee20ef3ca9dd4754382881"},\n {name: "Volunteer", routes: Array(4), id: "5bee20ef3ca9dd4754382882"}]', + type: "json" + } + ] + }, + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/roles" + } + ] + }, + { + type: "post", + url: "/auth/login", + title: "login to the service", + name: "login", + group: "Authentication", + version: "0.0.8", + parameter: { + fields: { + Parameter: [ + { + group: "Parameter", + type: "string", + optional: false, + field: "email", + description: "

Account email

" + }, + { + group: "Parameter", + type: "string", + optional: false, + field: "password", + description: "

Account password

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: '{"message": "Successfully logged in", "data": {}}', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Invalid Authentication", "data": {}}', + type: "object" + } + ] + }, + permission: [ + { + name: ": public" + } + ], + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/login" + } + ] + }, + { + type: "get", + url: "/auth/logout", + title: "logout of service", + name: "logout", + group: "Authentication", + version: "0.0.8", + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: '{"message": "Successfully logged out", "data": {}}', + type: "object" + } + ] + }, + permission: [ + { + name: ": public" + } + ], + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/logout" + } + ] + }, + { + type: "get", + url: "/auth/confirm/resend", + title: "resend confirmation token", + name: "resendConfirmAccount", + group: "Authentication", + version: "0.0.8", + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response:", + content: + '{"message": "Successfully resent confirmation email", "data": {}}', + type: "json" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response:", + content: + ' HTTP/1.1 422\n{"message": "Account already confirmed", "data": {}}', + type: "json" + }, + { + title: "Error-Response:", + content: + ' HTTP/1.1 428\n{"message": "Account confirmation token does not exist", "data": {}}', + type: "json" + } + ] + }, + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/confirm/resend" + } + ] + }, + { + type: "post", + url: "/auth/password/reset", + title: "reset password", + name: "resetPassword", + group: "Authentication", + version: "0.0.8", + parameter: { + fields: { + Parameter: [ + { + group: "Parameter", + type: "String", + optional: false, + field: "password", + description: "

the password of the account

" + } + ] + }, + examples: [ + { + title: "Request-Example:", + content: '{ "password": "hunter2" }', + type: "json" + } + ] + }, + header: { + fields: { + Header: [ + { + group: "Header", + type: "String", + optional: false, + field: "Authentication", + description: + "

the token that was provided in the reset password email

" + } + ] + }, + examples: [ + { + title: "Header-Example:", + content: + '{\n "X-Reset-Token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"\n}', + type: "json" + } + ] + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: '{"message": "Successfully reset password", "data": {}}', + type: "json" + } + ] + }, + permission: [ + { + name: ": must have authentication token" + } + ], + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/password/reset" + } + ] + }, + { + type: "patch", + url: "/hacker/checkin/:id", + title: + "update a hacker's status to be 'Checked-in'. Note that the Hacker must eitehr be Accepted or Confirmed.", + name: "checkinHacker", + group: "Hacker", + version: "0.0.9", + parameter: { + fields: { + body: [ + { + group: "body", + type: "string", + optional: true, + field: "status", + description: "

Check-in status. "Checked-in"

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Changed hacker information", \n "data": {\n "status": "Checked-in"\n }\n}', + type: "object" + } + ] + }, + permission: [ + { + name: "Administrator" + }, + { + name: "Volunteer" + } + ], + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/checkin/:id" + } + ] + }, + { + type: "post", + url: "/hacker/", + title: "create a new hacker", + name: "createHacker", + group: "Hacker", + version: "0.0.8", + parameter: { + fields: { + body: [ + { + group: "body", + type: "MongoID", + optional: false, + field: "accountId", + description: "

ObjectID of the respective account

" + }, + { + group: "body", + type: "Json", + optional: false, + field: "application", + description: + "

The hacker's application. Resume and jobInterest fields are required.

" + } + ] + }, + examples: [ + { + title: "application: ", + content: + '{\n "application":{\n "URL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n}', + type: "Json" + } + ] + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Hacker creation successful", \n "data": {\n "id":"5bff4d736f86be0a41badb91",\n "application":{\n "URL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n },\n "status":"Applied",\n "ethnicity":["White or Caucasian"," Asian or Pacific Islander"],\n "accountId":"5bff2a35e533b0f6562b4998",\n "school":"McPherson College",\n "gender":"Female",\n "needsBus":false,\n "major":"Accounting",\n "graduationYear":2019,\n "codeOfConduct":true,\n }\n}', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Error while creating hacker", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/" + } + ] + }, + { + type: "get", + url: "/hacker/email/:email", + title: "get a hacker's information", + name: "getHacker", + group: "Hacker", + version: "0.0.8", + parameter: { + fields: { + param: [ + { + group: "param", + type: "String", + optional: false, + field: "email", + description: "

a hacker's unique email

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Successfully retrieved hacker information", \n "data": {\n "id":"5bff4d736f86be0a41badb91",\n "application":{\n "URL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n },\n "status":"Applied",\n "ethnicity":["White or Caucasian"," Asian or Pacific Islander"],\n "accountId":"5bff2a35e533b0f6562b4998",\n "school":"McPherson College",\n "gender":"Female",\n "needsBus":false,\n "major":"Accounting",\n "graduationYear":2019,\n "codeOfConduct":true,\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Hacker not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/email/:email" + } + ] + }, + { + type: "get", + url: "/hacker/:id", + title: "get a hacker's information", + name: "getHacker", + group: "Hacker", + version: "0.0.8", + parameter: { + fields: { + param: [ + { + group: "param", + type: "String", + optional: false, + field: "id", + description: "

a hacker's unique mongoID

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Successfully retrieved hacker information", \n "data": {\n "id":"5bff4d736f86be0a41badb91",\n "application":{\n "URL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n },\n "status":"Applied",\n "ethnicity":["White or Caucasian"," Asian or Pacific Islander"],\n "accountId":"5bff2a35e533b0f6562b4998",\n "school":"McPherson College",\n "gender":"Female",\n "needsBus":false,\n "major":"Accounting",\n "graduationYear":2019,\n "codeOfConduct":true,\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Hacker not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/:id" + } + ] + }, + { + type: "get", + url: "/hacker/resume:id", + title: "get the resume for a hacker.", + name: "getHackerResume", + group: "Hacker", + version: "0.0.8", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "id", + description: "

Hacker id

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + } + ] + }, + examples: [ + { + title: "Success-Response:", + content: + 'HTTP/1.1 200 OK \n{ \n message: "Downloaded resume", \n data: { \n id: "507f191e810c19729de860ea", \n resume: [Buffer] \n } \n}', + type: "json" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

"Resume does not exist"

" + } + ] + }, + examples: [ + { + title: "Error-Response:", + content: + 'HTTP/1.1 404 \n{ \n message: "Resume not found", \n data: {} \n}', + type: "json" + } + ] + }, + permission: [ + { + name: + "Must be logged in, and the account id must be linked to the hacker." + } + ], + filename: "routes/api/hacker.js", + groupTitle: "Hacker" + }, + { + type: "get", + url: "/hacker/stats", + title: "Gets the stats of all of the hackers who have applied.", + name: "getHackerStats", + group: "Hacker", + version: "0.0.9", + parameter: { + fields: { + query: [ + { + group: "query", + type: "String", + optional: false, + field: "model", + description: + "

the model to be searched (Only hacker supported)

" + }, + { + group: "query", + type: "Array", + optional: false, + field: "q", + description: + "

the query to be executed. For more information on how to format this, please see https://docs.mchacks.ca/architecture/

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Retrieved stats",\n "data": {\n "stats" : {\n "total": 10,\n "status": { "Applied": 10 },\n "school": { "McGill University": 3, "Harvard University": 7 },\n degree: { "Undergraduate": 10 },\n gender: { "Male": 1, "Female": 9 },\n needsBus: { "true": 7, "false": 3 },\n ethnicity: { "White": 10, },\n jobInterest: { "Internship": 10 },\n major: { "Computer Science": 10 },\n graduationYear: { "2019": 10 },\n dietaryRestrictions: { "None": 10 },\n shirtSize: { "M": 3, "XL": 7 },\n age: { "22": 10 }\n }\n }\n}', + type: "object" + } + ] + }, + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/stats" + } + ] + }, + { + type: "patch", + url: "/hacker/:id", + title: "update a hacker's information.", + description: + "

This route only contains the ability to update a subset of a hacker's information. If you want to update a status, you must have Admin priviledges and use PATCH /hacker/status/:id.

", + name: "patchHacker", + group: "Hacker", + version: "0.0.8", + parameter: { + fields: { + body: [ + { + group: "body", + type: "String", + optional: true, + field: "school", + description: "

Name of the school the hacker goes to

" + }, + { + group: "body", + type: "String", + optional: true, + field: "gender", + description: "

Gender of the hacker

" + }, + { + group: "body", + type: "Boolean", + optional: true, + field: "needsBus", + description: + "

Whether the hacker requires a bus for transportation

" + }, + { + group: "body", + type: "String[]", + optional: true, + field: "ethnicity", + description: "

the ethnicities of the hacker

" + }, + { + group: "body", + type: "String[]", + optional: true, + field: "major", + description: "

the major of the hacker

" + }, + { + group: "body", + type: "Number", + optional: true, + field: "graduationYear", + description: "

the graduation year of the hacker

" + }, + { + group: "body", + type: "Json", + optional: true, + field: "application", + description: "

The hacker's application

" + } + ] + }, + examples: [ + { + title: "application: ", + content: + '{\n "URL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n }', + type: "Json" + } + ] + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Changed hacker information", \n "data": {\n "id":"5bff4d736f86be0a41badb91",\n "application":{\n "URL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n },\n "status":"Applied",\n "ethnicity":["White or Caucasian"," Asian or Pacific Islander"],\n "accountId":"5bff2a35e533b0f6562b4998",\n "school":"McPherson College",\n "gender":"Female",\n "needsBus":false,\n "major":"Accounting",\n "graduationYear":2019,\n "codeOfConduct":true,\n}', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Error while updating hacker", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/:id" + } + ] + }, + { + type: "patch", + url: "/hacker/confirmation/:id", + title: + "Allows confirmation of hacker attendence if they are accepted. Also allows change from 'confirmed' to 'cancelled'.", + name: "patchHackerConfirmed", + group: "Hacker", + version: "0.0.9", + parameter: { + fields: { + body: [ + { + group: "body", + type: "string", + optional: true, + field: "status", + description: + "

The new status of the hacker. "Accepted", "Confirmed", or "Cancelled"

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Changed hacker information", \n "data": {\n "status": "Confirmed"\n }\n}', + type: "object" + } + ] + }, + permission: [ + { + name: "Administrator" + }, + { + name: "Hacker" + } + ], + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/confirmation/:id" + } + ] + }, + { + type: "patch", + url: "/hacker/status/:id", + title: "update a hacker's status", + name: "patchHackerStatus", + group: "Hacker", + version: "0.0.9", + parameter: { + fields: { + body: [ + { + group: "body", + type: "string", + optional: true, + field: "status", + description: + "

Status of the hacker's application ("None"|"Applied"|"Waitlisted"|"Confirmed"|"Cancelled"|"Checked-in")

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Changed hacker information", \n "data": {\n "status": "Accepted"\n }\n}', + type: "object" + } + ] + }, + permission: [ + { + name: "Administrator" + } + ], + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/status/:id" + } + ] + }, + { + type: "post", + url: "/hacker/resume/:id", + title: "upload or update resume for a hacker.", + name: "postHackerResume", + group: "Hacker", + version: "0.0.8", + description: + "

NOTE: This must be sent via multipart/form-data POST request

", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "id", + description: "

Hacker id

" + } + ], + body: [ + { + group: "body", + type: "File", + optional: false, + field: "resume", + description: "

The uploaded file.

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: + "

Location in the bucket that the file was stored.

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + 'HTTP/1.1 200 OK\n{\n message: "Uploaded resume", \n data: {\n filename: "resumes/1535032624768-507f191e810c19729de860ea"\n }\n}', + type: "json" + } + ] + }, + permission: [ + { + name: + "Must be logged in, and the account id must be linked to the hacker." + } + ], + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/resume/:id" + } + ] + }, + { + type: "post", + url: "/hacker/email/weekOf/:id", + title: "", + description: + "

Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in.

", + name: "postHackerSendWeekOfEmail", + group: "Hacker", + version: "0.0.9", + parameter: { + fields: { + param: [ + { + group: "param", + type: "string", + optional: true, + field: "status", + description: "

The hacker ID

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Hacker week-of email sent.", \n "data": {}\n}', + type: "object" + } + ] + }, + permission: [ + { + name: "Administrator" + } + ], + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/email/weekOf/:id" + } + ] + }, + { + type: "post", + url: "/hacker/email/weekOf/:id", + title: "", + description: + "

Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in.

", + name: "postHackerSendWeekOfEmail", + group: "Hacker", + version: "0.0.9", + parameter: { + fields: { + param: [ + { + group: "param", + type: "string", + optional: true, + field: "status", + description: "

The hacker ID

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Hacker week-of email sent.", \n "data": {}\n}', + type: "object" + } + ] + }, + permission: [ + { + name: "Administrator" + } + ], + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/email/weekOf/:id" + } + ] + }, + { + type: "get", + url: "/sponsor/self", + title: "get information about logged in sponsor", + name: "self", + group: "Hacker", + version: "1.4.1", + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Sponsor object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Successfully retrieved sponsor information", \n "data": {\n "id": "5bff4d736f86be0a41badb91",\n "accountId": "5bff4d736f86be0a41badb99",\n "tier": 3,\n "company": "companyName",\n "contractURL": "https://www.contractHere.com",\n "nominees": ["5bff4d736f86be0a41badb93","5bff4d736f86be0a41badb94"]\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Sponsor not found", "data": {}}', + type: "object" + } + ] + }, + permission: [ + { + name: ": Sponsor" + } + ], + filename: "routes/api/sponsor.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/sponsor/self" + } + ] + }, + { + type: "get", + url: "/hacker/self", + title: "get information about own hacker", + name: "self", + group: "Hacker", + version: "0.0.8", + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Hacker found by logged in account id", \n "data": {\n "id":"5bff4d736f86be0a41badb91",\n "application":{\n "URL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n },\n "status":"Applied",\n "ethnicity":["White or Caucasian"," Asian or Pacific Islander"],\n "accountId":"5bff2a35e533b0f6562b4998",\n "school":"McPherson College",\n "gender":"Female",\n "needsBus":false,\n "major":["Accounting"],\n "graduationYear":2019,\n "codeOfConduct":true,\n } \n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Hacker not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/self" + } + ] + }, + { + type: "get", + url: "/", + title: "version", + version: "0.0.8", + name: "index", + group: "Index", + permission: [ + { + name: "public" + } + ], + filename: "routes/index.js", + groupTitle: "Index", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/" + } + ] + }, + { + type: "post", + url: "/api/role/", + title: "create a new role", + name: "createRole", + group: "Role", + version: "1.1.1", + parameter: { + fields: { + body: [ + { + group: "body", + type: "String", + optional: false, + field: "name", + description: "

Name of the route

" + }, + { + group: "body", + type: "Route[]", + optional: false, + field: "routes", + description: "

The routes that this role gives access to

" + } + ] + }, + examples: [ + { + title: "application: ", + content: + '{\n "name": "routename",\n "routes": [\n {\n uri: "/api/hacker/"\n requestType: "POST"\n }\n ]\n}', + type: "Json" + } + ] + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Role object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Role creation successful", \n "data": {\n "name": "routename",\n "routes": [\n {\n uri: "/api/hacker/"\n requestType: "POST"\n }\n ]\n }\n}', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Error while creating role", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/role.js", + groupTitle: "Role", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/api/role/" + } + ] + }, + { + type: "get", + url: "/search/", + title: "provide a specific query for any defined model", + name: "search", + group: "Search", + version: "0.0.8", + parameter: { + fields: { + query: [ + { + group: "query", + type: "String", + optional: false, + field: "model", + description: "

the model to be searched

" + }, + { + group: "query", + type: "Array", + optional: false, + field: "q", + description: + "

the query to be executed. For more information on how to format this, please see https://docs.mchacks.ca/architecture/

" + }, + { + group: "query", + type: "String", + optional: false, + field: "sort", + description: "

either "asc" or "desc"

" + }, + { + group: "query", + type: "number", + optional: false, + field: "page", + description: "

the page number that you would like

" + }, + { + group: "query", + type: "number", + optional: false, + field: "limit", + description: + "

the maximum number of results that you would like returned

" + }, + { + group: "query", + type: "any", + optional: false, + field: "sort_by", + description: + "

any parameter you want to sort the results by

" + }, + { + group: "query", + type: "boolean", + optional: false, + field: "expand", + description: + "

whether you want to expand sub documents within the results

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Results

" + } + ] + }, + examples: [ + { + title: "Success-Response:", + content: + '{\n "message": "Successfully executed query, returning all results",\n "data": [\n {...}\n ]\n }', + type: "object" + }, + { + title: "Success-Response:", + content: + '{\n "message": "No results found.",\n "data": {}\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response:", + content: '{"message": "Validation failed", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/search.js", + groupTitle: "Search", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/search/" + } + ] + }, + { + type: "get", + url: "/settings/", + title: "Get the settings for the current hackathon", + name: "getSettings", + group: "Settings", + version: "1.1.1", + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Settings Object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Settings creation successful.", \n "data": {\n "settings": {\n openTime: "Wed Feb 06 2019 00:00:00 GMT-0500 (GMT-05:00)",\n closeTime: "Sat Feb 01 2020 00:00:00 GMT-0500 (GMT-05:00)",\n confirmTime: "Sat Feb 20 2020 00:00:00 GMT-0500 (GMT-05:00)"\n }\n }\n}', + type: "object" + } + ] + }, + permission: [ + { + name: "public" + } + ], + filename: "routes/api/settings.js", + groupTitle: "Settings", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/settings/" + } + ] + }, + { + type: "patch", + url: "/settings/", + title: "Patch the settings for the current hackathon", + name: "patchSettings", + group: "Settings", + version: "1.1.1", + parameter: { + fields: { + body: [ + { + group: "body", + type: "Date", + optional: true, + field: "openTime", + description: "

The opening time for the hackathon.

" + }, + { + group: "body", + type: "Date", + optional: true, + field: "closeTime", + description: "

The closing time for the hackathon.

" + }, + { + group: "body", + type: "Date", + optional: true, + field: "confirmTime", + description: + "

The deadline for confirmation for the hackathon.

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Settings Object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Settings patch successful.", \n "data": {\n "settings": {\n openTime: "Wed Feb 06 2019 00:00:00 GMT-0500 (GMT-05:00)",\n closeTime: "Sat Feb 01 2020 00:00:00 GMT-0500 (GMT-05:00)",\n confirmTime: "Sat Feb 20 2020 00:00:00 GMT-0500 (GMT-05:00)"\n }\n }\n}', + type: "object" + } + ] + }, + permission: [ + { + name: "Administrators" + } + ], + filename: "routes/api/settings.js", + groupTitle: "Settings", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/settings/" + } + ] + }, + { + type: "post", + url: "/sponsor/", + title: "create a new sponsor", + name: "createSponsor", + group: "Sponsor", + version: "0.0.8", + parameter: { + fields: { + body: [ + { + group: "body", + type: "MongoID", + optional: false, + field: "accountId", + description: "

ObjectID of the respective account.

" + }, + { + group: "body", + type: "Number", + optional: false, + field: "tier", + description: + "

Tier of the sponsor, from 0 to 5. 0 is lowest tier, and 5 is the custom tier.

" + }, + { + group: "body", + type: "String", + optional: false, + field: "company", + description: "

Name of the company.

" + }, + { + group: "body", + type: "String", + optional: false, + field: "contractURL", + description: "

URL link to the contract with the company.

" + }, + { + group: "body", + type: "MongoID[]", + optional: false, + field: "nominees", + description: + "

Array of accounts that the company wish to nominate as hackers.

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Sponsor object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Sponsor creation successful", \n "data": {...}\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Error while creating sponsor", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/sponsor.js", + groupTitle: "Sponsor", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/sponsor/" + } + ] + }, + { + type: "get", + url: "/sponsor/:id", + title: "get a sponsor's information", + name: "getSponsor", + group: "Sponsor", + version: "0.0.8", + parameter: { + fields: { + param: [ + { + group: "param", + type: "string", + optional: false, + field: "id", + description: "

a sponsor's unique mongoID

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Sponsor object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Successfully retrieved sponsor information", \n "data": {\n "id": "5bff4d736f86be0a41badb91",\n "accountId": "5bff4d736f86be0a41badb99",\n "tier": 3,\n "company": "companyName",\n "contractURL": "https://www.contractHere.com",\n "nominees": ["5bff4d736f86be0a41badb93","5bff4d736f86be0a41badb94"]\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Sponsor not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/sponsor.js", + groupTitle: "Sponsor", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/sponsor/:id" + } + ] + }, + { + type: "patch", + url: "/sponsor/", + title: "update a sponsor", + name: "patchSponsor", + group: "Sponsor", + version: "1.3.0", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "id", + description: "

ObjectID of the sponsor

" + } + ], + body: [ + { + group: "body", + type: "String", + optional: false, + field: "company", + description: "

Name of the company.

" + }, + { + group: "body", + type: "String", + optional: false, + field: "contractURL", + description: "

URL link to the contract with the company.

" + }, + { + group: "body", + type: "ObjectId[]", + optional: false, + field: "nominees", + description: + "

Array of accounts that the company wish to nominate as hackers.

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Sponsor object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Sponsor update successful", \n "data": {...}\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Error while updating sponsor", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/sponsor.js", + groupTitle: "Sponsor", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/sponsor/" + } + ] + }, { - "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": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

{}

" - } - ] - }, - "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: "post", + url: "/team/", + title: "create a new team consisting of only the logged in user", + name: "createTeam", + group: "Team", + version: "0.0.8", + parameter: { + fields: { + body: [ + { + group: "body", + type: "String", + optional: false, + field: "name", + description: "

Name of the team.

" + }, + { + group: "body", + type: "String", + optional: true, + field: "devpostURL", + description: + "

Devpost link to hack. Once the link is sent, the hack will be considered to be submitted.

" + }, + { + group: "body", + type: "String", + optional: true, + field: "projectName", + description: "

Name of the team.

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Team object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Team creation successful", \n "data": {...}\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Error while creating team", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/team.js", + groupTitle: "Team", + sampleRequest: [ + { + 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: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

{}

" + } + ] + }, + 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", - "title": "get a team's information", - "name": "getTeam", - "group": "Team", - "version": "0.0.8", - "parameter": { - "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "id", - "description": "

MongoId of the team

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "String", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "Object", - "optional": false, - "field": "data", - "description": "

Team object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Team retrieval successful\", \n \"data\": { \n \"team\": {\n \"name\":\"foo\",\n \"members\": [\n ObjectId('...')\n ],\n \"devpostURL\": \"www.devpost.com/foo\",\n \"projectName\": \"fooey\"\n },\n \"members\": [\n {\n \"firstName\": \"John\",\n \"lastName\": \"Doe\"\n }\n ],\n }\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "String", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "Object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Team not found\", \"data\": {}}", - "type": "object" - }] - }, - "filename": "routes/api/team.js", - "groupTitle": "Team", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/team/:id" - }] + type: "get", + url: "/team/:id", + title: "get a team's information", + name: "getTeam", + group: "Team", + version: "0.0.8", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "id", + description: "

MongoId of the team

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Team object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Team retrieval successful", \n "data": { \n "team": {\n "name":"foo",\n "members": [\n ObjectId(\'...\')\n ],\n "devpostURL": "www.devpost.com/foo",\n "projectName": "fooey"\n },\n "members": [\n {\n "firstName": "John",\n "lastName": "Doe"\n }\n ],\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Team not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/team.js", + groupTitle: "Team", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/team/:id" + } + ] }, { - "type": "patch", - "url": "/team/join/", - "title": "Allows a logged in hacker to join a team by name", - "name": "patchJoinTeam", - "group": "Team", - "version": "1.1.1", - "parameter": { - "fields": { - "body": [{ - "group": "body", - "type": "string", - "optional": true, - "field": "name", - "description": "

Name of the team to join

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

{}

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Team join successful.\", \n \"data\": {}\n}", - "type": "object" - }] - }, - "filename": "routes/api/team.js", - "groupTitle": "Team", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/team/join/" - }] + type: "patch", + url: "/team/join/", + title: "Allows a logged in hacker to join a team by name", + name: "patchJoinTeam", + group: "Team", + version: "1.1.1", + parameter: { + fields: { + body: [ + { + group: "body", + type: "string", + optional: true, + field: "name", + description: "

Name of the team to join

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

{}

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Team join successful.", \n "data": {}\n}', + type: "object" + } + ] + }, + filename: "routes/api/team.js", + groupTitle: "Team", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/team/join/" + } + ] }, { - "type": "patch", - "url": "/team/:hackerId", - "title": "Update a team's information. The team is specified by the hacker belonging to it.", - "name": "patchTeam", - "group": "Team", - "version": "0.0.8", - "description": "

We use hackerId instead of teamId because authorization requires a one-to-one mapping from param id to accountId, but we are not able to have that from teamId to accountId due to multiple members in a team. Instead, we use hackerId, as there is a 1 to 1 link between hackerId to teamId, and a 1 to 1 link between hackerId and accountId

", - "parameter": { - "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "hackerId", - "description": "

a hacker's unique Id

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "String", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "Object", - "optional": false, - "field": "data", - "description": "

Team object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Team update successful.\", \n \"data\": {...}\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "String", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "Object", - "optional": false, - "field": "data", - "description": "

Query input that caused the error.

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Team not found\", \"data\": {teamId}}", - "type": "object" - }] - }, - "filename": "routes/api/team.js", - "groupTitle": "Team", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/team/:hackerId" - }] + type: "patch", + url: "/team/:hackerId", + title: + "Update a team's information. The team is specified by the hacker belonging to it.", + name: "patchTeam", + group: "Team", + version: "0.0.8", + description: + "

We use hackerId instead of teamId because authorization requires a one-to-one mapping from param id to accountId, but we are not able to have that from teamId to accountId due to multiple members in a team. Instead, we use hackerId, as there is a 1 to 1 link between hackerId to teamId, and a 1 to 1 link between hackerId and accountId

", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "hackerId", + description: "

a hacker's unique Id

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Team object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Team update successful.", \n "data": {...}\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

Query input that caused the error.

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Team not found", "data": {teamId}}', + type: "object" + } + ] + }, + filename: "routes/api/team.js", + groupTitle: "Team", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/team/:hackerId" + } + ] }, { - "type": "post", - "url": "/volunteer/", - "title": "create a new volunteer", - "name": "createVolunteer", - "group": "Volunteer", - "version": "0.0.8", - "parameter": { - "fields": { - "body": [{ - "group": "body", - "type": "MongoID", - "optional": false, - "field": "accountId", - "description": "

MongoID of the account of the volunteer

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "string", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "object", - "optional": false, - "field": "data", - "description": "

Volunteer object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Volunteer creation successful\", \n \"data\": {...}\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "string", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while creating volunteer\", \"data\": {}}", - "type": "object" - }] - }, - "filename": "routes/api/volunteer.js", - "groupTitle": "Volunteer", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/volunteer/" - }] + type: "post", + url: "/volunteer/", + title: "create a new volunteer", + name: "createVolunteer", + group: "Volunteer", + version: "0.0.8", + parameter: { + fields: { + body: [ + { + group: "body", + type: "MongoID", + optional: false, + field: "accountId", + description: "

MongoID of the account of the volunteer

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Volunteer object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Volunteer creation successful", \n "data": {...}\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{"message": "Error while creating volunteer", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/volunteer.js", + groupTitle: "Volunteer", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/volunteer/" + } + ] }, { - "type": "get", - "url": "/volunteer/:id", - "title": "get a volunteer's information", - "name": "getVolunteer", - "group": "Volunteer", - "version": "1.3.0", - "parameter": { - "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "id", - "description": "

a volunteer's unique mongoID

" - }] - } - }, - "success": { - "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "String", - "optional": false, - "field": "message", - "description": "

Success message

" - }, - { - "group": "Success 200", - "type": "Object", - "optional": false, - "field": "data", - "description": "

Volunteer object

" - } - ] - }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully retrieved volunteer information\", \n \"data\": {...}\n }", - "type": "object" - }] - }, - "error": { - "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "String", - "optional": false, - "field": "message", - "description": "

Error message

" - }, - { - "group": "Error 4xx", - "type": "Object", - "optional": false, - "field": "data", - "description": "

empty

" - } - ] - }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Volunteer not found\", \"data\": {}}", - "type": "object" - }] - }, - "filename": "routes/api/volunteer.js", - "groupTitle": "Volunteer", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/volunteer/:id" - }] + type: "get", + url: "/volunteer/:id", + title: "get a volunteer's information", + name: "getVolunteer", + group: "Volunteer", + version: "1.3.0", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "id", + description: "

a volunteer's unique mongoID

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Volunteer object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Successfully retrieved volunteer information", \n "data": {...}\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Volunteer not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/volunteer.js", + groupTitle: "Volunteer", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/volunteer/:id" + } + ] } ] -}); \ No newline at end of file +}); diff --git a/docs/api/api_data.json b/docs/api/api_data.json index b6d16068..99bc13f7 100644 --- a/docs/api/api_data.json +++ b/docs/api/api_data.json @@ -1,4 +1,5 @@ -[{ +[ + { "type": "post", "url": "/account/", "title": "create a new account", @@ -7,7 +8,8 @@ "version": "0.0.8", "parameter": { "fields": { - "body": [{ + "body": [ + { "group": "body", "type": "String", "optional": false, @@ -71,23 +73,28 @@ "description": "

the user's phone number, represented as a string.

" } ], - "header": [{ - "group": "header", - "type": "JWT", - "optional": true, - "field": "token", - "description": "

the user's invite token.

" - }] - }, - "examples": [{ - "title": "Request-Example:", - "content": "{ \n \"firstName\": \"Theo\",\n \"lastName\":\"Klein\",\n \"pronoun\":\"he/him\",\n \"email\":\"theo@klein.com\",\n \"password\":\"hunter2\",\n \"dietaryRestrictions\":[\"Halal\"],\n \"phoneNumber\":1234567890,\n \"shirtSize\":\"S\",\n \"birthDate\":\"10/30/1997\"\n}", - "type": "json" - }] + "header": [ + { + "group": "header", + "type": "JWT", + "optional": true, + "field": "token", + "description": "

the user's invite token.

" + } + ] + }, + "examples": [ + { + "title": "Request-Example:", + "content": "{ \n \"firstName\": \"Theo\",\n \"lastName\":\"Klein\",\n \"pronoun\":\"he/him\",\n \"email\":\"theo@klein.com\",\n \"password\":\"hunter2\",\n \"dietaryRestrictions\":[\"Halal\"],\n \"phoneNumber\":1234567890,\n \"shirtSize\":\"S\",\n \"birthDate\":\"10/30/1997\"\n}", + "type": "json" + } + ] }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -103,15 +110,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Account creation successful\", \n \"data\": {\n \"id\": ObjectId(\"5bff8b9f3274cf001bc71048\"),\n \t\"firstName\": \"Theo\",\n \"lastName\":\"Klein\",\n \"pronoun\":\"he/him\",\n \"email\":\"theo@klein.com\",\n \"dietaryRestrictions\":[\"Halal\"],\n \"phoneNumber\":1234567890,\n \t\"shirtSize\":\"S\",\n \"birthDate\":Date(\"10/30/1997\")\n }\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Account creation successful\", \n \"data\": {\n \"id\": ObjectId(\"5bff8b9f3274cf001bc71048\"),\n \t\"firstName\": \"Theo\",\n \"lastName\":\"Klein\",\n \"pronoun\":\"he/him\",\n \"email\":\"theo@klein.com\",\n \"dietaryRestrictions\":[\"Halal\"],\n \"phoneNumber\":1234567890,\n \t\"shirtSize\":\"S\",\n \"birthDate\":Date(\"10/30/1997\")\n }\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "string", "optional": false, @@ -127,17 +137,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\n \"message\": \"Account already exists\", \n \"data\": {\n \"route\": \"/\"\n }\n}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\n \"message\": \"Account already exists\", \n \"data\": {\n \"route\": \"/\"\n }\n}", + "type": "object" + } + ] }, "filename": "routes/api/account.js", "groupTitle": "Account", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/account/" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/account/" + } + ] }, { "type": "get", @@ -148,18 +162,21 @@ "version": "0.0.8", "parameter": { "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "id", - "description": "

MongoId of an account

" - }] + "param": [ + { + "group": "param", + "type": "ObjectId", + "optional": false, + "field": "id", + "description": "

MongoId of an account

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -175,15 +192,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Account found by user id\", \n \"data\": {\n \"id\": ObjectId(\"5bff8b9f3274cf001bc71048\"),\n \"firstName\": \"Theo\",\n \"lastName\":\"Klein\",\n \"pronoun\":\"he/him\",\n \"email\":\"theo@klein.com\",\n \"dietaryRestrictions\":[\"Halal\"],\n \"phoneNumber\":1234567890,\n \"shirtSize\":\"S\",\n \"birthDate\":Date(\"10/30/1997\")\n }\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Account found by user id\", \n \"data\": {\n \"id\": ObjectId(\"5bff8b9f3274cf001bc71048\"),\n \"firstName\": \"Theo\",\n \"lastName\":\"Klein\",\n \"pronoun\":\"he/him\",\n \"email\":\"theo@klein.com\",\n \"dietaryRestrictions\":[\"Halal\"],\n \"phoneNumber\":1234567890,\n \"shirtSize\":\"S\",\n \"birthDate\":Date(\"10/30/1997\")\n }\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "string", "optional": false, @@ -199,17 +219,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Account not found\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Account not found\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/account.js", "groupTitle": "Account", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/account/:id" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/account/:id" + } + ] }, { "type": "get", @@ -220,17 +244,21 @@ "version": "0.0.8", "description": "

Get all of the invites that currently exist in the database.

", "success": { - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Invite retrieval successful.\", \n \"data\": [{\n \"email\":\"abc@def.com\",\n \"accountType\":\"Hacker\"\n }]\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Invite retrieval successful.\", \n \"data\": [{\n \"email\":\"abc@def.com\",\n \"accountType\":\"Hacker\"\n }]\n }", + "type": "object" + } + ] }, "filename": "routes/api/account.js", "groupTitle": "Account", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/account/invite" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/account/invite" + } + ] }, { "type": "post", @@ -242,7 +270,8 @@ "description": "

sends link with token to be used with the account/create route

", "parameter": { "fields": { - "body": [{ + "body": [ + { "group": "body", "type": "String", "optional": true, @@ -261,7 +290,8 @@ }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -277,15 +307,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully invited user\", \n \"data\": {}\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Successfully invited user\", \n \"data\": {}\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "string", "optional": false, @@ -301,17 +334,21 @@ } ] }, - "examples": [{ - "title": "Error-Response:", - "content": "{\n \"message\": \"Invalid Authentication\",\n \"data\": {\n \"route\": \"/invite\"\n }\n }", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response:", + "content": "{\n \"message\": \"Invalid Authentication\",\n \"data\": {\n \"route\": \"/invite\"\n }\n }", + "type": "object" + } + ] }, "filename": "routes/api/account.js", "groupTitle": "Account", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/account/invite" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/account/invite" + } + ] }, { "type": "get", @@ -322,7 +359,8 @@ "version": "0.0.8", "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -338,15 +376,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Account found by user email\", \n \"data\": {\n \t\"id\": ObjectId(\"5bff8b9f3274cf001bc71048\"),\n \t\"firstName\": \"Theo\",\n \"lastName\":\"Klein\",\n \"pronoun\":\"he/him\",\n \"email\":\"theo@klein.com\",\n \"dietaryRestrictions\":[\"Halal\"],\n \"phoneNumber\":1234567890,\n \t\"shirtSize\":\"S\",\n \"birthDate\":Date(\"10/30/1997\")\n }\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Account found by user email\", \n \"data\": {\n \t\"id\": ObjectId(\"5bff8b9f3274cf001bc71048\"),\n \t\"firstName\": \"Theo\",\n \"lastName\":\"Klein\",\n \"pronoun\":\"he/him\",\n \"email\":\"theo@klein.com\",\n \"dietaryRestrictions\":[\"Halal\"],\n \"phoneNumber\":1234567890,\n \t\"shirtSize\":\"S\",\n \"birthDate\":Date(\"10/30/1997\")\n }\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "string", "optional": false, @@ -362,17 +403,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Account not found\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Account not found\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/account.js", "groupTitle": "Account", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/account/self" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/account/self" + } + ] }, { "type": "patch", @@ -383,7 +428,8 @@ "version": "0.0.8", "parameter": { "fields": { - "body": [{ + "body": [ + { "group": "body", "type": "String", "optional": true, @@ -441,15 +487,18 @@ } ] }, - "examples": [{ - "title": "Request-Example:", - "content": "{ \"shirtSize\": \"M\" }", - "type": "json" - }] + "examples": [ + { + "title": "Request-Example:", + "content": "{ \"shirtSize\": \"M\" }", + "type": "json" + } + ] }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -465,15 +514,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Changed account information\", \n \"data\": {\n \"id\": ObjectId(\"5bff8b9f3274cf001bc71048\"),\n \t\"firstName\": \"Theo\",\n \"lastName\":\"Klein\",\n \"pronoun\":\"he/him\",\n \"email\":\"theo@klein.com\",\n \"dietaryRestrictions\":[\"Halal\"],\n \"phoneNumber\":1234567890,\n \t\"shirtSize\":\"M\",\n \"birthDate\":Date(\"10/30/1997\")\n }\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Changed account information\", \n \"data\": {\n \"id\": ObjectId(\"5bff8b9f3274cf001bc71048\"),\n \t\"firstName\": \"Theo\",\n \"lastName\":\"Klein\",\n \"pronoun\":\"he/him\",\n \"email\":\"theo@klein.com\",\n \"dietaryRestrictions\":[\"Halal\"],\n \"phoneNumber\":1234567890,\n \t\"shirtSize\":\"M\",\n \"birthDate\":Date(\"10/30/1997\")\n }\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "string", "optional": false, @@ -489,17 +541,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while updating account\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Error while updating account\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/account.js", "groupTitle": "Account", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/account/:id" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/account/:id" + } + ] }, { "type": "patch", @@ -510,7 +566,8 @@ "version": "0.0.8", "parameter": { "fields": { - "Parameter": [{ + "Parameter": [ + { "group": "Parameter", "type": "String", "optional": false, @@ -526,15 +583,18 @@ } ] }, - "examples": [{ - "title": "Request-Example:", - "content": "{ \n \"oldPassword\": \"password12345\",\n \"newPassword\": \"password123456\"\n}", - "type": "json" - }] + "examples": [ + { + "title": "Request-Example:", + "content": "{ \n \"oldPassword\": \"password12345\",\n \"newPassword\": \"password123456\"\n}", + "type": "json" + } + ] }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -550,20 +610,26 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\"message\": \"Successfully reset password\", \"data\": {}}", - "type": "json" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\"message\": \"Successfully reset password\", \"data\": {}}", + "type": "json" + } + ] }, - "permission": [{ - "name": ": Must be logged in" - }], + "permission": [ + { + "name": ": Must be logged in" + } + ], "filename": "routes/api/auth.js", "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/password/change" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/auth/password/change" + } + ] }, { "type": "post", @@ -574,18 +640,21 @@ "version": "0.0.8", "parameter": { "fields": { - "Parameter": [{ - "group": "Parameter", - "type": "String", - "optional": false, - "field": "JWT", - "description": "

for confirming the account

" - }] + "Parameter": [ + { + "group": "Parameter", + "type": "String", + "optional": false, + "field": "JWT", + "description": "

for confirming the account

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -601,15 +670,18 @@ } ] }, - "examples": [{ - "title": "Success-Response:", - "content": "{\"message\": \"Successfully confirmed account\", \"data\": {}}", - "type": "json" - }] + "examples": [ + { + "title": "Success-Response:", + "content": "{\"message\": \"Successfully confirmed account\", \"data\": {}}", + "type": "json" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "string", "optional": false, @@ -625,17 +697,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Invalid token for confirming account, \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Invalid token for confirming account, \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/auth.js", "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/confirm/:token" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/auth/confirm/:token" + } + ] }, { "type": "post", @@ -646,23 +722,28 @@ "version": "0.0.8", "parameter": { "fields": { - "Parameter": [{ - "group": "Parameter", - "type": "String", - "optional": false, - "field": "email", - "description": "

the email address of the account

" - }] + "Parameter": [ + { + "group": "Parameter", + "type": "String", + "optional": false, + "field": "email", + "description": "

the email address of the account

" + } + ] }, - "examples": [{ - "title": "Request-Example:", - "content": "{ \"email\": \"myemail@mchacks.ca\" }", - "type": "json" - }] + "examples": [ + { + "title": "Request-Example:", + "content": "{ \"email\": \"myemail@mchacks.ca\" }", + "type": "json" + } + ] }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -678,20 +759,26 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\"message\": \"Sent reset email\", \"data\": {}}", - "type": "json" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\"message\": \"Sent reset email\", \"data\": {}}", + "type": "json" + } + ] }, - "permission": [{ - "name": ": public" - }], + "permission": [ + { + "name": ": public" + } + ], "filename": "routes/api/auth.js", "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/password/forgot" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/auth/password/forgot" + } + ] }, { "type": "get", @@ -702,18 +789,21 @@ "version": "0.0.8", "parameter": { "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "id", - "description": "

MongoId of an account

" - }] + "param": [ + { + "group": "param", + "type": "ObjectId", + "optional": false, + "field": "id", + "description": "

MongoId of an account

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -729,15 +819,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully retrieved role bindings\",\n \"data\": {\n accountId:\"5beca4ab2e069a34f91697b2\"\n id:\"5beca4ae2e069a34f91698b1\"\n roles: [\n {\n _id:\"5beca4ab2e069a34f91697d9\",\n name:\"hacker\",\n routes: [\n {_id: \"5beca4ae2e069a34f9169852\", requestType: \"POST\", uri: \"/api/auth/login\"},\n {_id: \"5beca4ae2e069a34f9169851\", requestType: \"POST\", uri: \"/api/auth/logout\"},\n {_id: \"5beca4ae2e069a34f9169850\", requestType: \"GET\", uri: \"/api/auth/rolebindings/:self\"},\n {_id: \"5beca4ae2e069a34f916984f\", requestType: \"GET\", uri: \"/api/account/self\"},\n {_id: \"5beca4ae2e069a34f916984e\", requestType: \"GET\", uri: \"/api/account/:self\"},\n {_id: \"5beca4ae2e069a34f916984d\", requestType: \"PATCH\", uri: \"/api/account/:self\"},\n {_id: \"5beca4ae2e069a34f916984c\", requestType: \"POST\", uri: \"/api/hacker/\"},\n {_id: \"5beca4ae2e069a34f916984b\", requestType: \"GET\", uri: \"/api/hacker/:self\"},\n {_id: \"5beca4ae2e069a34f916984a\", requestType: \"GET\", uri: \"/api/hacker/:self/resume\"},\n {_id: \"5beca4ae2e069a34f9169849\", requestType: \"PATCH\", uri: \"/api/hacker/:self\"}\n ]\n }\n ]\n }\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Successfully retrieved role bindings\",\n \"data\": {\n accountId:\"5beca4ab2e069a34f91697b2\"\n id:\"5beca4ae2e069a34f91698b1\"\n roles: [\n {\n _id:\"5beca4ab2e069a34f91697d9\",\n name:\"hacker\",\n routes: [\n {_id: \"5beca4ae2e069a34f9169852\", requestType: \"POST\", uri: \"/api/auth/login\"},\n {_id: \"5beca4ae2e069a34f9169851\", requestType: \"POST\", uri: \"/api/auth/logout\"},\n {_id: \"5beca4ae2e069a34f9169850\", requestType: \"GET\", uri: \"/api/auth/rolebindings/:self\"},\n {_id: \"5beca4ae2e069a34f916984f\", requestType: \"GET\", uri: \"/api/account/self\"},\n {_id: \"5beca4ae2e069a34f916984e\", requestType: \"GET\", uri: \"/api/account/:self\"},\n {_id: \"5beca4ae2e069a34f916984d\", requestType: \"PATCH\", uri: \"/api/account/:self\"},\n {_id: \"5beca4ae2e069a34f916984c\", requestType: \"POST\", uri: \"/api/hacker/\"},\n {_id: \"5beca4ae2e069a34f916984b\", requestType: \"GET\", uri: \"/api/hacker/:self\"},\n {_id: \"5beca4ae2e069a34f916984a\", requestType: \"GET\", uri: \"/api/hacker/:self/resume\"},\n {_id: \"5beca4ae2e069a34f9169849\", requestType: \"PATCH\", uri: \"/api/hacker/:self\"}\n ]\n }\n ]\n }\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "string", "optional": false, @@ -753,17 +846,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Role Bindings not found\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Role Bindings not found\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/auth.js", "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/rolebindings/:id" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/auth/rolebindings/:id" + } + ] }, { "type": "get", @@ -775,7 +872,8 @@ "version": "0.0.8", "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -791,17 +889,21 @@ } ] }, - "examples": [{ - "title": "Success-Response:", - "content": "{\"message\": \"Sucessfully retrieved all roles\", \"data\":\n[{name: \"GodStaff\", routes: Array(27), id: \"5bee20ef3ca9dd4754382880\"},\n {name: \"Hacker\", routes: Array(10), id: \"5bee20ef3ca9dd4754382881\"},\n {name: \"Volunteer\", routes: Array(4), id: \"5bee20ef3ca9dd4754382882\"}]", - "type": "json" - }] + "examples": [ + { + "title": "Success-Response:", + "content": "{\"message\": \"Sucessfully retrieved all roles\", \"data\":\n[{name: \"GodStaff\", routes: Array(27), id: \"5bee20ef3ca9dd4754382880\"},\n {name: \"Hacker\", routes: Array(10), id: \"5bee20ef3ca9dd4754382881\"},\n {name: \"Volunteer\", routes: Array(4), id: \"5bee20ef3ca9dd4754382882\"}]", + "type": "json" + } + ] }, "filename": "routes/api/auth.js", "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/roles" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/auth/roles" + } + ] }, { "type": "post", @@ -812,7 +914,8 @@ "version": "0.0.8", "parameter": { "fields": { - "Parameter": [{ + "Parameter": [ + { "group": "Parameter", "type": "string", "optional": false, @@ -831,7 +934,8 @@ }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -847,15 +951,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\"message\": \"Successfully logged in\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\"message\": \"Successfully logged in\", \"data\": {}}", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "string", "optional": false, @@ -871,20 +978,26 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Invalid Authentication\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Invalid Authentication\", \"data\": {}}", + "type": "object" + } + ] }, - "permission": [{ - "name": ": public" - }], + "permission": [ + { + "name": ": public" + } + ], "filename": "routes/api/auth.js", "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/login" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/auth/login" + } + ] }, { "type": "get", @@ -895,7 +1008,8 @@ "version": "0.0.8", "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -911,20 +1025,26 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\"message\": \"Successfully logged out\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\"message\": \"Successfully logged out\", \"data\": {}}", + "type": "object" + } + ] }, - "permission": [{ - "name": ": public" - }], + "permission": [ + { + "name": ": public" + } + ], "filename": "routes/api/auth.js", "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/logout" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/auth/logout" + } + ] }, { "type": "get", @@ -935,7 +1055,8 @@ "version": "0.0.8", "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -951,15 +1072,18 @@ } ] }, - "examples": [{ - "title": "Success-Response:", - "content": "{\"message\": \"Successfully resent confirmation email\", \"data\": {}}", - "type": "json" - }] + "examples": [ + { + "title": "Success-Response:", + "content": "{\"message\": \"Successfully resent confirmation email\", \"data\": {}}", + "type": "json" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "string", "optional": false, @@ -975,7 +1099,8 @@ } ] }, - "examples": [{ + "examples": [ + { "title": "Error-Response:", "content": " HTTP/1.1 422\n{\"message\": \"Account already confirmed\", \"data\": {}}", "type": "json" @@ -989,9 +1114,11 @@ }, "filename": "routes/api/auth.js", "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/confirm/resend" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/auth/confirm/resend" + } + ] }, { "type": "post", @@ -1002,39 +1129,48 @@ "version": "0.0.8", "parameter": { "fields": { - "Parameter": [{ - "group": "Parameter", - "type": "String", - "optional": false, - "field": "password", - "description": "

the password of the account

" - }] + "Parameter": [ + { + "group": "Parameter", + "type": "String", + "optional": false, + "field": "password", + "description": "

the password of the account

" + } + ] }, - "examples": [{ - "title": "Request-Example:", - "content": "{ \"password\": \"hunter2\" }", - "type": "json" - }] + "examples": [ + { + "title": "Request-Example:", + "content": "{ \"password\": \"hunter2\" }", + "type": "json" + } + ] }, "header": { "fields": { - "Header": [{ - "group": "Header", - "type": "String", - "optional": false, - "field": "Authentication", - "description": "

the token that was provided in the reset password email

" - }] + "Header": [ + { + "group": "Header", + "type": "String", + "optional": false, + "field": "Authentication", + "description": "

the token that was provided in the reset password email

" + } + ] }, - "examples": [{ - "title": "Header-Example:", - "content": "{\n \"X-Reset-Token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c\"\n}", - "type": "json" - }] + "examples": [ + { + "title": "Header-Example:", + "content": "{\n \"X-Reset-Token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c\"\n}", + "type": "json" + } + ] }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -1050,20 +1186,26 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\"message\": \"Successfully reset password\", \"data\": {}}", - "type": "json" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\"message\": \"Successfully reset password\", \"data\": {}}", + "type": "json" + } + ] }, - "permission": [{ - "name": ": must have authentication token" - }], + "permission": [ + { + "name": ": must have authentication token" + } + ], "filename": "routes/api/auth.js", "groupTitle": "Authentication", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/auth/password/reset" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/auth/password/reset" + } + ] }, { "type": "patch", @@ -1074,18 +1216,21 @@ "version": "0.0.9", "parameter": { "fields": { - "body": [{ - "group": "body", - "type": "string", - "optional": true, - "field": "status", - "description": "

Check-in status. "Checked-in"

" - }] + "body": [ + { + "group": "body", + "type": "string", + "optional": true, + "field": "status", + "description": "

Check-in status. "Checked-in"

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -1101,13 +1246,16 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Changed hacker information\", \n \"data\": {\n \"status\": \"Checked-in\"\n }\n}", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Changed hacker information\", \n \"data\": {\n \"status\": \"Checked-in\"\n }\n}", + "type": "object" + } + ] }, - "permission": [{ + "permission": [ + { "name": "Administrator" }, { @@ -1116,9 +1264,11 @@ ], "filename": "routes/api/hacker.js", "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/checkin/:id" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/checkin/:id" + } + ] }, { "type": "post", @@ -1129,7 +1279,8 @@ "version": "0.0.8", "parameter": { "fields": { - "body": [{ + "body": [ + { "group": "body", "type": "MongoID", "optional": false, @@ -1194,15 +1345,18 @@ } ] }, - "examples": [{ - "title": "application: ", - "content": "{\n \"application\":{\n \"portfolioURL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n}", - "type": "Json" - }] + "examples": [ + { + "title": "application: ", + "content": "{\n \"application\":{\n \"URL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n}", + "type": "Json" + } + ] }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -1218,15 +1372,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Hacker creation successful\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"application\":{\n \"portfolioURL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n },\n \"status\":\"Applied\",\n \"ethnicity\":[\"White or Caucasian\",\" Asian or Pacific Islander\"],\n \"accountId\":\"5bff2a35e533b0f6562b4998\",\n \"school\":\"McPherson College\",\n \"gender\":\"Female\",\n \"needsBus\":false,\n \"major\":\"Accounting\",\n \"graduationYear\":2019,\n \"codeOfConduct\":true,\n }\n}", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Hacker creation successful\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"application\":{\n \"URL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n },\n \"status\":\"Applied\",\n \"ethnicity\":[\"White or Caucasian\",\" Asian or Pacific Islander\"],\n \"accountId\":\"5bff2a35e533b0f6562b4998\",\n \"school\":\"McPherson College\",\n \"gender\":\"Female\",\n \"needsBus\":false,\n \"major\":\"Accounting\",\n \"graduationYear\":2019,\n \"codeOfConduct\":true,\n }\n}", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "string", "optional": false, @@ -1242,17 +1399,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while creating hacker\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Error while creating hacker\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/hacker.js", "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/" + } + ] }, { "type": "get", @@ -1263,18 +1424,21 @@ "version": "0.0.8", "parameter": { "fields": { - "param": [{ - "group": "param", - "type": "String", - "optional": false, - "field": "email", - "description": "

a hacker's unique email

" - }] + "param": [ + { + "group": "param", + "type": "String", + "optional": false, + "field": "email", + "description": "

a hacker's unique email

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "String", "optional": false, @@ -1290,15 +1454,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully retrieved hacker information\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"application\":{\n \"portfolioURL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n },\n \"status\":\"Applied\",\n \"ethnicity\":[\"White or Caucasian\",\" Asian or Pacific Islander\"],\n \"accountId\":\"5bff2a35e533b0f6562b4998\",\n \"school\":\"McPherson College\",\n \"gender\":\"Female\",\n \"needsBus\":false,\n \"major\":\"Accounting\",\n \"graduationYear\":2019,\n \"codeOfConduct\":true,\n }\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Successfully retrieved hacker information\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"application\":{\n \"URL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n },\n \"status\":\"Applied\",\n \"ethnicity\":[\"White or Caucasian\",\" Asian or Pacific Islander\"],\n \"accountId\":\"5bff2a35e533b0f6562b4998\",\n \"school\":\"McPherson College\",\n \"gender\":\"Female\",\n \"needsBus\":false,\n \"major\":\"Accounting\",\n \"graduationYear\":2019,\n \"codeOfConduct\":true,\n }\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "String", "optional": false, @@ -1314,17 +1481,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Hacker not found\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Hacker not found\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/hacker.js", "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/email/:email" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/email/:email" + } + ] }, { "type": "get", @@ -1335,18 +1506,21 @@ "version": "0.0.8", "parameter": { "fields": { - "param": [{ - "group": "param", - "type": "String", - "optional": false, - "field": "id", - "description": "

a hacker's unique mongoID

" - }] + "param": [ + { + "group": "param", + "type": "String", + "optional": false, + "field": "id", + "description": "

a hacker's unique mongoID

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "String", "optional": false, @@ -1362,15 +1536,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully retrieved hacker information\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"application\":{\n \"portfolioURL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n },\n \"status\":\"Applied\",\n \"ethnicity\":[\"White or Caucasian\",\" Asian or Pacific Islander\"],\n \"accountId\":\"5bff2a35e533b0f6562b4998\",\n \"school\":\"McPherson College\",\n \"gender\":\"Female\",\n \"needsBus\":false,\n \"major\":\"Accounting\",\n \"graduationYear\":2019,\n \"codeOfConduct\":true,\n }\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Successfully retrieved hacker information\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"application\":{\n \"URL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n },\n \"status\":\"Applied\",\n \"ethnicity\":[\"White or Caucasian\",\" Asian or Pacific Islander\"],\n \"accountId\":\"5bff2a35e533b0f6562b4998\",\n \"school\":\"McPherson College\",\n \"gender\":\"Female\",\n \"needsBus\":false,\n \"major\":\"Accounting\",\n \"graduationYear\":2019,\n \"codeOfConduct\":true,\n }\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "String", "optional": false, @@ -1386,17 +1563,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Hacker not found\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Hacker not found\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/hacker.js", "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/:id" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/:id" + } + ] }, { "type": "get", @@ -1407,50 +1588,62 @@ "version": "0.0.8", "parameter": { "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "id", - "description": "

Hacker id

" - }] + "param": [ + { + "group": "param", + "type": "ObjectId", + "optional": false, + "field": "id", + "description": "

Hacker id

" + } + ] } }, "success": { "fields": { - "Success 200": [{ - "group": "Success 200", - "type": "String", - "optional": false, - "field": "message", - "description": "

Success message

" - }] + "Success 200": [ + { + "group": "Success 200", + "type": "String", + "optional": false, + "field": "message", + "description": "

Success message

" + } + ] }, - "examples": [{ - "title": "Success-Response:", - "content": "HTTP/1.1 200 OK \n{ \n message: \"Downloaded resume\", \n data: { \n id: \"507f191e810c19729de860ea\", \n resume: [Buffer] \n } \n}", - "type": "json" - }] + "examples": [ + { + "title": "Success-Response:", + "content": "HTTP/1.1 200 OK \n{ \n message: \"Downloaded resume\", \n data: { \n id: \"507f191e810c19729de860ea\", \n resume: [Buffer] \n } \n}", + "type": "json" + } + ] }, "error": { "fields": { - "Error 4xx": [{ - "group": "Error 4xx", - "type": "String", - "optional": false, - "field": "message", - "description": "

"Resume does not exist"

" - }] - }, - "examples": [{ - "title": "Error-Response:", - "content": "HTTP/1.1 404 \n{ \n message: \"Resume not found\", \n data: {} \n}", - "type": "json" - }] - }, - "permission": [{ - "name": "Must be logged in, and the account id must be linked to the hacker." - }], + "Error 4xx": [ + { + "group": "Error 4xx", + "type": "String", + "optional": false, + "field": "message", + "description": "

"Resume does not exist"

" + } + ] + }, + "examples": [ + { + "title": "Error-Response:", + "content": "HTTP/1.1 404 \n{ \n message: \"Resume not found\", \n data: {} \n}", + "type": "json" + } + ] + }, + "permission": [ + { + "name": "Must be logged in, and the account id must be linked to the hacker." + } + ], "filename": "routes/api/hacker.js", "groupTitle": "Hacker" }, @@ -1463,7 +1656,8 @@ "version": "0.0.9", "parameter": { "fields": { - "query": [{ + "query": [ + { "group": "query", "type": "String", "optional": false, @@ -1482,7 +1676,8 @@ }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -1498,17 +1693,21 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Retrieved stats\",\n \"data\": {\n \"stats\" : {\n \"total\": 10,\n \"status\": { \"Applied\": 10 },\n \"school\": { \"McGill University\": 3, \"Harvard University\": 7 },\n degree: { \"Undergraduate\": 10 },\n gender: { \"Male\": 1, \"Female\": 9 },\n needsBus: { \"true\": 7, \"false\": 3 },\n ethnicity: { \"White\": 10, },\n jobInterest: { \"Internship\": 10 },\n major: { \"Computer Science\": 10 },\n graduationYear: { \"2019\": 10 },\n dietaryRestrictions: { \"None\": 10 },\n shirtSize: { \"M\": 3, \"XL\": 7 },\n age: { \"22\": 10 }\n }\n }\n}", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Retrieved stats\",\n \"data\": {\n \"stats\" : {\n \"total\": 10,\n \"status\": { \"Applied\": 10 },\n \"school\": { \"McGill University\": 3, \"Harvard University\": 7 },\n degree: { \"Undergraduate\": 10 },\n gender: { \"Male\": 1, \"Female\": 9 },\n needsBus: { \"true\": 7, \"false\": 3 },\n ethnicity: { \"White\": 10, },\n jobInterest: { \"Internship\": 10 },\n major: { \"Computer Science\": 10 },\n graduationYear: { \"2019\": 10 },\n dietaryRestrictions: { \"None\": 10 },\n shirtSize: { \"M\": 3, \"XL\": 7 },\n age: { \"22\": 10 }\n }\n }\n}", + "type": "object" + } + ] }, "filename": "routes/api/hacker.js", "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/stats" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/stats" + } + ] }, { "type": "patch", @@ -1520,7 +1719,8 @@ "version": "0.0.8", "parameter": { "fields": { - "body": [{ + "body": [ + { "group": "body", "type": "String", "optional": true, @@ -1571,15 +1771,18 @@ } ] }, - "examples": [{ - "title": "application: ", - "content": "{\n \"portfolioURL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n }", - "type": "Json" - }] + "examples": [ + { + "title": "application: ", + "content": "{\n \"URL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n }", + "type": "Json" + } + ] }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -1595,15 +1798,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Changed hacker information\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"application\":{\n \"portfolioURL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n },\n \"status\":\"Applied\",\n \"ethnicity\":[\"White or Caucasian\",\" Asian or Pacific Islander\"],\n \"accountId\":\"5bff2a35e533b0f6562b4998\",\n \"school\":\"McPherson College\",\n \"gender\":\"Female\",\n \"needsBus\":false,\n \"major\":\"Accounting\",\n \"graduationYear\":2019,\n \"codeOfConduct\":true,\n}", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Changed hacker information\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"application\":{\n \"URL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n },\n \"status\":\"Applied\",\n \"ethnicity\":[\"White or Caucasian\",\" Asian or Pacific Islander\"],\n \"accountId\":\"5bff2a35e533b0f6562b4998\",\n \"school\":\"McPherson College\",\n \"gender\":\"Female\",\n \"needsBus\":false,\n \"major\":\"Accounting\",\n \"graduationYear\":2019,\n \"codeOfConduct\":true,\n}", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "string", "optional": false, @@ -1619,17 +1825,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while updating hacker\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Error while updating hacker\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/hacker.js", "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/:id" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/:id" + } + ] }, { "type": "patch", @@ -1640,18 +1850,21 @@ "version": "0.0.9", "parameter": { "fields": { - "body": [{ - "group": "body", - "type": "string", - "optional": true, - "field": "status", - "description": "

The new status of the hacker. "Accepted", "Confirmed", or "Cancelled"

" - }] + "body": [ + { + "group": "body", + "type": "string", + "optional": true, + "field": "status", + "description": "

The new status of the hacker. "Accepted", "Confirmed", or "Cancelled"

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -1667,13 +1880,16 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Changed hacker information\", \n \"data\": {\n \"status\": \"Confirmed\"\n }\n}", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Changed hacker information\", \n \"data\": {\n \"status\": \"Confirmed\"\n }\n}", + "type": "object" + } + ] }, - "permission": [{ + "permission": [ + { "name": "Administrator" }, { @@ -1682,9 +1898,11 @@ ], "filename": "routes/api/hacker.js", "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/confirmation/:id" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/confirmation/:id" + } + ] }, { "type": "patch", @@ -1695,18 +1913,21 @@ "version": "0.0.9", "parameter": { "fields": { - "body": [{ - "group": "body", - "type": "string", - "optional": true, - "field": "status", - "description": "

Status of the hacker's application ("None"|"Applied"|"Waitlisted"|"Confirmed"|"Cancelled"|"Checked-in")

" - }] + "body": [ + { + "group": "body", + "type": "string", + "optional": true, + "field": "status", + "description": "

Status of the hacker's application ("None"|"Applied"|"Waitlisted"|"Confirmed"|"Cancelled"|"Checked-in")

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -1722,20 +1943,26 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Changed hacker information\", \n \"data\": {\n \"status\": \"Accepted\"\n }\n}", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Changed hacker information\", \n \"data\": {\n \"status\": \"Accepted\"\n }\n}", + "type": "object" + } + ] }, - "permission": [{ - "name": "Administrator" - }], + "permission": [ + { + "name": "Administrator" + } + ], "filename": "routes/api/hacker.js", "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/status/:id" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/status/:id" + } + ] }, { "type": "post", @@ -1747,25 +1974,30 @@ "description": "

NOTE: This must be sent via multipart/form-data POST request

", "parameter": { "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "id", - "description": "

Hacker id

" - }], - "body": [{ - "group": "body", - "type": "File", - "optional": false, - "field": "resume", - "description": "

The uploaded file.

" - }] + "param": [ + { + "group": "param", + "type": "ObjectId", + "optional": false, + "field": "id", + "description": "

Hacker id

" + } + ], + "body": [ + { + "group": "body", + "type": "File", + "optional": false, + "field": "resume", + "description": "

The uploaded file.

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "String", "optional": false, @@ -1781,20 +2013,26 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "HTTP/1.1 200 OK\n{\n message: \"Uploaded resume\", \n data: {\n filename: \"resumes/1535032624768-507f191e810c19729de860ea\"\n }\n}", - "type": "json" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "HTTP/1.1 200 OK\n{\n message: \"Uploaded resume\", \n data: {\n filename: \"resumes/1535032624768-507f191e810c19729de860ea\"\n }\n}", + "type": "json" + } + ] }, - "permission": [{ - "name": "Must be logged in, and the account id must be linked to the hacker." - }], + "permission": [ + { + "name": "Must be logged in, and the account id must be linked to the hacker." + } + ], "filename": "routes/api/hacker.js", "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/resume/:id" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/resume/:id" + } + ] }, { "type": "post", @@ -1806,18 +2044,21 @@ "version": "0.0.9", "parameter": { "fields": { - "param": [{ - "group": "param", - "type": "string", - "optional": true, - "field": "status", - "description": "

The hacker ID

" - }] + "param": [ + { + "group": "param", + "type": "string", + "optional": true, + "field": "status", + "description": "

The hacker ID

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -1833,20 +2074,26 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Hacker week-of email sent.\", \n \"data\": {}\n}", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Hacker week-of email sent.\", \n \"data\": {}\n}", + "type": "object" + } + ] }, - "permission": [{ - "name": "Administrator" - }], + "permission": [ + { + "name": "Administrator" + } + ], "filename": "routes/api/hacker.js", "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/email/weekOf/:id" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/email/weekOf/:id" + } + ] }, { "type": "post", @@ -1858,18 +2105,21 @@ "version": "0.0.9", "parameter": { "fields": { - "param": [{ - "group": "param", - "type": "string", - "optional": true, - "field": "status", - "description": "

The hacker ID

" - }] + "param": [ + { + "group": "param", + "type": "string", + "optional": true, + "field": "status", + "description": "

The hacker ID

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -1885,20 +2135,26 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Hacker week-of email sent.\", \n \"data\": {}\n}", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Hacker week-of email sent.\", \n \"data\": {}\n}", + "type": "object" + } + ] }, - "permission": [{ - "name": "Administrator" - }], + "permission": [ + { + "name": "Administrator" + } + ], "filename": "routes/api/hacker.js", "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/email/weekOf/:id" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/email/weekOf/:id" + } + ] }, { "type": "get", @@ -1909,7 +2165,8 @@ "version": "1.4.1", "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "String", "optional": false, @@ -1925,15 +2182,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully retrieved sponsor information\", \n \"data\": {\n \"id\": \"5bff4d736f86be0a41badb91\",\n \"accountId\": \"5bff4d736f86be0a41badb99\",\n \"tier\": 3,\n \"company\": \"companyName\",\n \"contractURL\": \"https://www.contractHere.com\",\n \"nominees\": [\"5bff4d736f86be0a41badb93\",\"5bff4d736f86be0a41badb94\"]\n }\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Successfully retrieved sponsor information\", \n \"data\": {\n \"id\": \"5bff4d736f86be0a41badb91\",\n \"accountId\": \"5bff4d736f86be0a41badb99\",\n \"tier\": 3,\n \"company\": \"companyName\",\n \"contractURL\": \"https://www.contractHere.com\",\n \"nominees\": [\"5bff4d736f86be0a41badb93\",\"5bff4d736f86be0a41badb94\"]\n }\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "String", "optional": false, @@ -1949,20 +2209,26 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Sponsor not found\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Sponsor not found\", \"data\": {}}", + "type": "object" + } + ] }, - "permission": [{ - "name": ": Sponsor" - }], + "permission": [ + { + "name": ": Sponsor" + } + ], "filename": "routes/api/sponsor.js", "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/sponsor/self" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/sponsor/self" + } + ] }, { "type": "get", @@ -1973,7 +2239,8 @@ "version": "0.0.8", "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -1989,15 +2256,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Hacker found by logged in account id\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"application\":{\n \"portfolioURL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n },\n \"status\":\"Applied\",\n \"ethnicity\":[\"White or Caucasian\",\" Asian or Pacific Islander\"],\n \"accountId\":\"5bff2a35e533b0f6562b4998\",\n \"school\":\"McPherson College\",\n \"gender\":\"Female\",\n \"needsBus\":false,\n \"major\":[\"Accounting\"],\n \"graduationYear\":2019,\n \"codeOfConduct\":true,\n } \n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Hacker found by logged in account id\", \n \"data\": {\n \"id\":\"5bff4d736f86be0a41badb91\",\n \"application\":{\n \"URL\":{\n \"resume\":\"resumes/1543458163426-5bff4d736f86be0a41badb91\",\n \"github\":\"https://github.com/abcd\",\n \"dropler\":\"https://dribbble.com/abcd\",\n \"personal\":\"https://www.hi.com/\",\n \"linkedIn\":\"https://linkedin.com/in/abcd\",\n \"other\":\"https://github.com/hackmcgill/hackerAPI/issues/168\"\n },\n \"jobInterest\":\"Internship\",\n \"skills\":[\"Javascript\",\"Typescript\"],\n \"comments\":\"hi!\",\n \"essay\":\"Pls accept me\"\n },\n \"status\":\"Applied\",\n \"ethnicity\":[\"White or Caucasian\",\" Asian or Pacific Islander\"],\n \"accountId\":\"5bff2a35e533b0f6562b4998\",\n \"school\":\"McPherson College\",\n \"gender\":\"Female\",\n \"needsBus\":false,\n \"major\":[\"Accounting\"],\n \"graduationYear\":2019,\n \"codeOfConduct\":true,\n } \n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "string", "optional": false, @@ -2013,17 +2283,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Hacker not found\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Hacker not found\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/hacker.js", "groupTitle": "Hacker", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/hacker/self" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/hacker/self" + } + ] }, { "type": "get", @@ -2032,14 +2306,18 @@ "version": "0.0.8", "name": "index", "group": "Index", - "permission": [{ - "name": "public" - }], + "permission": [ + { + "name": "public" + } + ], "filename": "routes/index.js", "groupTitle": "Index", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/" + } + ] }, { "type": "post", @@ -2050,7 +2328,8 @@ "version": "1.1.1", "parameter": { "fields": { - "body": [{ + "body": [ + { "group": "body", "type": "String", "optional": false, @@ -2066,15 +2345,18 @@ } ] }, - "examples": [{ - "title": "application: ", - "content": "{\n \"name\": \"routename\",\n \"routes\": [\n {\n uri: \"/api/hacker/\"\n requestType: \"POST\"\n }\n ]\n}", - "type": "Json" - }] + "examples": [ + { + "title": "application: ", + "content": "{\n \"name\": \"routename\",\n \"routes\": [\n {\n uri: \"/api/hacker/\"\n requestType: \"POST\"\n }\n ]\n}", + "type": "Json" + } + ] }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -2090,15 +2372,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Role creation successful\", \n \"data\": {\n \"name\": \"routename\",\n \"routes\": [\n {\n uri: \"/api/hacker/\"\n requestType: \"POST\"\n }\n ]\n }\n}", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Role creation successful\", \n \"data\": {\n \"name\": \"routename\",\n \"routes\": [\n {\n uri: \"/api/hacker/\"\n requestType: \"POST\"\n }\n ]\n }\n}", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "string", "optional": false, @@ -2114,17 +2399,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while creating role\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Error while creating role\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/role.js", "groupTitle": "Role", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/api/role/" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/api/role/" + } + ] }, { "type": "get", @@ -2135,7 +2424,8 @@ "version": "0.0.8", "parameter": { "fields": { - "query": [{ + "query": [ + { "group": "query", "type": "String", "optional": false, @@ -2189,7 +2479,8 @@ }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "String", "optional": false, @@ -2205,7 +2496,8 @@ } ] }, - "examples": [{ + "examples": [ + { "title": "Success-Response:", "content": "{\n \"message\": \"Successfully executed query, returning all results\",\n \"data\": [\n {...}\n ]\n }", "type": "object" @@ -2219,7 +2511,8 @@ }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "String", "optional": false, @@ -2235,17 +2528,21 @@ } ] }, - "examples": [{ - "title": "Error-Response:", - "content": "{\"message\": \"Validation failed\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response:", + "content": "{\"message\": \"Validation failed\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/search.js", "groupTitle": "Search", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/search/" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/search/" + } + ] }, { "type": "get", @@ -2256,7 +2553,8 @@ "version": "1.1.1", "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -2272,20 +2570,26 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Settings creation successful.\", \n \"data\": {\n \"settings\": {\n openTime: \"Wed Feb 06 2019 00:00:00 GMT-0500 (GMT-05:00)\",\n closeTime: \"Sat Feb 01 2020 00:00:00 GMT-0500 (GMT-05:00)\",\n confirmTime: \"Sat Feb 20 2020 00:00:00 GMT-0500 (GMT-05:00)\"\n }\n }\n}", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Settings creation successful.\", \n \"data\": {\n \"settings\": {\n openTime: \"Wed Feb 06 2019 00:00:00 GMT-0500 (GMT-05:00)\",\n closeTime: \"Sat Feb 01 2020 00:00:00 GMT-0500 (GMT-05:00)\",\n confirmTime: \"Sat Feb 20 2020 00:00:00 GMT-0500 (GMT-05:00)\"\n }\n }\n}", + "type": "object" + } + ] }, - "permission": [{ - "name": "public" - }], + "permission": [ + { + "name": "public" + } + ], "filename": "routes/api/settings.js", "groupTitle": "Settings", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/settings/" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/settings/" + } + ] }, { "type": "patch", @@ -2296,7 +2600,8 @@ "version": "1.1.1", "parameter": { "fields": { - "body": [{ + "body": [ + { "group": "body", "type": "Date", "optional": true, @@ -2322,7 +2627,8 @@ }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -2338,20 +2644,26 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Settings patch successful.\", \n \"data\": {\n \"settings\": {\n openTime: \"Wed Feb 06 2019 00:00:00 GMT-0500 (GMT-05:00)\",\n closeTime: \"Sat Feb 01 2020 00:00:00 GMT-0500 (GMT-05:00)\",\n confirmTime: \"Sat Feb 20 2020 00:00:00 GMT-0500 (GMT-05:00)\"\n }\n }\n}", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Settings patch successful.\", \n \"data\": {\n \"settings\": {\n openTime: \"Wed Feb 06 2019 00:00:00 GMT-0500 (GMT-05:00)\",\n closeTime: \"Sat Feb 01 2020 00:00:00 GMT-0500 (GMT-05:00)\",\n confirmTime: \"Sat Feb 20 2020 00:00:00 GMT-0500 (GMT-05:00)\"\n }\n }\n}", + "type": "object" + } + ] }, - "permission": [{ - "name": "Administrators" - }], + "permission": [ + { + "name": "Administrators" + } + ], "filename": "routes/api/settings.js", "groupTitle": "Settings", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/settings/" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/settings/" + } + ] }, { "type": "post", @@ -2362,7 +2674,8 @@ "version": "0.0.8", "parameter": { "fields": { - "body": [{ + "body": [ + { "group": "body", "type": "MongoID", "optional": false, @@ -2402,7 +2715,8 @@ }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "String", "optional": false, @@ -2418,15 +2732,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Sponsor creation successful\", \n \"data\": {...}\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Sponsor creation successful\", \n \"data\": {...}\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "String", "optional": false, @@ -2442,17 +2759,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while creating sponsor\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Error while creating sponsor\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/sponsor.js", "groupTitle": "Sponsor", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/sponsor/" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/sponsor/" + } + ] }, { "type": "get", @@ -2463,18 +2784,21 @@ "version": "0.0.8", "parameter": { "fields": { - "param": [{ - "group": "param", - "type": "string", - "optional": false, - "field": "id", - "description": "

a sponsor's unique mongoID

" - }] + "param": [ + { + "group": "param", + "type": "string", + "optional": false, + "field": "id", + "description": "

a sponsor's unique mongoID

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "String", "optional": false, @@ -2490,15 +2814,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully retrieved sponsor information\", \n \"data\": {\n \"id\": \"5bff4d736f86be0a41badb91\",\n \"accountId\": \"5bff4d736f86be0a41badb99\",\n \"tier\": 3,\n \"company\": \"companyName\",\n \"contractURL\": \"https://www.contractHere.com\",\n \"nominees\": [\"5bff4d736f86be0a41badb93\",\"5bff4d736f86be0a41badb94\"]\n }\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Successfully retrieved sponsor information\", \n \"data\": {\n \"id\": \"5bff4d736f86be0a41badb91\",\n \"accountId\": \"5bff4d736f86be0a41badb99\",\n \"tier\": 3,\n \"company\": \"companyName\",\n \"contractURL\": \"https://www.contractHere.com\",\n \"nominees\": [\"5bff4d736f86be0a41badb93\",\"5bff4d736f86be0a41badb94\"]\n }\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "String", "optional": false, @@ -2514,17 +2841,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Sponsor not found\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Sponsor not found\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/sponsor.js", "groupTitle": "Sponsor", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/sponsor/:id" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/sponsor/:id" + } + ] }, { "type": "patch", @@ -2535,14 +2866,17 @@ "version": "1.3.0", "parameter": { "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "id", - "description": "

ObjectID of the sponsor

" - }], - "body": [{ + "param": [ + { + "group": "param", + "type": "ObjectId", + "optional": false, + "field": "id", + "description": "

ObjectID of the sponsor

" + } + ], + "body": [ + { "group": "body", "type": "String", "optional": false, @@ -2568,7 +2902,8 @@ }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "String", "optional": false, @@ -2584,15 +2919,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Sponsor update successful\", \n \"data\": {...}\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Sponsor update successful\", \n \"data\": {...}\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "String", "optional": false, @@ -2608,17 +2946,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while updating sponsor\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Error while updating sponsor\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/sponsor.js", "groupTitle": "Sponsor", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/sponsor/" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/sponsor/" + } + ] }, { "type": "post", @@ -2629,7 +2971,8 @@ "version": "0.0.8", "parameter": { "fields": { - "body": [{ + "body": [ + { "group": "body", "type": "String", "optional": false, @@ -2655,7 +2998,8 @@ }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -2671,15 +3015,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Team creation successful\", \n \"data\": {...}\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Team creation successful\", \n \"data\": {...}\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "string", "optional": false, @@ -2695,17 +3042,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while creating team\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Error while creating team\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/team.js", "groupTitle": "Team", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/team/" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/team/" + } + ] }, { "type": "patch", @@ -2716,7 +3067,8 @@ "version": "1.1.1", "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -2732,17 +3084,21 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Removal from team successful.\", \n \"data\": {}\n}", - "type": "object" - }] + "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/" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/team/leave/" + } + ] }, { "type": "get", @@ -2753,18 +3109,21 @@ "version": "0.0.8", "parameter": { "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "id", - "description": "

MongoId of the team

" - }] + "param": [ + { + "group": "param", + "type": "ObjectId", + "optional": false, + "field": "id", + "description": "

MongoId of the team

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "String", "optional": false, @@ -2780,15 +3139,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Team retrieval successful\", \n \"data\": { \n \"team\": {\n \"name\":\"foo\",\n \"members\": [\n ObjectId('...')\n ],\n \"devpostURL\": \"www.devpost.com/foo\",\n \"projectName\": \"fooey\"\n },\n \"members\": [\n {\n \"firstName\": \"John\",\n \"lastName\": \"Doe\"\n }\n ],\n }\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Team retrieval successful\", \n \"data\": { \n \"team\": {\n \"name\":\"foo\",\n \"members\": [\n ObjectId('...')\n ],\n \"devpostURL\": \"www.devpost.com/foo\",\n \"projectName\": \"fooey\"\n },\n \"members\": [\n {\n \"firstName\": \"John\",\n \"lastName\": \"Doe\"\n }\n ],\n }\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "String", "optional": false, @@ -2804,17 +3166,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Team not found\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Team not found\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/team.js", "groupTitle": "Team", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/team/:id" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/team/:id" + } + ] }, { "type": "patch", @@ -2825,18 +3191,21 @@ "version": "1.1.1", "parameter": { "fields": { - "body": [{ - "group": "body", - "type": "string", - "optional": true, - "field": "name", - "description": "

Name of the team to join

" - }] + "body": [ + { + "group": "body", + "type": "string", + "optional": true, + "field": "name", + "description": "

Name of the team to join

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -2852,17 +3221,21 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Team join successful.\", \n \"data\": {}\n}", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Team join successful.\", \n \"data\": {}\n}", + "type": "object" + } + ] }, "filename": "routes/api/team.js", "groupTitle": "Team", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/team/join/" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/team/join/" + } + ] }, { "type": "patch", @@ -2874,18 +3247,21 @@ "description": "

We use hackerId instead of teamId because authorization requires a one-to-one mapping from param id to accountId, but we are not able to have that from teamId to accountId due to multiple members in a team. Instead, we use hackerId, as there is a 1 to 1 link between hackerId to teamId, and a 1 to 1 link between hackerId and accountId

", "parameter": { "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "hackerId", - "description": "

a hacker's unique Id

" - }] + "param": [ + { + "group": "param", + "type": "ObjectId", + "optional": false, + "field": "hackerId", + "description": "

a hacker's unique Id

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "String", "optional": false, @@ -2901,15 +3277,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Team update successful.\", \n \"data\": {...}\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Team update successful.\", \n \"data\": {...}\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "String", "optional": false, @@ -2925,17 +3304,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Team not found\", \"data\": {teamId}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Team not found\", \"data\": {teamId}}", + "type": "object" + } + ] }, "filename": "routes/api/team.js", "groupTitle": "Team", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/team/:hackerId" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/team/:hackerId" + } + ] }, { "type": "post", @@ -2946,18 +3329,21 @@ "version": "0.0.8", "parameter": { "fields": { - "body": [{ - "group": "body", - "type": "MongoID", - "optional": false, - "field": "accountId", - "description": "

MongoID of the account of the volunteer

" - }] + "body": [ + { + "group": "body", + "type": "MongoID", + "optional": false, + "field": "accountId", + "description": "

MongoID of the account of the volunteer

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "string", "optional": false, @@ -2973,15 +3359,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Volunteer creation successful\", \n \"data\": {...}\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Volunteer creation successful\", \n \"data\": {...}\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "string", "optional": false, @@ -2997,17 +3386,21 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Error while creating volunteer\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Error while creating volunteer\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/volunteer.js", "groupTitle": "Volunteer", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/volunteer/" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/volunteer/" + } + ] }, { "type": "get", @@ -3018,18 +3411,21 @@ "version": "1.3.0", "parameter": { "fields": { - "param": [{ - "group": "param", - "type": "ObjectId", - "optional": false, - "field": "id", - "description": "

a volunteer's unique mongoID

" - }] + "param": [ + { + "group": "param", + "type": "ObjectId", + "optional": false, + "field": "id", + "description": "

a volunteer's unique mongoID

" + } + ] } }, "success": { "fields": { - "Success 200": [{ + "Success 200": [ + { "group": "Success 200", "type": "String", "optional": false, @@ -3045,15 +3441,18 @@ } ] }, - "examples": [{ - "title": "Success-Response: ", - "content": "{\n \"message\": \"Successfully retrieved volunteer information\", \n \"data\": {...}\n }", - "type": "object" - }] + "examples": [ + { + "title": "Success-Response: ", + "content": "{\n \"message\": \"Successfully retrieved volunteer information\", \n \"data\": {...}\n }", + "type": "object" + } + ] }, "error": { "fields": { - "Error 4xx": [{ + "Error 4xx": [ + { "group": "Error 4xx", "type": "String", "optional": false, @@ -3069,16 +3468,20 @@ } ] }, - "examples": [{ - "title": "Error-Response: ", - "content": "{\"message\": \"Volunteer not found\", \"data\": {}}", - "type": "object" - }] + "examples": [ + { + "title": "Error-Response: ", + "content": "{\"message\": \"Volunteer not found\", \"data\": {}}", + "type": "object" + } + ] }, "filename": "routes/api/volunteer.js", "groupTitle": "Volunteer", - "sampleRequest": [{ - "url": "https://api.mchacks.ca/api/volunteer/:id" - }] + "sampleRequest": [ + { + "url": "https://api.mchacks.ca/api/volunteer/:id" + } + ] } -] \ No newline at end of file +] diff --git a/middlewares/auth.middleware.js b/middlewares/auth.middleware.js index 2b42f181..9fda8800 100644 --- a/middlewares/auth.middleware.js +++ b/middlewares/auth.middleware.js @@ -3,61 +3,60 @@ const jwt = require("jsonwebtoken"); const passport = require("passport"); const Services = { - Auth: require("../services/auth.service"), - ResetPasswordToken: require("../services/resetPassword.service"), - Account: require("../services/account.service"), - Email: require("../services/email.service"), - AccountConfirmation: require("../services/accountConfirmation.service"), - Role: require("../services/role.service"), - RoleBinding: require("../services/roleBinding.service"), - Env: require("../services/env.service") + Auth: require("../services/auth.service"), + ResetPasswordToken: require("../services/resetPassword.service"), + Account: require("../services/account.service"), + Email: require("../services/email.service"), + AccountConfirmation: require("../services/accountConfirmation.service"), + Role: require("../services/role.service"), + RoleBinding: require("../services/roleBinding.service"), + Env: require("../services/env.service") }; const Middleware = { - Util: require("./util.middleware") + Util: require("./util.middleware") }; const Constants = { - General: require("../constants/general.constant"), - Error: require("../constants/error.constant"), - Role: require("../constants/role.constant") + General: require("../constants/general.constant"), + Error: require("../constants/error.constant"), + Role: require("../constants/role.constant") }; /** * @param {*} req * @param {*} res - * @param {(err?)=>void} next - * Calls passport.authenticate with a custom error handler. Errors during authentication will return res with a generic 500 error, + * @param {(err?)=>void} next + * Calls passport.authenticate with a custom error handler. Errors during authentication will return res with a generic 500 error, * Failed authentication returns a AUTH 401 error, and errors during login will return res with a LOGIN 500 error. */ function login(req, res, next) { - passport.authenticate("emailAndPass", - function (err, user) { - if (err) { - return next({ - status: 500, - message: Constants.Error.GENERIC_500_MESSAGE, - error: {} - }); - } - if (!user) { - return next({ - status: 401, - message: Constants.Error.AUTH_401_MESSAGE, - error: {} - }); - } - req.login(user, (loginErr) => { - if (loginErr) { - return next({ - status: 500, - message: Constants.Error.LOGIN_500_MESSAGE, - error: {} - }); - } - return next(); - }); - })(req, res, next); + passport.authenticate("emailAndPass", function(err, user) { + if (err) { + return next({ + status: 500, + message: Constants.Error.GENERIC_500_MESSAGE, + error: {} + }); + } + if (!user) { + return next({ + status: 401, + message: Constants.Error.AUTH_401_MESSAGE, + error: {} + }); + } + req.login(user, loginErr => { + if (loginErr) { + return next({ + status: 500, + message: Constants.Error.LOGIN_500_MESSAGE, + error: {} + }); + } + return next(); + }); + })(req, res, next); } /** @@ -65,19 +64,19 @@ function login(req, res, next) { * Calls next() if the user is properly authenticated. */ function ensureAuthenticated() { - return function (req, res, next) { - if (req.isUnauthenticated()) { - return next({ - status: 401, - message: Constants.Error.AUTH_401_MESSAGE, - error: { - route: req.path - } - }); - } else { - return next(); + return function(req, res, next) { + if (req.isUnauthenticated()) { + return next({ + status: 401, + message: Constants.Error.AUTH_401_MESSAGE, + error: { + route: req.path } - }; + }); + } else { + return next(); + } + }; } /** @@ -86,101 +85,120 @@ function ensureAuthenticated() { * Calls next() if the user is properly authorized. */ function ensureAuthorized(findByIdFns) { - return function (req, res, next) { - Services.Auth.ensureAuthorized(req, findByIdFns).then( - (auth) => { - if (!auth) { - return next({ - status: 403, - message: Constants.Error.AUTH_403_MESSAGE, - error: { - route: req.path - } - }); - } else { - return next(); - } - }, - (err) => { - return next(err); + return function(req, res, next) { + Services.Auth.ensureAuthorized(req, findByIdFns).then( + auth => { + if (!auth) { + return next({ + status: 403, + message: Constants.Error.AUTH_403_MESSAGE, + error: { + route: req.path } - ); - }; + }); + } else { + return next(); + } + }, + err => { + return next(err); + } + ); + }; } /** * Middleware which retrieves the rolebindings for an account - * @param {{body: {param: {id:string}}}} req - * @param {*} res - * @param {(err?)=>void} next + * @param {{body: {param: {id:string}}}} req + * @param {*} res + * @param {(err?)=>void} next */ async function retrieveRoleBindings(req, res, next) { - const roleBindings = await Services.RoleBinding.getRoleBindingForAcct(req.params.id); - if (!roleBindings) { - return next({ - status: 404, - message: "Role Bindings not found" - }) - } - req.roleBindings = roleBindings; - return next(); + const roleBindings = await Services.RoleBinding.getRoleBindingForAcct( + req.params.id + ); + if (!roleBindings) { + return next({ + status: 404, + message: "Role Bindings not found" + }); + } + req.roleBindings = roleBindings; + return next(); } /** * Checks that the oldPassword is the current password for the logged in user. If the password is correct, * then updates the password to the string in newPassword. - * @param {{user: {email: string}, body: {oldPassword: string, newPassword: string}} req - * @param {*} res - * @param {*} next + * @param {{user: {email: string}, body: {oldPassword: string, newPassword: string}} req + * @param {*} res + * @param {*} next */ async function changePassword(req, res, next) { - const acc = await Services.Account.getAccountIfValid(req.user.email, req.body.oldPassword); - // user's old password is correct - if (!!acc) { - req.body.account = await Services.Account.updatePassword(req.user.id, req.body.newPassword); - return next(); - } else { - return next({ - status: 401, - message: Constants.Error.AUTH_401_MESSAGE, - }); - } + const acc = await Services.Account.getAccountIfValid( + req.user.email, + req.body.oldPassword + ); + // user's old password is correct + if (!!acc) { + req.body.account = await Services.Account.updatePassword( + req.user.id, + req.body.newPassword + ); + return next(); + } else { + return next({ + status: 401, + message: Constants.Error.AUTH_401_MESSAGE + }); + } } /** * Middleware that sends an email to reset the password for the inputted email address. * @param {{body: {email:String}}} req the request object - * @param {*} res - * @param {(err?)=>void} next + * @param {*} res + * @param {(err?)=>void} next */ async function sendResetPasswordEmailMiddleware(req, res, next) { - const user = await Services.Account.findByEmail(req.body.email); - if (user) { - //create the reset password token - await Services.ResetPasswordToken.create(user.id); - //find the thing we just created - const ResetPasswordTokenModel = await Services.ResetPasswordToken.findByAccountId(user.id); - //generate email - const token = Services.ResetPasswordToken.generateToken(ResetPasswordTokenModel.id, user.id); - const address = Services.Env.isProduction() ? process.env.FRONTEND_ADDRESS_DEPLOY : process.env.FRONTEND_ADDRESS_DEV; - const mailData = Services.ResetPasswordToken.generateResetPasswordEmail(address, req.body.email, token); - if (mailData !== undefined) { - Services.Email.send(mailData, (err) => { - if (err) { - return next(err); - } else { - return next(); - } - }); + const user = await Services.Account.findByEmail(req.body.email); + if (user) { + //create the reset password token + await Services.ResetPasswordToken.create(user.id); + //find the thing we just created + const ResetPasswordTokenModel = await Services.ResetPasswordToken.findByAccountId( + user.id + ); + //generate email + const token = Services.ResetPasswordToken.generateToken( + ResetPasswordTokenModel.id, + user.id + ); + const address = Services.Env.isProduction() + ? process.env.FRONTEND_ADDRESS_DEPLOY + : process.env.FRONTEND_ADDRESS_DEV; + const mailData = Services.ResetPasswordToken.generateResetPasswordEmail( + address, + req.body.email, + token + ); + if (mailData !== undefined) { + Services.Email.send(mailData, err => { + if (err) { + return next(err); } else { - return next({ - message: Constants.Error.EMAIL_500_MESSAGE, - }); + return next(); } + }); } else { - //Didn't find the user, but we don't want to throw an error because someone might be trying to see who has an account. - return next(); + return next({ + message: Constants.Error.EMAIL_500_MESSAGE + }); } + } else { + //Didn't find the user, but we don't want to throw an error because someone might be trying to see who has an account. + return next(); + } } /** @@ -192,28 +210,44 @@ async function sendResetPasswordEmailMiddleware(req, res, next) { * @param {(err?)=>void} next */ async function sendConfirmAccountEmailMiddleware(req, res, next) { - const account = req.body.account; - if (account.confirmed) { + const account = req.body.account; + if (account.confirmed) { + return next(); + } + await Services.AccountConfirmation.create( + Constants.General.HACKER, + account.email, + account.id + ); + const accountConfirmationToken = await Services.AccountConfirmation.findByAccountId( + account.id + ); + const token = Services.AccountConfirmation.generateToken( + accountConfirmationToken.id, + account.id + ); + const address = Services.Env.isProduction() + ? process.env.FRONTEND_ADDRESS_DEPLOY + : process.env.FRONTEND_ADDRESS_DEV; + const mailData = Services.AccountConfirmation.generateAccountConfirmationEmail( + address, + account.email, + Constants.General.HACKER, + token + ); + if (mailData !== undefined) { + Services.Email.send(mailData, err => { + if (err) { + return next(err); + } else { return next(); - } - await Services.AccountConfirmation.create(Constants.General.HACKER, account.email, account.id); - const accountConfirmationToken = await Services.AccountConfirmation.findByAccountId(account.id); - const token = Services.AccountConfirmation.generateToken(accountConfirmationToken.id, account.id); - const address = Services.Env.isProduction() ? process.env.FRONTEND_ADDRESS_DEPLOY : process.env.FRONTEND_ADDRESS_DEV; - const mailData = Services.AccountConfirmation.generateAccountConfirmationEmail(address, account.email, Constants.General.HACKER, token); - if (mailData !== undefined) { - Services.Email.send(mailData, (err) => { - if (err) { - return next(err); - } else { - return next(); - } - }); - } else { - return next({ - message: Constants.Error.EMAIL_500_MESSAGE, - }); - } + } + }); + } else { + return next({ + message: Constants.Error.EMAIL_500_MESSAGE + }); + } } /** @@ -223,193 +257,229 @@ async function sendConfirmAccountEmailMiddleware(req, res, next) { * @param {(err?)=>void} next */ async function resendConfirmAccountEmail(req, res, next) { - const account = await Services.Account.findById(req.user.id); - if (account.confirmed) { - return next({ - status: 422, - message: "Account already confirmed" - }); - } - const accountConfirmationToken = await Services.AccountConfirmation.findByAccountId(account.id); - if (!accountConfirmationToken) { - return next({ - status: 428, - message: "Account confirmation token does not exist" - }); - } - const token = Services.AccountConfirmation.generateToken(accountConfirmationToken.id, account.id); - const address = Services.Env.isProduction() ? process.env.FRONTEND_ADDRESS_DEPLOY : process.env.FRONTEND_ADDRESS_DEV; - const mailData = Services.AccountConfirmation.generateAccountConfirmationEmail(address, account.email, accountConfirmationToken.accountType, token); - if (mailData !== undefined) { - Services.Email.send(mailData, (err) => { - if (err) { - return next(err); - } else { - return next(); - } - }); - } else { - return next({ - message: "Error while generating email" - }); - } + const account = await Services.Account.findById(req.user.id); + if (account.confirmed) { + return next({ + status: 422, + message: "Account already confirmed" + }); + } + const accountConfirmationToken = await Services.AccountConfirmation.findByAccountId( + account.id + ); + if (!accountConfirmationToken) { + return next({ + status: 428, + message: "Account confirmation token does not exist" + }); + } + const token = Services.AccountConfirmation.generateToken( + accountConfirmationToken.id, + account.id + ); + const address = Services.Env.isProduction() + ? process.env.FRONTEND_ADDRESS_DEPLOY + : process.env.FRONTEND_ADDRESS_DEV; + const mailData = Services.AccountConfirmation.generateAccountConfirmationEmail( + address, + account.email, + accountConfirmationToken.accountType, + token + ); + if (mailData !== undefined) { + Services.Email.send(mailData, err => { + if (err) { + return next(err); + } else { + return next(); + } + }); + } else { + return next({ + message: "Error while generating email" + }); + } } /** * Attempts to parse the jwt token that is found in req.body.token using process.env.JWT_RESET_PWD_SECRET as the key. * Places the parsed object into req.body.decodedToken. - * @param {{body:{token:string}}} req - * @param {any} res - * @param {(err?)=>void} next + * @param {{body:{token:string}}} req + * @param {any} res + * @param {(err?)=>void} next */ function parseResetToken(req, res, next) { - jwt.verify(req.body['x-reset-token'], process.env.JWT_RESET_PWD_SECRET, function (err, decoded) { - if (err) { - return next(err); - } else { - req.body.decodedToken = decoded; - return next(); - } - }); + jwt.verify( + req.body["x-reset-token"], + process.env.JWT_RESET_PWD_SECRET, + function(err, decoded) { + if (err) { + return next(err); + } else { + req.body.decodedToken = decoded; + return next(); + } + } + ); } /** * Attempts to parse the jwt token that is found in req.body.token using process.env.JWT_CONFIRM_ACC_SECRET as the key. * Places the parsed object into req.body.decodedToken * If the token does not exist it just continues flow - * @param {{body:{token:string}}} req - * @param {any} res - * @param {(err?)=>void} next + * @param {{body:{token:string}}} req + * @param {any} res + * @param {(err?)=>void} next */ function parseAccountConfirmationToken(req, res, next) { - if (!!req.body.token) { - jwt.verify(req.body.token, process.env.JWT_CONFIRM_ACC_SECRET, function (err, decoded) { - if (err) { - return next(err); - } else { - req.body.decodedToken = decoded; - } - }); - } - return next(); + if (!!req.body.token) { + jwt.verify(req.body.token, process.env.JWT_CONFIRM_ACC_SECRET, function( + err, + decoded + ) { + if (err) { + return next(err); + } else { + req.body.decodedToken = decoded; + } + }); + } + return next(); } /** * Returns the type of account based on the confirmation token - * @param {{body:{decodedToken:{accountConfirmationId:string, accountId:string}}}} req - * @param {any} res - * @param {(err?)=>void} next + * @param {{body:{decodedToken:{accountConfirmationId:string, accountId:string}}}} req + * @param {any} res + * @param {(err?)=>void} next */ async function getAccountTypeFromConfirmationToken(req, res, next) { - const confirmationObj = await Services.AccountConfirmation.findById(req.body.decodedToken.accountConfirmationId); - if (confirmationObj) { - req.body.accountType = confirmationObj.accountType; - return next(); - } else { - //Either the token was already used, it's invalid, or user does not exist. - return next({ - status: 401, - message: Constants.Error.ACCOUNT_TOKEN_401_MESSAGE, - error: {} - }); - } + const confirmationObj = await Services.AccountConfirmation.findById( + req.body.decodedToken.accountConfirmationId + ); + if (confirmationObj) { + req.body.accountType = confirmationObj.accountType; + return next(); + } else { + //Either the token was already used, it's invalid, or user does not exist. + return next({ + status: 401, + message: Constants.Error.ACCOUNT_TOKEN_401_MESSAGE, + error: {} + }); + } } /** * Verifies that the resetId exists, and that the accountId exists. - * @param {{body:{decodedToken:{resetId:string, accountId:string}}}} req - * @param {any} res - * @param {(err?)=>void} next + * @param {{body:{decodedToken:{resetId:string, accountId:string}}}} req + * @param {any} res + * @param {(err?)=>void} next */ async function validateResetToken(req, res, next) { - const resetObj = await Services.ResetPasswordToken.findById(req.body.decodedToken.resetId); - const userObj = await Services.Account.findById(req.body.decodedToken.accountId); - if (resetObj && userObj) { - req.body.user = userObj; - return next(); - } else { - //Either the token was already used, it's invalid, or user does not exist. - return next({ - status: 401, - message: Constants.Error.ACCOUNT_TOKEN_401_MESSAGE, - error: {} - }); - } + const resetObj = await Services.ResetPasswordToken.findById( + req.body.decodedToken.resetId + ); + const userObj = await Services.Account.findById( + req.body.decodedToken.accountId + ); + if (resetObj && userObj) { + req.body.user = userObj; + return next(); + } else { + //Either the token was already used, it's invalid, or user does not exist. + return next({ + status: 401, + message: Constants.Error.ACCOUNT_TOKEN_401_MESSAGE, + error: {} + }); + } } /** * Verifies that the confirm account exists, and that the accountId exists. - * @param {{body:{decodedToken:{accountConfirmationId: String, accountId: String}}}} req - * @param {any} res - * @param {(err?)=>void} next + * @param {{body:{decodedToken:{accountConfirmationId: String, accountId: String}}}} req + * @param {any} res + * @param {(err?)=>void} next */ async function validateConfirmationToken(req, res, next) { - const confirmationObj = await Services.AccountConfirmation.findById(req.body.decodedToken.accountConfirmationId); - const userObj = await Services.Account.findById(req.body.decodedToken.accountId); - if (confirmationObj && userObj && (confirmationObj.accountId == userObj.id)) { - userObj.confirmed = true; - userObj.accountType = confirmationObj.accountType; - await Services.Account.updateOne(confirmationObj.accountId, userObj); - req.body.user = userObj; - return next(); - } else { - //Either the token was already used, it's invalid, or user does not exist. - return next({ - status: 401, - message: Constants.Error.ACCOUNT_TOKEN_401_MESSAGE, - error: {} - }); - } + const confirmationObj = await Services.AccountConfirmation.findById( + req.body.decodedToken.accountConfirmationId + ); + const userObj = await Services.Account.findById( + req.body.decodedToken.accountId + ); + if (confirmationObj && userObj && confirmationObj.accountId == userObj.id) { + userObj.confirmed = true; + userObj.accountType = confirmationObj.accountType; + await Services.Account.updateOne(confirmationObj.accountId, userObj); + req.body.user = userObj; + return next(); + } else { + //Either the token was already used, it's invalid, or user does not exist. + return next({ + status: 401, + message: Constants.Error.ACCOUNT_TOKEN_401_MESSAGE, + error: {} + }); + } } /** - * - * @param {body: {decodedToken:{accountConfirmationId: String}}} req - * @param {*} res - * @param {*} next + * + * @param {body: {decodedToken:{accountConfirmationId: String}}} req + * @param {*} res + * @param {*} next */ async function validateConfirmationTokenWithoutAccount(req, res, next) { - if (!!req.body.decodedToken) { - const confirmationObj = await Services.AccountConfirmation.findById(req.body.decodedToken.accountConfirmationId); - if (!confirmationObj.accountId) { - req.body.accountDetails.confirmed = true; - req.body.accountDetails.accountType = confirmationObj.accountType; - } + if (!!req.body.decodedToken) { + const confirmationObj = await Services.AccountConfirmation.findById( + req.body.decodedToken.accountConfirmationId + ); + if (!confirmationObj.accountId) { + req.body.accountDetails.confirmed = true; + req.body.accountDetails.accountType = confirmationObj.accountType; } - return next(); + } + return next(); } - /** * Middleware that deletes the reset token in the db * @param {{body: {decodedToken:{resetId:String}}}} req the request object - * @param {*} res - * @param {(err?)=>void} next + * @param {*} res + * @param {(err?)=>void} next */ function deleteResetToken(req, res, next) { - Services.ResetPasswordToken.deleteToken(req.body.decodedToken.resetId).then( - () => { - return next(); - }, - (err) => { - return next(err); - } - ); + Services.ResetPasswordToken.deleteToken(req.body.decodedToken.resetId).then( + () => { + return next(); + }, + err => { + return next(err); + } + ); } /** * Middleware that creates rolebinding to access POST route for respective account * @param {{body: {account:{accountType:String, id: ObjectId}}}} req the request object - * @param {*} res - * @param {(err?)=>void} next + * @param {*} res + * @param {(err?)=>void} next */ async function addCreationRoleBindings(req, res, next) { - // Get the default role for the account type given - const roleName = Constants.General.POST_ROLES[req.body.account.accountType]; - await Services.RoleBinding.createRoleBindingByRoleName(req.body.account.id, roleName); - // Add default account role bindings - await Services.RoleBinding.createRoleBindingByRoleName(req.body.account.id, Constants.Role.accountRole.name); - return next(); + // Get the default role for the account type given + const roleName = Constants.General.POST_ROLES[req.body.account.accountType]; + await Services.RoleBinding.createRoleBindingByRoleName( + req.body.account.id, + roleName + ); + // Add default account role bindings + await Services.RoleBinding.createRoleBindingByRoleName( + req.body.account.id, + Constants.Role.accountRole.name + ); + return next(); } /** @@ -417,54 +487,76 @@ async function addCreationRoleBindings(req, res, next) { * @param {string} roleName name of the role to be added to account */ function createRoleBindings(roleName = undefined) { - return Middleware.Util.asyncMiddleware(async (req, res, next) => { - await Services.RoleBinding.createRoleBindingByRoleName(req.user.id, roleName); - return next(); - }); + return Middleware.Util.asyncMiddleware(async (req, res, next) => { + await Services.RoleBinding.createRoleBindingByRoleName( + req.user.id, + roleName + ); + return next(); + }); } /** - * Adds a rolebinding between the user and the role with the name stored in 'accountType'. - * @param {{user: {id: ObjectId, accountType: string}}} req - * @param {*} res - * @param {(err?) => void} next + * Adds a rolebinding between the user and the role with the name stored in 'accountType'. + * @param {{user: {id: ObjectId, accountType: string}}} req + * @param {*} res + * @param {(err?) => void} next */ async function addAccountTypeRoleBinding(req, res, next) { - await Services.RoleBinding.createRoleBindingByRoleName(req.user.id, req.user.accountType); - return next(); + await Services.RoleBinding.createRoleBindingByRoleName( + req.user.id, + req.user.accountType + ); + return next(); } /** * Middleware to retrieve all the roles in the database - * @param {*} req - * @param {*} res - * @param {(err?) => void } next + * @param {*} req + * @param {*} res + * @param {(err?) => void } next */ async function retrieveRoles(req, res, next) { - const roles = await Services.Role.getAll(); - req.roles = roles; - return next(); + const roles = await Services.Role.getAll(); + req.roles = roles; + return next(); } module.exports = { - //for each route, set up an authentication middleware for that route - login: login, - ensureAuthenticated: ensureAuthenticated, - ensureAuthorized: ensureAuthorized, - sendResetPasswordEmailMiddleware: Middleware.Util.asyncMiddleware(sendResetPasswordEmailMiddleware), - parseResetToken: parseResetToken, - validateResetToken: Middleware.Util.asyncMiddleware(validateResetToken), - deleteResetToken: deleteResetToken, - sendConfirmAccountEmailMiddleware: Middleware.Util.asyncMiddleware(sendConfirmAccountEmailMiddleware), - parseAccountConfirmationToken: parseAccountConfirmationToken, - validateConfirmationToken: Middleware.Util.asyncMiddleware(validateConfirmationToken), - getAccountTypeFromConfirmationToken: Middleware.Util.asyncMiddleware(getAccountTypeFromConfirmationToken), - validateConfirmationTokenWithoutAccount: Middleware.Util.asyncMiddleware(validateConfirmationTokenWithoutAccount), - createRoleBindings: createRoleBindings, - addAccountTypeRoleBinding: Middleware.Util.asyncMiddleware(addAccountTypeRoleBinding), - addCreationRoleBindings: Middleware.Util.asyncMiddleware(addCreationRoleBindings), - resendConfirmAccountEmail: Middleware.Util.asyncMiddleware(resendConfirmAccountEmail), - retrieveRoleBindings: Middleware.Util.asyncMiddleware(retrieveRoleBindings), - retrieveRoles: Middleware.Util.asyncMiddleware(retrieveRoles), - changePassword: Middleware.Util.asyncMiddleware(changePassword), -}; \ No newline at end of file + //for each route, set up an authentication middleware for that route + login: login, + ensureAuthenticated: ensureAuthenticated, + ensureAuthorized: ensureAuthorized, + sendResetPasswordEmailMiddleware: Middleware.Util.asyncMiddleware( + sendResetPasswordEmailMiddleware + ), + parseResetToken: parseResetToken, + validateResetToken: Middleware.Util.asyncMiddleware(validateResetToken), + deleteResetToken: deleteResetToken, + sendConfirmAccountEmailMiddleware: Middleware.Util.asyncMiddleware( + sendConfirmAccountEmailMiddleware + ), + parseAccountConfirmationToken: parseAccountConfirmationToken, + validateConfirmationToken: Middleware.Util.asyncMiddleware( + validateConfirmationToken + ), + getAccountTypeFromConfirmationToken: Middleware.Util.asyncMiddleware( + getAccountTypeFromConfirmationToken + ), + validateConfirmationTokenWithoutAccount: Middleware.Util.asyncMiddleware( + validateConfirmationTokenWithoutAccount + ), + createRoleBindings: createRoleBindings, + addAccountTypeRoleBinding: Middleware.Util.asyncMiddleware( + addAccountTypeRoleBinding + ), + addCreationRoleBindings: Middleware.Util.asyncMiddleware( + addCreationRoleBindings + ), + resendConfirmAccountEmail: Middleware.Util.asyncMiddleware( + resendConfirmAccountEmail + ), + retrieveRoleBindings: Middleware.Util.asyncMiddleware(retrieveRoleBindings), + retrieveRoles: Middleware.Util.asyncMiddleware(retrieveRoles), + changePassword: Middleware.Util.asyncMiddleware(changePassword) +}; diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index 9d191d4c..36f564d5 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -37,7 +37,7 @@ function parsePatch(req, res, next) { * @param {(err?)=>void} next * @return {void} * @description - * Moves accountId, school, degree, gender, needsBus, application from req.body to req.body.hackerDetails. + * Moves accountId, application from req.body to req.body.hackerDetails. * Adds _id to hackerDetails. */ function parseHacker(req, res, next) { @@ -217,7 +217,7 @@ function ensureAccountLinkedToHacker(req, res, next) { * @param {(err?)=>void} next */ async function uploadResume(req, res, next) { - const gcfilename = `resumes/${Date.now()}-${req.hacker.id}`; + const gcfilename = `resume/${Date.now()}-${req.hacker.id}`; await Services.Storage.upload(req.file, gcfilename); req.body.gcfilename = gcfilename; await Services.Hacker.updateOne(req.hacker.id, { @@ -456,6 +456,7 @@ function checkStatus(statuses) { * @param {*} next */ async function updateHacker(req, res, next) { + console.log("does it reach?"); const hacker = await Services.Hacker.updateOne(req.params.id, req.body); if (hacker) { const acct = await Services.Account.findById(hacker.accountId); diff --git a/models/hacker.model.js b/models/hacker.model.js index 9c78cefd..f977b3ec 100644 --- a/models/hacker.model.js +++ b/models/hacker.model.js @@ -111,7 +111,8 @@ const HackerSchema = new mongoose.Schema({ } }, accomodation: { - needsBus: Boolean + needsBus: Boolean, + default: false }, team: { type: mongoose.Schema.Types.ObjectId, diff --git a/package-lock.json b/package-lock.json index ad884d92..deef321c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hackboard", - "version": "0.0.0", + "version": "0.0.8", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e745de4a..a94a1464 100644 --- a/package.json +++ b/package.json @@ -1,52 +1,52 @@ { - "name": "hackboard", - "version": "0.0.0", - "private": true, - "scripts": { - "start": "NODE_ENV=deployment node ./bin/www.js", - "debug": "DEBUG=hackboard:* NODE_ENV=deployment nodemon --ignore gcp_creds.json ./bin/www.js", - "development": "DEBUG=hackboard:* NODE_ENV=development nodemon --ignore gcp_creds.json ./bin/www.js", - "test": "DEBUG=hackboard:* NODE_ENV=test mocha --reporter spec tests/**.js --exit", - "seed": "NODE_ENV=development node ./seed/index.js", - "docs": "apidoc -i ./routes -o ./docs/api/" - }, - "dependencies": { - "@google-cloud/logging-winston": "^0.11.1", - "@google-cloud/storage": "^3.5.0", - "@sendgrid/mail": "^6.4.0", - "bcrypt": "^3.0.6", - "cookie-parser": "~1.4.4", - "cookie-session": "^2.0.0-beta.3", - "cors": "^2.8.5", - "cryptiles": "^4.1.3", - "debug": "~4.1.1", - "dotenv": "^8.2.0", - "express": "~4.17.1", - "express-validator": "^6.2.0", - "express-winston": "^2.6.0", - "handlebars": "^4.4.3", - "jsonwebtoken": "^8.5.1", - "memory-cache": "^0.2.0", - "mongoose": "^5.7.5", - "multer": "^1.4.2", - "passport": "^0.4.0", - "passport-local": "^1.0.0", - "q": "^1.5.1", - "qrcode": "^1.4.2", - "winston": "^2.4.4" - }, - "devDependencies": { - "@types/express": "^4.17.1", - "@types/google-cloud__storage": "^1.7.2", - "@types/mongodb": "^3.3.1", - "@types/mongoose": "^5.5.21", - "@types/multer": "^1.3.10", - "apidoc": "^0.17.7", - "chai": "^4.2.0", - "chai-http": "^4.3.0", - "jshint": "^2.10.2", - "jslint": "^0.12.1", - "mocha": "^6.2.1", - "nodemon": "^1.19.4" - } + "name": "hackboard", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "NODE_ENV=deployment node ./bin/www.js", + "debug": "DEBUG=hackboard:* NODE_ENV=deployment nodemon --ignore gcp_creds.json ./bin/www.js", + "development": "DEBUG=hackboard:* NODE_ENV=development nodemon --ignore gcp_creds.json ./bin/www.js", + "test": "DEBUG=hackboard:* NODE_ENV=test mocha --reporter spec tests/**.js --exit", + "seed": "NODE_ENV=test node ./seed/index.js", + "docs": "apidoc -i ./routes -o ./docs/api/" + }, + "dependencies": { + "@google-cloud/logging-winston": "^0.11.1", + "@google-cloud/storage": "^3.5.0", + "@sendgrid/mail": "^6.4.0", + "bcrypt": "^3.0.6", + "cookie-parser": "~1.4.4", + "cookie-session": "^2.0.0-beta.3", + "cors": "^2.8.5", + "cryptiles": "^4.1.3", + "debug": "~4.1.1", + "dotenv": "^8.2.0", + "express": "~4.17.1", + "express-validator": "^6.2.0", + "express-winston": "^2.6.0", + "handlebars": "^4.4.3", + "jsonwebtoken": "^8.5.1", + "memory-cache": "^0.2.0", + "mongoose": "^5.7.5", + "multer": "^1.4.2", + "passport": "^0.4.0", + "passport-local": "^1.0.0", + "q": "^1.5.1", + "qrcode": "^1.4.2", + "winston": "^2.4.4" + }, + "devDependencies": { + "@types/express": "^4.17.1", + "@types/google-cloud__storage": "^1.7.2", + "@types/mongodb": "^3.3.1", + "@types/mongoose": "^5.5.21", + "@types/multer": "^1.3.10", + "apidoc": "^0.17.7", + "chai": "^4.2.0", + "chai-http": "^4.3.0", + "jshint": "^2.10.2", + "jslint": "^0.12.1", + "mocha": "^6.2.1", + "nodemon": "^1.19.4" + } } diff --git a/routes/api/hacker.js b/routes/api/hacker.js index e3518966..65bbd8db 100644 --- a/routes/api/hacker.js +++ b/routes/api/hacker.js @@ -40,7 +40,7 @@ module.exports = { "data": { "id":"5bff4d736f86be0a41badb91", "application":{ - "portfolioURL":{ + "URL":{ "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91", "github":"https://github.com/abcd", "dropler":"https://dribbble.com/abcd", @@ -96,7 +96,13 @@ module.exports = { * @apiParamExample {Json} application: * { "application":{ - "portfolioURL":{ + "general":{ + "school": "McGill University", + "degree": "Undergraduate", + "fieldOfStudy": "Computer Science", + "graduationYear": "2021", + "jobInterest":"Internship", + "URL":{ "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91", "github":"https://github.com/abcd", "dropler":"https://dribbble.com/abcd", @@ -104,10 +110,24 @@ module.exports = { "linkedIn":"https://linkedin.com/in/abcd", "other":"https://github.com/hackmcgill/hackerAPI/issues/168" }, - "jobInterest":"Internship", + }, + "shortAnswer": { "skills":["Javascript","Typescript"], + "question1": "I love McHacks", + "question2":"Pls accept me", "comments":"hi!", - "essay":"Pls accept me" + }, + "other:" { + "gender": "male", + "ethnicity": "Asian or Pacific Islander", + "codeOfConduct_MLH": true, + "codeOfConduct_MCHACKS": true, + } + "accomodation": { + "needsBus": "false" + }, + } + * } * * @apiSuccess {string} message Success message @@ -118,29 +138,37 @@ module.exports = { * "data": { "id":"5bff4d736f86be0a41badb91", "application":{ - "portfolioURL":{ - "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91", - "github":"https://github.com/abcd", - "dribbble":"https://dribbble.com/abcd", - "personal":"https://www.hi.com/", - "linkedIn":"https://linkedin.com/in/abcd", - "other":"https://github.com/hackmcgill/hackerAPI/issues/168" - }, + "general":{ + "school": "McGill University", + "degree": "Undergraduate", + "fieldOfStudy": "Computer Science", + "graduationYear": "2021", "jobInterest":"Internship", + "URL":{ + "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91", + "github":"https://github.com/abcd", + "dropler":"https://dribbble.com/abcd", + "personal":"https://www.hi.com/", + "linkedIn":"https://linkedin.com/in/abcd", + "other":"https://github.com/hackmcgill/hackerAPI/issues/168" + }, + }, + "shortAnswer": { "skills":["Javascript","Typescript"], + "question1": "I love McHacks", + "question2":"Pls accept me", "comments":"hi!", - "essay":"Pls accept me" - }, - "status":"Applied", - "ethnicity":["White or Caucasian"," Asian or Pacific Islander"], - "accountId":"5bff2a35e533b0f6562b4998", - "school":"McPherson College", - "gender":"Female", - "needsBus":false, - "major":"Accounting", - "graduationYear":2019, - "codeOfConduct":true, - * } + }, + "other:" { + "gender": "male", + "ethnicity": "Asian or Pacific Islander", + "codeOfConduct_MLH": true, + "codeOfConduct_MCHACKS": true, + } + "accomodation": { + "needsBus": "false" + }, + } * } * @apiError {string} message Error message @@ -151,7 +179,6 @@ module.exports = { hackerRouter.route("/").post( Middleware.Auth.ensureAuthenticated(), Middleware.Auth.ensureAuthorized(), - Middleware.Validator.Hacker.newHackerValidator, Middleware.parseBody.middleware, @@ -303,18 +330,38 @@ module.exports = { * @apiParam (body) {Json} [application] The hacker's application * @apiParamExample {Json} application: * { - "portfolioURL":{ + "application":{ + "general":{ + "school": "McGill University", + "degree": "Undergraduate", + "fieldOfStudy": "Computer Science", + "graduationYear": "2021", + "jobInterest":"Internship", + "URL":{ "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91", "github":"https://github.com/abcd", - "dribbble":"https://dribbble.com/abcd", + "dropler":"https://dribbble.com/abcd", "personal":"https://www.hi.com/", "linkedIn":"https://linkedin.com/in/abcd", "other":"https://github.com/hackmcgill/hackerAPI/issues/168" + }, + }, + "shortAnswer": { + "skills":["Javascript","Typescript"], + "question1": "I love McHacks", + "question2":"Pls accept me", + "comments":"hi!", }, - "jobInterest":"Internship", - "skills":["Javascript","Typescript"], - "comments":"hi!", - "essay":"Pls accept me" + "other:" { + "gender": "male", + "ethnicity": "Asian or Pacific Islander", + "codeOfConduct_MLH": true, + "codeOfConduct_MCHACKS": true, + } + "accomodation": { + "needsBus": "false" + }, + } } * * @apiSuccess {string} message Success message @@ -324,29 +371,40 @@ module.exports = { * "message": "Changed hacker information", * "data": { "id":"5bff4d736f86be0a41badb91", - "application":{ - "portfolioURL":{ - "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91", - "github":"https://github.com/abcd", - "dribbble":"https://dribbble.com/abcd", - "personal":"https://www.hi.com/", - "linkedIn":"https://linkedin.com/in/abcd", - "other":"https://github.com/hackmcgill/hackerAPI/issues/168" - }, + "status": "Applied", + "application":{ + "general":{ + "school": "McGill University", + "degree": "Undergraduate", + "fieldOfStudy": "Computer Science", + "graduationYear": "2021", "jobInterest":"Internship", + "URL":{ + "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91", + "github":"https://github.com/abcd", + "dropler":"https://dribbble.com/abcd", + "personal":"https://www.hi.com/", + "linkedIn":"https://linkedin.com/in/abcd", + "other":"https://github.com/hackmcgill/hackerAPI/issues/168" + }, + }, + "shortAnswer": { "skills":["Javascript","Typescript"], + "question1": "I love McHacks", + "question2":"Pls accept me", "comments":"hi!", - "essay":"Pls accept me" - }, - "status":"Applied", - "ethnicity":["White or Caucasian"," Asian or Pacific Islander"], - "accountId":"5bff2a35e533b0f6562b4998", - "school":"McPherson College", - "gender":"Female", - "needsBus":false, - "major":"Accounting", - "graduationYear":2019, - "codeOfConduct":true, + }, + "other:" { + "gender": "male", + "ethnicity": "Asian or Pacific Islander", + "codeOfConduct_MLH": true, + "codeOfConduct_MCHACKS": true, + } + "accomodation": { + "needsBus": "false" + }, + } + } * } * @apiError {string} message Error message * @apiError {object} data empty @@ -383,29 +441,39 @@ module.exports = { "message": "Successfully retrieved hacker information", "data": { "id":"5bff4d736f86be0a41badb91", + "status": "Applied", "application":{ - "portfolioURL":{ - "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91", - "github":"https://github.com/abcd", - "dropler":"https://dribbble.com/abcd", - "personal":"https://www.hi.com/", - "linkedIn":"https://linkedin.com/in/abcd", - "other":"https://github.com/hackmcgill/hackerAPI/issues/168" - }, + "general":{ + "school": "McGill University", + "degree": "Undergraduate", + "fieldOfStudy": "Computer Science", + "graduationYear": "2021", "jobInterest":"Internship", + "URL":{ + "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91", + "github":"https://github.com/abcd", + "dropler":"https://dribbble.com/abcd", + "personal":"https://www.hi.com/", + "linkedIn":"https://linkedin.com/in/abcd", + "other":"https://github.com/hackmcgill/hackerAPI/issues/168" + }, + }, + "shortAnswer": { "skills":["Javascript","Typescript"], + "question1": "I love McHacks", + "question2":"Pls accept me", "comments":"hi!", - "essay":"Pls accept me" - }, - "status":"Applied", - "ethnicity":["White or Caucasian"," Asian or Pacific Islander"], - "accountId":"5bff2a35e533b0f6562b4998", - "school":"McPherson College", - "gender":"Female", - "needsBus":false, - "major":"Accounting", - "graduationYear":2019, - "codeOfConduct":true, + }, + "other:" { + "gender": "male", + "ethnicity": "Asian or Pacific Islander", + "codeOfConduct_MLH": true, + "codeOfConduct_MCHACKS": true, + } + "accomodation": { + "needsBus": "false" + }, + } } } @@ -440,29 +508,39 @@ module.exports = { "message": "Successfully retrieved hacker information", "data": { "id":"5bff4d736f86be0a41badb91", + "status": "Applied", "application":{ - "portfolioURL":{ - "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91", - "github":"https://github.com/abcd", - "dropler":"https://dribbble.com/abcd", - "personal":"https://www.hi.com/", - "linkedIn":"https://linkedin.com/in/abcd", - "other":"https://github.com/hackmcgill/hackerAPI/issues/168" - }, + "general":{ + "school": "McGill University", + "degree": "Undergraduate", + "fieldOfStudy": "Computer Science", + "graduationYear": "2021", "jobInterest":"Internship", + "URL":{ + "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91", + "github":"https://github.com/abcd", + "dropler":"https://dribbble.com/abcd", + "personal":"https://www.hi.com/", + "linkedIn":"https://linkedin.com/in/abcd", + "other":"https://github.com/hackmcgill/hackerAPI/issues/168" + }, + }, + "shortAnswer": { "skills":["Javascript","Typescript"], + "question1": "I love McHacks", + "question2":"Pls accept me", "comments":"hi!", - "essay":"Pls accept me" - }, - "status":"Applied", - "ethnicity":["White or Caucasian"," Asian or Pacific Islander"], - "accountId":"5bff2a35e533b0f6562b4998", - "school":"McPherson College", - "gender":"Female", - "needsBus":false, - "major":"Accounting", - "graduationYear":2019, - "codeOfConduct":true, + }, + "other:" { + "gender": "male", + "ethnicity": "Asian or Pacific Islander", + "codeOfConduct_MLH": true, + "codeOfConduct_MCHACKS": true, + } + "accomodation": { + "needsBus": "false" + }, + } } } diff --git a/scripts/batch_scripts.py b/scripts/batch_scripts.py index cedeef75..58a359ec 100644 --- a/scripts/batch_scripts.py +++ b/scripts/batch_scripts.py @@ -392,12 +392,12 @@ def getHackers(): 'First Name': result['accountId']['firstName'], 'Last Name': result['accountId']['lastName'], 'Email': result['accountId']['email'], - 'School': result['school'], - 'Degree': result['degree'], - 'Graduation Year': result['graduationYear'], - 'Job Interest': result['application']['jobInterest'], - 'Github': result['application']['portfolioURL']['github'], - 'LinkedIn': result['application']['portfolioURL']['linkedIn'], + 'School': result['application']['general']['school'], + 'Degree': result['application']['general']['degree'], + 'Graduation Year': result['application']['general']['graduationYear'], + 'Job Interest': result['application']['general']['jobInterest'], + 'Github': result['application']['general']['URL']['github'], + 'LinkedIn': result['application']['general']['URL']['linkedIn'], }) diff --git a/services/hacker.service.js b/services/hacker.service.js index 355a579e..54f35d46 100644 --- a/services/hacker.service.js +++ b/services/hacker.service.js @@ -140,7 +140,7 @@ function getStats(hackers) { needsBus: {}, ethnicity: {}, jobInterest: {}, - major: {}, + fieldOfStudy: {}, graduationYear: {}, dietaryRestrictions: {}, shirtSize: {}, @@ -156,37 +156,44 @@ function getStats(hackers) { stats.status[hacker.status] = stats.status[hacker.status] ? stats.status[hacker.status] + 1 : 1; - stats.school[hacker.school] = stats.school[hacker.school] - ? stats.school[hacker.school] + 1 + stats.school[hacker.application.general.school] = stats.school[ + hacker.application.general.school + ] + ? stats.school[hacker.application.general.school] + 1 : 1; - stats.degree[hacker.degree] = stats.degree[hacker.degree] - ? stats.degree[hacker.degree] + 1 + stats.degree[hacker.application.general.degree] = stats.degree[ + hacker.application.general.degree + ] + ? stats.degree[hacker.application.general.degree] + 1 : 1; - stats.gender[hacker.gender] = stats.gender[hacker.gender] - ? stats.gender[hacker.gender] + 1 + stats.gender[hacker.application.other.gender] = stats.gender[ + hacker.application.other.gender + ] + ? stats.gender[hacker.application.other.gender] + 1 : 1; - stats.needsBus[hacker.needsBus] = stats.needsBus[hacker.needsBus] - ? stats.needsBus[hacker.needsBus] + 1 + stats.needsBus[hacker.application.accomodation.needsBus] = stats.needsBus[ + hacker.application.accomodation.needsBus + ] + ? stats.needsBus[hacker.application.accomodation.needsBus] + 1 : 1; - for (const ethnicity of hacker.ethnicity) { + for (const ethnicity of hacker.application.other.ethnicity) { stats.ethnicity[ethnicity] = stats.ethnicity[ethnicity] ? stats.ethnicity[ethnicity] + 1 : 1; } - stats.jobInterest[hacker.application.jobInterest] = stats.jobInterest[ - hacker.application.jobInterest - ] - ? stats.jobInterest[hacker.application.jobInterest] + 1 + stats.jobInterest[hacker.application.general.jobInterest] = stats + .jobInterest[hacker.application.general.jobInterest] + ? stats.jobInterest[hacker.application.general.jobInterest] + 1 : 1; - stats.major[hacker.major] = stats.major[hacker.major] - ? stats.major[hacker.major] + 1 + stats.fieldOfStudy[hacker.application.general.fieldOfStudy] = stats + .fieldOfStudy[hacker.application.general.fieldOfStudy] + ? stats.fieldOfStudy[hacker.application.general.fieldOfStudy] + 1 : 1; - stats.graduationYear[hacker.graduationYear] = stats.graduationYear[ - hacker.graduationYear - ] - ? stats.graduationYear[hacker.graduationYear] + 1 + stats.graduationYear[hacker.application.general.graduationYear] = stats + .graduationYear[hacker.application.general.graduationYear] + ? stats.graduationYear[hacker.application.general.graduationYear] + 1 : 1; for (const dietaryRestrictions of hacker.accountId.dietaryRestrictions) { diff --git a/tests/account.test.js b/tests/account.test.js index f718405f..8acc1624 100644 --- a/tests/account.test.js +++ b/tests/account.test.js @@ -7,17 +7,16 @@ const logger = require("../services/logger.service"); const Account = require("../models/account.model"); const should = chai.should(); const Constants = { - Error: require("../constants/error.constant"), - General: require("../constants/general.constant"), - Success: require("../constants/success.constant"), + Error: require("../constants/error.constant"), + General: require("../constants/general.constant"), + Success: require("../constants/success.constant") }; - const util = { - account: require("./util/account.test.util"), - auth: require("./util/auth.test.util"), - accountConfirmation: require("./util/accountConfirmation.test.util"), - reset: require("./util/resetPassword.test.util") + account: require("./util/account.test.util"), + auth: require("./util/auth.test.util"), + accountConfirmation: require("./util/accountConfirmation.test.util"), + reset: require("./util/resetPassword.test.util") }; const agent = chai.request.agent(server.app); // tokens @@ -39,567 +38,608 @@ const storedAccount3 = util.account.NonConfirmedAccount3; const newAccount0 = util.account.unlinkedAccounts.new[0]; - -describe("GET user account", function () { - // fail on authentication - it("should fail to list the user's account on /api/account/self GET due to authentication", function (done) { - chai.request(server.app) - .get("/api/account/self") - .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(); - }); - }); - - // fail due to invalid login - it("should fail due to invalid password", function (done) { - agent.post("/api/auth/login").type("application/json").send({ - email: Admin0.email, - password: "FakePassword" - }).end((err, res) => { - res.should.have.status(401); +describe("GET user account", function() { + // fail on authentication + it("should fail to list the user's account on /api/account/self GET due to authentication", function(done) { + chai + .request(server.app) + .get("/api/account/self") + .end(function(err, res) { + console.log(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(); + }); + }); + + // fail due to invalid login + it("should fail due to invalid password", function(done) { + agent + .post("/api/auth/login") + .type("application/json") + .send({ + email: Admin0.email, + password: "FakePassword" + }) + .end((err, res) => { + res.should.have.status(401); + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); + done(); + }); + }); + + // success case + it("should list the user's account on /api/account/self GET", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .get("/api/account/self") + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(200); + res.should.be.json; res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); + res.body.message.should.equal(Constants.Success.ACCOUNT_READ); + res.body.should.have.property("data"); + res.body.data.should.be.a("object"); + res.body.data.should.have.property("firstName"); + res.body.data.should.have.property("lastName"); + res.body.data.should.have.property("email"); + res.body.data.should.have.property("dietaryRestrictions"); + res.body.data.should.have.property("shirtSize"); done(); - }); + }) + ); }); - - // success case - it("should list the user's account on /api/account/self GET", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); + }); + + // success case - admin case + it("should list another account specified by id using admin priviledge on /api/account/:id/ GET", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .get(`/api/account/${teamHackerAccount0._id}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); } - return agent - .get("/api/account/self") - // does not have password because of to stripped json - .end(function (err, res) { - if (err) { - return done(err); - } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.ACCOUNT_READ); - res.body.should.have.property("data"); - res.body.data.should.be.a("object"); - res.body.data.should.have.property("firstName"); - res.body.data.should.have.property("lastName"); - res.body.data.should.have.property("email"); - res.body.data.should.have.property("dietaryRestrictions"); - res.body.data.should.have.property("shirtSize"); - done(); - }); - }); - }); - - // success case - admin case - it("should list another account specified by id using admin priviledge on /api/account/:id/ GET", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get(`/api/account/${teamHackerAccount0._id}`) - // does not have password because of to stripped json - .end(function (err, res) { - if (err) { - return done(err); - } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.ACCOUNT_READ); - res.body.should.have.property("data"); - - // use acc.toStrippedJSON to deal with hidden passwords and convert _id to id - const acc = new Account(teamHackerAccount0); - chai.assert.equal(JSON.stringify(res.body.data), JSON.stringify(acc.toStrippedJSON())); - done(); - }); - }); + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.ACCOUNT_READ); + res.body.should.have.property("data"); + + // use acc.toStrippedJSON to deal with hidden passwords and convert _id to id + const acc = new Account(teamHackerAccount0); + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(acc.toStrippedJSON()) + ); + done(); + }) + ); }); - // success case - user case - it("should list an account specified by id on /api/account/:id/ GET", function (done) { - util.auth.login(agent, teamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); + }); + // success case - user case + it("should list an account specified by id on /api/account/:id/ GET", function(done) { + util.auth.login(agent, teamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .get(`/api/account/${teamHackerAccount0._id}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); } - return agent - .get(`/api/account/${teamHackerAccount0._id}`) - // does not have password because of to stripped json - .end(function (err, res) { - if (err) { - return done(err); - } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.ACCOUNT_READ); - res.body.should.have.property("data"); - - // use acc.toStrippedJSON to deal with hidden passwords and convert _id to id - const acc = new Account(teamHackerAccount0); - chai.assert.equal(JSON.stringify(res.body.data), JSON.stringify(acc.toStrippedJSON())); - done(); - }); - }); + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.ACCOUNT_READ); + res.body.should.have.property("data"); + + // use acc.toStrippedJSON to deal with hidden passwords and convert _id to id + const acc = new Account(teamHackerAccount0); + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(acc.toStrippedJSON()) + ); + done(); + }) + ); }); - - // // fail case on authorization - it("should fail to list an account specified by id on /api/account/:id/ GET due to lack of authorization", function (done) { - util.auth.login(agent, teamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); + }); + + // // fail case on authorization + it("should fail to list an account specified by id on /api/account/:id/ GET due to lack of authorization", function(done) { + util.auth.login(agent, teamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .get(`/api/account/${Admin0._id}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); } - return agent - .get(`/api/account/${Admin0._id}`) - // does not have password because of to stripped json - .end(function (err, res) { - if (err) { - return done(err); - } - 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"); + 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(); - }); - }); + done(); + }) + ); }); + }); }); -describe("POST create account", function () { - it("should SUCCEED and create a new account", function (done) { - chai.request(server.app) - .post(`/api/account/`) - .type("application/json") - .send(newAccount0) - .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.ACCOUNT_CREATE); - - // use acc.toStrippedJSON to deal with hidden passwords and convert _id to id - const acc = (new Account(newAccount0)).toStrippedJSON(); - // delete id as those are generated - delete acc.id; - delete res.body.data.id; - chai.assert.equal(JSON.stringify(res.body.data), JSON.stringify(acc)); - done(); - }); - }); - - it("should FAIL to create an account because the email is already in use", function (done) { - chai.request(server.app) - .post(`/api/account/`) - .type("application/json") - .send(teamHackerAccount0) - .end(function (err, res) { - res.should.have.status(422); - done(); - }); - }); +describe("POST create account", function() { + it("should SUCCEED and create a new account", function(done) { + chai + .request(server.app) + .post(`/api/account/`) + .type("application/json") + .send(newAccount0) + .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.ACCOUNT_CREATE); + + // use acc.toStrippedJSON to deal with hidden passwords and convert _id to id + const acc = new Account(newAccount0).toStrippedJSON(); + // delete id as those are generated + delete acc.id; + delete res.body.data.id; + chai.assert.equal(JSON.stringify(res.body.data), JSON.stringify(acc)); + done(); + }); + }); + + it("should FAIL to create an account because the email is already in use", function(done) { + chai + .request(server.app) + .post(`/api/account/`) + .type("application/json") + .send(teamHackerAccount0) + .end(function(err, res) { + res.should.have.status(422); + done(); + }); + }); }); -describe("POST confirm account", function () { - it("should SUCCEED and confirm the account", function (done) { - chai.request(server.app) - .post(`/api/auth/confirm/${confirmationToken}`) - .type("application/json") - .end(function (err, res) { - res.should.have.status(200); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.AUTH_CONFIRM_ACCOUNT); - done(); - }); - }); - it("should FAIL confirming the account", function (done) { - chai.request(server.app) - .post(`/api/auth/confirm/${fakeToken}`) - .type("application/json") - .end(function (err, res) { - res.should.have.status(401); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.ACCOUNT_TOKEN_401_MESSAGE); - done(); - }); - }); - it("should FAIL to confirm account that has token with email but no account", function (done) { - chai.request(server.app) - .post(`/api/auth/confirm/${fakeToken}`) - .type("application/json") - .end(function (err, res) { - res.should.have.status(401); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.ACCOUNT_TOKEN_401_MESSAGE); - done(); - }); - }); +describe("POST confirm account", function() { + it("should SUCCEED and confirm the account", function(done) { + chai + .request(server.app) + .post(`/api/auth/confirm/${confirmationToken}`) + .type("application/json") + .end(function(err, res) { + res.should.have.status(200); + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.AUTH_CONFIRM_ACCOUNT); + done(); + }); + }); + it("should FAIL confirming the account", function(done) { + chai + .request(server.app) + .post(`/api/auth/confirm/${fakeToken}`) + .type("application/json") + .end(function(err, res) { + res.should.have.status(401); + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Error.ACCOUNT_TOKEN_401_MESSAGE + ); + done(); + }); + }); + it("should FAIL to confirm account that has token with email but no account", function(done) { + chai + .request(server.app) + .post(`/api/auth/confirm/${fakeToken}`) + .type("application/json") + .end(function(err, res) { + res.should.have.status(401); + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Error.ACCOUNT_TOKEN_401_MESSAGE + ); + done(); + }); + }); }); -describe("PATCH update account", function () { - const updatedInfo = { - "_id": teamHackerAccount0._id, - "firstName": "new", - "lastName": "name" - }; - - const failUpdatedInfo = { - "_id": Admin0._id, - "firstName": "fail", - "lastName": "fail" - }; - - // fail on authentication - it("should fail to update an account due to authentication", function (done) { - chai.request(server.app) - .patch(`/api/account/${updatedInfo._id}`) - .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(); - }); - }); - - // succeed on :all case - it("should SUCCEED and use admin to update another account", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - agent - .patch(`/api/account/${updatedInfo._id}`) - .type("application/json") - .send(updatedInfo) - .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.ACCOUNT_UPDATE); - res.body.should.have.property("data"); - // Is this correct matching of data? - res.body.data.firstName.should.equal(updatedInfo.firstName); - res.body.data.lastName.should.equal(updatedInfo.lastName); - done(); - }); +describe("PATCH update account", function() { + const updatedInfo = { + _id: teamHackerAccount0._id, + firstName: "new", + lastName: "name" + }; + + const failUpdatedInfo = { + _id: Admin0._id, + firstName: "fail", + lastName: "fail" + }; + + // fail on authentication + it("should fail to update an account due to authentication", function(done) { + chai + .request(server.app) + .patch(`/api/account/${updatedInfo._id}`) + .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(); + }); + }); + + // succeed on :all case + it("should SUCCEED and use admin to update another account", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + agent + .patch(`/api/account/${updatedInfo._id}`) + .type("application/json") + .send(updatedInfo) + .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.ACCOUNT_UPDATE); + res.body.should.have.property("data"); + // Is this correct matching of data? + res.body.data.firstName.should.equal(updatedInfo.firstName); + res.body.data.lastName.should.equal(updatedInfo.lastName); + done(); }); }); - - // succeed on :self case - it("should SUCCEED and update the user's own account", function (done) { - util.auth.login(agent, teamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - agent - .patch(`/api/account/${updatedInfo._id}`) - .type("application/json") - .send(updatedInfo) - .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.ACCOUNT_UPDATE); - res.body.should.have.property("data"); - // Is this correct matching of data? - res.body.data.firstName.should.equal(updatedInfo.firstName); - res.body.data.lastName.should.equal(updatedInfo.lastName); - done(); - }); + }); + + // succeed on :self case + it("should SUCCEED and update the user's own account", function(done) { + util.auth.login(agent, teamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + agent + .patch(`/api/account/${updatedInfo._id}`) + .type("application/json") + .send(updatedInfo) + .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.ACCOUNT_UPDATE); + res.body.should.have.property("data"); + // Is this correct matching of data? + res.body.data.firstName.should.equal(updatedInfo.firstName); + res.body.data.lastName.should.equal(updatedInfo.lastName); + done(); }); }); - - // fail due to lack of authorization - it("should Fail to update an account due to lack of authorization", function (done) { - util.auth.login(agent, teamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - agent - .patch(`/api/account/${failUpdatedInfo._id}`) - .type("application/json") - .send(updatedInfo) - .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(); - }); + }); + + // fail due to lack of authorization + it("should Fail to update an account due to lack of authorization", function(done) { + util.auth.login(agent, teamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + agent + .patch(`/api/account/${failUpdatedInfo._id}`) + .type("application/json") + .send(updatedInfo) + .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(); }); }); + }); }); -describe("POST reset password", function () { - const password = { - "password": "NewPassword" - }; - it("should SUCCEED and change the password", function (done) { - chai.request(server.app) - .post("/api/auth/password/reset") - .type("application/json") - .set("X-Reset-Token", resetToken) - .send(password) - .end(function (err, res) { - res.should.have.status(200); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.AUTH_RESET_PASSWORD); - done(); - }); - }); +describe("POST reset password", function() { + const password = { + password: "NewPassword" + }; + it("should SUCCEED and change the password", function(done) { + chai + .request(server.app) + .post("/api/auth/password/reset") + .type("application/json") + .set("X-Reset-Token", resetToken) + .send(password) + .end(function(err, res) { + res.should.have.status(200); + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.AUTH_RESET_PASSWORD); + done(); + }); + }); }); -describe("PATCH change password for logged in user", function () { - const successChangePassword = { - "oldPassword": Admin0.password, - "newPassword": "password12345" - }; - const failChangePassword = { - "oldPassword": "WrongPassword", - "newPassword": "password12345" - }; - // fail on authentication - it("should fail to change the user's password because they are not logged in", function (done) { - chai.request(server.app) - .patch("/api/auth/password/change") - .type("application/json") - .send(failChangePassword) - .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(); - }); - }); - // success case - it("should change the logged in user's password to a new password", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - agent - .patch("/api/auth/password/change") - .type("application/json") - .send(successChangePassword) - .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.AUTH_RESET_PASSWORD); - done(); - }); +describe("PATCH change password for logged in user", function() { + const successChangePassword = { + oldPassword: Admin0.password, + newPassword: "password12345" + }; + const failChangePassword = { + oldPassword: "WrongPassword", + newPassword: "password12345" + }; + // fail on authentication + it("should fail to change the user's password because they are not logged in", function(done) { + chai + .request(server.app) + .patch("/api/auth/password/change") + .type("application/json") + .send(failChangePassword) + .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(); + }); + }); + // success case + it("should change the logged in user's password to a new password", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + agent + .patch("/api/auth/password/change") + .type("application/json") + .send(successChangePassword) + .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.AUTH_RESET_PASSWORD); + done(); }); }); - // fail case because old password in incorrect - it("should fail to change the logged in user's password to a new password because old password is incorrect", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - agent - .patch("/api/auth/password/change") - .type("application/json") - .send(failChangePassword) - .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(); - }); + }); + // fail case because old password in incorrect + it("should fail to change the logged in user's password to a new password because old password is incorrect", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + agent + .patch("/api/auth/password/change") + .type("application/json") + .send(failChangePassword) + .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(); }); }); + }); }); -describe("GET retrieve permissions", function () { - it("should SUCCEED and retrieve the rolebindings for the user", function (done) { - util.auth.login(agent, teamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - agent - .get("/api/auth/rolebindings/" + teamHackerAccount0._id) - .type("application/json") - .end(function (err, res) { - res.should.have.status(200); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.AUTH_GET_ROLE_BINDINGS); - res.body.should.have.property("data"); - res.body.data.should.be.a("object"); - res.body.data.should.have.property("roles"); - res.body.data.should.have.property("accountId"); - res.body.data.accountId.should.equal(teamHackerAccount0._id.toHexString()); - done(); - }); +describe("GET retrieve permissions", function() { + it("should SUCCEED and retrieve the rolebindings for the user", function(done) { + util.auth.login(agent, teamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + agent + .get("/api/auth/rolebindings/" + teamHackerAccount0._id) + .type("application/json") + .end(function(err, res) { + res.should.have.status(200); + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Success.AUTH_GET_ROLE_BINDINGS + ); + res.body.should.have.property("data"); + res.body.data.should.be.a("object"); + res.body.data.should.have.property("roles"); + res.body.data.should.have.property("accountId"); + res.body.data.accountId.should.equal( + teamHackerAccount0._id.toHexString() + ); + done(); }); }); - it("should FAIL to retrieve the rolebindings as the account is not authenticated", function (done) { - chai.request(server.app) - .get("/api/auth/rolebindings/" + teamHackerAccount0._id) - .type("application/json") - .end(function (err, res) { - res.should.have.status(401); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); - done(); - }); - }); + }); + it("should FAIL to retrieve the rolebindings as the account is not authenticated", function(done) { + chai + .request(server.app) + .get("/api/auth/rolebindings/" + teamHackerAccount0._id) + .type("application/json") + .end(function(err, res) { + res.should.have.status(401); + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); + done(); + }); + }); }); -describe("GET resend confirmation email", function () { - it("should SUCCEED and resend the confirmation email", function (done) { - util.auth.login(agent, storedAccount1, (error) => { - if (error) { - agent.close(); - return done(error); - } - agent - .get("/api/auth/confirm/resend") - .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.AUTH_SEND_CONFIRMATION_EMAIL); - done(); - }); +describe("GET resend confirmation email", function() { + it("should SUCCEED and resend the confirmation email", function(done) { + util.auth.login(agent, storedAccount1, error => { + if (error) { + agent.close(); + return done(error); + } + agent + .get("/api/auth/confirm/resend") + .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.AUTH_SEND_CONFIRMATION_EMAIL + ); + done(); }); }); - it("should FAIL as the account is already confirmed", function (done) { - util.auth.login(agent, teamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - agent - .get("/api/auth/confirm/resend") - .type("application/json") - .end(function (err, res) { - res.should.have.status(422); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal("Account already confirmed"); - done(); - }); + }); + it("should FAIL as the account is already confirmed", function(done) { + util.auth.login(agent, teamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + agent + .get("/api/auth/confirm/resend") + .type("application/json") + .end(function(err, res) { + res.should.have.status(422); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal("Account already confirmed"); + done(); }); }); - it("should FAIL as account confirmation token does not exist", function (done) { - util.auth.login(agent, storedAccount3, (error) => { - if (error) { - agent.close(); - return done(error); - } - agent - .get("/api/auth/confirm/resend") - .type("application/json") - .end(function (err, res) { - res.should.have.status(428); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal("Account confirmation token does not exist"); - done(); - }); + }); + it("should FAIL as account confirmation token does not exist", function(done) { + util.auth.login(agent, storedAccount3, error => { + if (error) { + agent.close(); + return done(error); + } + agent + .get("/api/auth/confirm/resend") + .type("application/json") + .end(function(err, res) { + res.should.have.status(428); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + "Account confirmation token does not exist" + ); + done(); }); }); + }); }); -describe("POST invite account", function () { - it("Should succeed to invite a user to create an account", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); +describe("POST invite account", function() { + it("Should succeed to invite a user to create an account", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .post("/api/account/invite") + .type("application/json") + .send({ + email: newAccount0.email, + accountType: Constants.General.VOLUNTEER + }) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); } - return agent - .post("/api/account/invite") - .type("application/json") - .send({ - email: newAccount0.email, - accountType: Constants.General.VOLUNTEER - }) - // does not have password because of to stripped json - .end(function (err, res) { - if (err) { - return done(err); - } - res.should.have.status(200); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.ACCOUNT_INVITE); - done(); - }); - }); + res.should.have.status(200); + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.ACCOUNT_INVITE); + done(); + }) + ); }); + }); }); -describe("GET invites", function () { - it("Should FAIL to get all invites due to Authentication", function (done) { - chai.request(server.app) - .get("/api/account/invite") - .end(function (err, res) { - res.should.have.status(401); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); - done(); - }); +describe("GET invites", function() { + it("Should FAIL to get all invites due to Authentication", function(done) { + chai + .request(server.app) + .get("/api/account/invite") + .end(function(err, res) { + res.should.have.status(401); + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); + done(); + }); + }); + it("Should FAIL to get all invites due to Authorization", function(done) { + util.auth.login(agent, teamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent.get("/api/account/invite").end(function(err, res) { + res.should.have.status(403); + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.AUTH_403_MESSAGE); + done(); + }); }); - it("Should FAIL to get all invites due to Authorization", function (done) { - util.auth.login(agent, teamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/account/invite") - .end(function (err, res) { - res.should.have.status(403); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.AUTH_403_MESSAGE); - done(); - }); - }); + }); + it("Should SUCCEED to get all invites", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent.get("/api/account/invite").end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(200); + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.ACCOUNT_GET_INVITES); + res.body.should.have.property("data"); + res.body.data.should.have.property("invites"); + res.body.data.invites.length.should.equal( + util.accountConfirmation.AccountConfirmationTokens.length + ); + done(); + }); }); - it("Should SUCCEED to get all invites", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/account/invite") - .end(function (err, res) { - if (err) { - return done(err); - } - res.should.have.status(200); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.ACCOUNT_GET_INVITES); - res.body.should.have.property("data"); - res.body.data.should.have.property("invites"); - res.body.data.invites.length.should.equal(util.accountConfirmation.AccountConfirmationTokens.length); - done(); - }); - }); - }); -}); \ No newline at end of file + }); +}); diff --git a/tests/hacker.test.js b/tests/hacker.test.js index 2953af27..28b45c4e 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -9,16 +9,16 @@ const Hacker = require("../models/hacker.model"); const fs = require("fs"); const path = require("path"); const Constants = { - Success: require("../constants/success.constant"), - General: require("../constants/general.constant"), - Error: require("../constants/error.constant"), + Success: require("../constants/success.constant"), + General: require("../constants/general.constant"), + Error: require("../constants/error.constant") }; const util = { - auth: require("./util/auth.test.util"), - hacker: require("./util/hacker.test.util"), - account: require("./util/account.test.util"), - accountConfirmation: require("./util/accountConfirmation.test.util") + auth: require("./util/auth.test.util"), + hacker: require("./util/hacker.test.util"), + account: require("./util/account.test.util"), + accountConfirmation: require("./util/accountConfirmation.test.util") }; const StorageService = require("../services/storage.service"); @@ -29,7 +29,6 @@ const volunteerAccount0 = util.account.volunteerAccounts.stored[0]; const newHackerAccount0 = util.account.hackerAccounts.new[0]; const newHacker0 = util.hacker.newHacker0; const invalidHacker0 = util.hacker.invalidHacker0; - const newHacker1 = util.hacker.newHacker1; const noTeamHackerAccount0 = util.account.hackerAccounts.stored.noTeam[0]; @@ -43,977 +42,1050 @@ const duplicateAccountLinkHacker0 = util.hacker.duplicateAccountLinkHacker0; const invalidHacker1 = util.hacker.invalidHacker1; -describe("GET hacker", function () { - // fail on authentication - it("should fail to list a hacker's information on /api/hacker/:id GET due to authentication", function (done) { - chai.request(server.app) - .get(`/api/hacker/` + TeamHacker0._id) - .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(); - }); +describe("GET hacker", function() { + // fail on authentication + it("should fail to list a hacker's information on /api/hacker/:id GET due to authentication", function(done) { + chai + .request(server.app) + .get(`/api/hacker/` + TeamHacker0._id) + .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(); + }); + }); + + // success case + it("should list the user's hacker info on /api/hacker/self GET", function(done) { + util.auth.login(agent, teamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent.get("/api/hacker/self").end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.HACKER_READ); + res.body.should.have.property("data"); + + let hacker = new Hacker(TeamHacker0); + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(hacker.toJSON()) + ); + done(); + }); }); - - // success case - it("should list the user's hacker info on /api/hacker/self GET", function (done) { - util.auth.login(agent, teamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/hacker/self") - .end(function (err, res) { - if (err) { - return done(err); - } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.HACKER_READ); - res.body.should.have.property("data"); - - let hacker = new Hacker(TeamHacker0); - chai.assert.equal(JSON.stringify(res.body.data), JSON.stringify(hacker.toJSON())); - done(); - }); - }); + }); + + // fail case due to wrong account type + it("should fail to list the hacker info of an admin due to wrong account type /api/account/self GET", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent.get("/api/hacker/self").end(function(err, res) { + res.should.have.status(409); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.ACCOUNT_TYPE_409_MESSAGE); + done(); + }); }); - - // fail case due to wrong account type - it("should fail to list the hacker info of an admin due to wrong account type /api/account/self GET", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); + }); + + // succeed on admin case + it("should list a hacker's information using admin power on /api/hacker/:id GET", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .get(`/api/hacker/${TeamHacker0._id}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); } - return agent - .get("/api/hacker/self") - .end(function (err, res) { - res.should.have.status(409); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.ACCOUNT_TYPE_409_MESSAGE); - done(); - }); - }); + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.HACKER_READ); + res.body.should.have.property("data"); + + let hacker = new Hacker(TeamHacker0); + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(hacker.toJSON()) + ); + + done(); + }) + ); }); - - // succeed on admin case - it("should list a hacker's information using admin power on /api/hacker/:id GET", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); + }); + + // succeed on :self case + it("should list the user's hacker information on /api/hacker/:id GET", function(done) { + util.auth.login(agent, teamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .get(`/api/hacker/${TeamHacker0._id}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); } - return agent - .get(`/api/hacker/${TeamHacker0._id}`) - // does not have password because of to stripped json - .end(function (err, res) { - if (err) { - return done(err); - } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.HACKER_READ); - res.body.should.have.property("data"); - - let hacker = new Hacker(TeamHacker0); - chai.assert.equal(JSON.stringify(res.body.data), JSON.stringify(hacker.toJSON())); - - done(); - }); - }); + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.HACKER_READ); + res.body.should.have.property("data"); + + let hacker = new Hacker(TeamHacker0); + + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(hacker.toJSON()) + ); + + done(); + }) + ); }); - - // succeed on :self case - it("should list the user's hacker information on /api/hacker/:id GET", function (done) { - util.auth.login(agent, teamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); + }); + + // fail due to lack of authorization + it("should fail to list a hacker information due to lack of authorization on /api/hacker/:id GET", function(done) { + util.auth.login(agent, noTeamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .get(`/api/hacker/${TeamHacker0._id}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); } - return agent - .get(`/api/hacker/${TeamHacker0._id}`) - // does not have password because of to stripped json - .end(function (err, res) { - if (err) { - return done(err); - } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.HACKER_READ); - res.body.should.have.property("data"); - - let hacker = new Hacker(TeamHacker0); - - chai.assert.equal(JSON.stringify(res.body.data), JSON.stringify(hacker.toJSON())); - - done(); - }); - }); + 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(); + }) + ); }); - - // fail due to lack of authorization - it("should fail to list a hacker information due to lack of authorization on /api/hacker/:id GET", function (done) { - util.auth.login(agent, noTeamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get(`/api/hacker/${TeamHacker0._id}`) - // does not have password because of to stripped json - .end(function (err, res) { - if (err) { - return done(err); - } - 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(); - }); - }); - }); - - // fail due to lack of hacker - it("should fail to list an invalid hacker /api/hacker/:id GET", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get(`/api/hacker/${invalidHacker1._id}`) - .end(function (err, res) { - if (err) { - return done(err); - } - res.should.have.status(404); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.HACKER_404_MESSAGE); - res.body.should.have.property("data"); - - done(); - }); + }); + + // fail due to lack of hacker + it("should fail to list an invalid hacker /api/hacker/:id GET", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get(`/api/hacker/${invalidHacker1._id}`) + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(404); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.HACKER_404_MESSAGE); + res.body.should.have.property("data"); + + done(); }); }); - - // succeed on admin case - it("should list a hacker's information using admin power on /api/hacker/email/:email GET", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); + }); + + // succeed on admin case + it("should list a hacker's information using admin power on /api/hacker/email/:email GET", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .get(`/api/hacker/email/${teamHackerAccount0.email}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); } - return agent - .get(`/api/hacker/email/${teamHackerAccount0.email}`) - // does not have password because of to stripped json - .end(function (err, res) { - if (err) { - return done(err); - } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.HACKER_READ); - res.body.should.have.property("data"); - - let hacker = new Hacker(TeamHacker0); - chai.assert.equal(JSON.stringify(res.body.data), JSON.stringify(hacker.toJSON())); - - done(); - }); - }); + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.HACKER_READ); + res.body.should.have.property("data"); + + let hacker = new Hacker(TeamHacker0); + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(hacker.toJSON()) + ); + + done(); + }) + ); }); - - // succeed on :self case - it("should list the user's hacker information on /api/hacker/email/:email GET", function (done) { - util.auth.login(agent, teamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); + }); + + // succeed on :self case + it("should list the user's hacker information on /api/hacker/email/:email GET", function(done) { + util.auth.login(agent, teamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .get(`/api/hacker/email/${teamHackerAccount0.email}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); } - return agent - .get(`/api/hacker/email/${teamHackerAccount0.email}`) - // does not have password because of to stripped json - .end(function (err, res) { - if (err) { - return done(err); - } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.HACKER_READ); - res.body.should.have.property("data"); - - let hacker = new Hacker(TeamHacker0); - - chai.assert.equal(JSON.stringify(res.body.data), JSON.stringify(hacker.toJSON())); - - done(); - }); - }); + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.HACKER_READ); + res.body.should.have.property("data"); + + let hacker = new Hacker(TeamHacker0); + + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(hacker.toJSON()) + ); + + done(); + }) + ); }); - - // fail due to lack of authorization - it("should fail to list a hacker information due to lack of authorization on /api/hacker/email/:id GET", function (done) { - util.auth.login(agent, noTeamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); + }); + + // fail due to lack of authorization + it("should fail to list a hacker information due to lack of authorization on /api/hacker/email/:id GET", function(done) { + util.auth.login(agent, noTeamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .get(`/api/hacker/email/${teamHackerAccount0.email}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); } - return agent - .get(`/api/hacker/email/${teamHackerAccount0.email}`) - // does not have password because of to stripped json - .end(function (err, res) { - if (err) { - return done(err); - } - 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(); - }); - }); + 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(); + }) + ); }); + }); }); -describe("POST create hacker", function () { - // fail on authentication - it("should fail to create a new hacker due to lack of authentication", - function (done) { - chai.request(server.app) - .post(`/api/hacker/`) - .type("application/json") - .send(newHacker1) - .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(); - }); - }); - - // succeed on admin case - it("should SUCCEED and create a new hacker (with an account that has been confirmed) using admin credentials", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .post(`/api/hacker/`) - .type("application/json") - .send(newHacker0) - .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_CREATE); - res.body.should.have.property("data"); - - // create JSON version of model - // delete id as they will be different between model objects - // update status to be applied on the comparator hacker object - const hacker = (new Hacker(newHacker0)).toJSON(); - hacker.status = Constants.General.HACKER_STATUS_APPLIED; - delete res.body.data.id; - delete hacker.id; - chai.assert.equal(JSON.stringify(res.body.data), JSON.stringify(hacker)); - - done(); - }); +describe("POST create hacker", function() { + // fail on authentication + it("should fail to create a new hacker due to lack of authentication", function(done) { + chai + .request(server.app) + .post(`/api/hacker/`) + .type("application/json") + .send(newHacker1) + .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(); + }); + }); + + // succeed on admin case + it("should SUCCEED and create a new hacker (with an account that has been confirmed) using admin credentials", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + + return done(error); + } + return agent + .post(`/api/hacker/`) + .type("application/json") + .send(newHacker0) + .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_CREATE); + res.body.should.have.property("data"); + + // create JSON version of model + // delete id as they will be different between model objects + // update status to be applied on the comparator hacker object + const hacker = new Hacker(newHacker0).toJSON(); + hacker.status = Constants.General.HACKER_STATUS_APPLIED; + delete res.body.data.id; + delete hacker.id; + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(hacker), + "objects do not match" + ); + + done(); }); }); - - // succeed on user case - it("should SUCCEED and create a new hacker for user (with an account that has been confirmed)", function (done) { - util.auth.login(agent, newHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .post(`/api/hacker/`) - .type("application/json") - .send(newHacker0) - .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_CREATE); - res.body.should.have.property("data"); - - // create JSON version of model - // delete id as they will be different between model objects - // update status to be applied on the comparator hacker object - const hacker = (new Hacker(newHacker0)).toJSON(); - hacker.status = Constants.General.HACKER_STATUS_APPLIED; - delete res.body.data.id; - delete hacker.id; - chai.assert.equal(JSON.stringify(res.body.data), JSON.stringify(hacker)); - done(); - }); + }); + + // succeed on user case + it("should SUCCEED and create a new hacker for user (with an account that has been confirmed)", function(done) { + util.auth.login(agent, newHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .post(`/api/hacker/`) + .type("application/json") + .send(newHacker0) + .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_CREATE); + res.body.should.have.property("data"); + + // create JSON version of model + // delete id as they will be different between model objects + // update status to be applied on the comparator hacker object + const hacker = new Hacker(newHacker0).toJSON(); + hacker.status = Constants.General.HACKER_STATUS_APPLIED; + delete res.body.data.id; + delete hacker.id; + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(hacker) + ); + done(); }); }); - - // should fail due to 'false' on code of conduct - it("should FAIL if the new hacker does not accept code of conduct", function (done) { - util.auth.login(agent, newHacker0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .post(`/api/hacker/`) - .type("application/json") - .send(invalidHacker0) - .end(function (err, res) { - res.should.have.status(422); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal("Validation failed"); - res.body.should.have.property("data"); - res.body.data.should.have.property("codeOfConduct"); - res.body.data.codeOfConduct.msg.should.equal("Must be equal to true"); - done(); - }); + }); + + // should fail due to 'false' on code of conduct + it("should FAIL if the new hacker does not accept code of conduct", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .post(`/api/hacker/`) + .type("application/json") + .send(invalidHacker0) + .end(function(err, res) { + console.log(res); + res.should.have.status(422); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal("Validation failed"); + res.body.should.have.property("data"); + res.body.data.should.have.property("application"); + res.body.data.application.should.have.property("other"); + res.body.data.application.other.should.have.property( + "codeOfConduct_MLH" + ); + res.body.data.application.other.should.have.property( + "codeOfConduct_MCHACKS" + ); + res.body.data.application.other.codeOfConduct_MLH.msg.should.equal( + "Must be equal to true" + ); + res.body.data.application.other.codeOfConduct_MCHACKS.msh.should.equal( + "Must be equal to true" + ); + done(); }); }); - - // fail on unconfirmed account, using admin - it("should FAIL to create a new hacker if the account hasn't been confirmed", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .post(`/api/hacker/`) - .type("application/json") - .send(util.hacker.unconfirmedAccountHacker0) - .end(function (err, res) { - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.ACCOUNT_403_MESSAGE); - res.should.have.status(403); - done(); - }); + }); + + // fail on unconfirmed account, using admin + it("should FAIL to create a new hacker if the account hasn't been confirmed", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .post(`/api/hacker/`) + .type("application/json") + .send(util.hacker.unconfirmedAccountHacker0) + .end(function(err, res) { + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.ACCOUNT_403_MESSAGE); + res.should.have.status(403); + done(); }); }); - - // fail due to duplicate accountId - it("should FAIL to create new hacker due to duplicate account link", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .post(`/api/hacker/`) - .type("application/json") - .send(duplicateAccountLinkHacker0) - .end(function (err, res) { - res.should.have.status(409); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.HACKER_ID_409_MESSAGE); - res.body.should.have.property("data"); - done(); - }); + }); + + // fail due to duplicate accountId + it("should FAIL to create new hacker due to duplicate account link", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .post(`/api/hacker/`) + .type("application/json") + .send(duplicateAccountLinkHacker0) + .end(function(err, res) { + res.should.have.status(409); + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.HACKER_ID_409_MESSAGE); + res.body.should.have.property("data"); + done(); }); }); - - // fail on invalid input - it("should FAIL to create new hacker due to invalid input", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .post(`/api/hacker/`) - .type("application/json") - .send(invalidHacker1) - .end(function (err, res) { - // replace with actual test comparisons after error handler is implemented - res.should.have.status(422); - done(); - }); + }); + + // fail on invalid input + it("should FAIL to create new hacker due to invalid input", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .post(`/api/hacker/`) + .type("application/json") + .send(invalidHacker1) + .end(function(err, res) { + // replace with actual test comparisons after error handler is implemented + res.should.have.status(422); + done(); }); }); + }); }); -describe("PATCH update one hacker", function () { - // fail on authentication - it("should fail to update a hacker on /api/hacker/:id GET due to authentication", function (done) { - chai.request(server.app) - .patch(`/api/hacker/${TeamHacker0._id}`) - .type("application/json") - .send({ - gender: "Other" - }) - .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 succeed on admin case - it("should SUCCEED and update a hacker using admin power", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/${TeamHacker0._id}`) - .type("application/json") - .send({ - gender: "Other" - }) - .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(JSON.stringify(res.body.data), JSON.stringify({ - gender: "Other" - })); - done(); - }); +describe("PATCH update one hacker", function() { + // fail on authentication + it("should fail to update a hacker on /api/hacker/:id GET due to authentication", function(done) { + chai + .request(server.app) + .patch(`/api/hacker/${TeamHacker0._id}`) + .type("application/json") + .send({ + gender: "Other" + }) + .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 succeed on admin case + it("should SUCCEED and update a hacker using admin power", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + let app = TeamHacker0.application; + app.other.gender = "Other"; + return agent + .patch(`/api/hacker/${TeamHacker0._id}`) + .type("application/json") + .send({ + application: app + }) + .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( + JSON.stringify(res.body.data.application.other.gender), + '"Other"' + ); + done(); }); }); - - it("should SUCCEED and update a hacker STATUS as an Admin", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/status/${TeamHacker0._id}`) - .type("application/json") - .send({ - status: "Accepted" - }) - .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(JSON.stringify(res.body.data), JSON.stringify({ - status: "Accepted" - })); - done(); - }); + }); + + it("should SUCCEED and update a hacker STATUS as an Admin", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/status/${TeamHacker0._id}`) + .type("application/json") + .send({ + status: "Accepted" + }) + .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( + JSON.stringify(res.body.data), + JSON.stringify({ + status: "Accepted" + }) + ); + done(); }); }); - - it("should FAIL and NOT update a hacker STATUS as a Hacker", function (done) { - util.auth.login(agent, teamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/status/${TeamHacker0._id}`) - .type("application/json") - .send({ - status: "Accepted" - }) - .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 and NOT update a hacker STATUS as a Hacker", function(done) { + util.auth.login(agent, teamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/status/${TeamHacker0._id}`) + .type("application/json") + .send({ + status: "Accepted" + }) + .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(); }); }); - - // volunteer should successfully checkin hacker - it("should SUCCEED and check in hacker as a volunteer", function (done) { - util.auth.login(agent, volunteerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/checkin/${TeamHacker0._id}`) - .type("application/json") - .send({ - status: "Checked-in" - }) - .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(JSON.stringify(res.body.data), JSON.stringify({ - status: "Checked-in" - })); - done(); - }); + }); + + // volunteer should successfully checkin hacker + it("should SUCCEED and check in hacker as a volunteer", function(done) { + util.auth.login(agent, volunteerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/checkin/${TeamHacker0._id}`) + .type("application/json") + .send({ + status: "Checked-in" + }) + .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( + JSON.stringify(res.body.data), + JSON.stringify({ + status: "Checked-in" + }) + ); + done(); }); }); - - // hacker should fail to checkin hacker - it("should FAIL to check in hacker as a hacker", function (done) { - util.auth.login(agent, teamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/checkin/${TeamHacker0._id}`) - .type("application/json") - .send({ - status: "Checked-in" - }) - .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(); - }); + }); + + // hacker should fail to checkin hacker + it("should FAIL to check in hacker as a hacker", function(done) { + util.auth.login(agent, teamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/checkin/${TeamHacker0._id}`) + .type("application/json") + .send({ + status: "Checked-in" + }) + .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(); }); }); - - // should succeed on hacker case - it("should SUCCEED and update the user's hacker info", function (done) { - util.auth.login(agent, noTeamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/${noTeamHacker0._id}`) - .type("application/json") - .send({ - gender: "Other" - }) - .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(JSON.stringify(res.body.data), JSON.stringify({ - gender: "Other" - })); - done(); - }); + }); + + // should succeed on hacker case + it("should SUCCEED and update the user's hacker info", function(done) { + util.auth.login(agent, noTeamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + let app = noTeamHacker0.application; + app.other.gender = "Other"; + return agent + .patch(`/api/hacker/${noTeamHacker0._id}`) + .type("application/json") + .send({ + application: app + }) + .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( + JSON.stringify(res.body.data.application.other.gender), + '"Other"' + ); + done(); }); }); - - // should fail due to authorization - it("should Fail to update hacker info due to lack of authorization", function (done) { - util.auth.login(agent, noTeamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/${TeamHacker0._id}`) - .type("application/json") - .send({ - gender: "Other" - }) - .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(); - }); + }); + + // should fail due to authorization + it("should Fail to update hacker info due to lack of authorization", function(done) { + util.auth.login(agent, noTeamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/${TeamHacker0._id}`) + .type("application/json") + .send({ + gender: "Other" + }) + .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(); }); }); - - // fail due to lack of hacker - it("should fail to change an invalid hacker's info", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get(`/api/hacker/${invalidHacker1._id}`) - .end(function (err, res) { - if (err) { - return done(err); - } - res.should.have.status(404); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.HACKER_404_MESSAGE); - res.body.should.have.property("data"); - - done(); - }); + }); + + // fail due to lack of hacker + it("should fail to change an invalid hacker's info", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get(`/api/hacker/${invalidHacker1._id}`) + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(404); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.HACKER_404_MESSAGE); + res.body.should.have.property("data"); + + done(); }); }); + }); + + // Succeed and change accepted to confirm + it("should succeed for hacker to update their own status from accepted to confirmed", function(done) { + util.auth.login(agent, noTeamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/confirmation/${noTeamHacker0._id}`) + .type("application/json") + .send({ + confirm: true + }) + .end(function(err, res) { + if (err) { + return done(err); + } + 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( + JSON.stringify(res.body.data), + JSON.stringify({ + status: Constants.General.HACKER_STATUS_CONFIRMED + }) + ); - // Succeed and change accepted to confirm - it("should succeed for hacker to update their own status from accepted to confirmed", function (done) { - util.auth.login(agent, noTeamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/confirmation/${noTeamHacker0._id}`) - .type("application/json") - .send({ - confirm: true - }) - .end(function (err, res) { - if (err) { - return done(err); - } - 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(JSON.stringify(res.body.data), JSON.stringify({ - status: Constants.General.HACKER_STATUS_CONFIRMED - })); - - done(); - }); + done(); }); }); + }); + + // Succeed and change confirmed to accepted + it("should succeed for hacker to update their own status from confirmed to accepted", function(done) { + util.auth.login(agent, teamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/confirmation/${TeamHacker0._id}`) + .type("application/json") + .send({ + confirm: false + }) + .end(function(err, res) { + if (err) { + return done(err); + } + 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( + JSON.stringify(res.body.data), + JSON.stringify({ + status: Constants.General.HACKER_STATUS_CANCELLED + }) + ); - // Succeed and change confirmed to accepted - it("should succeed for hacker to update their own status from confirmed to accepted", function (done) { - util.auth.login(agent, teamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/confirmation/${TeamHacker0._id}`) - .type("application/json") - .send({ - confirm: false - }) - .end(function (err, res) { - if (err) { - return done(err); - } - 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(JSON.stringify(res.body.data), JSON.stringify({ - status: Constants.General.HACKER_STATUS_CANCELLED - })); - - done(); - }); + done(); }); }); - - // fail for a hacker that's not accepted - it("should fail to update hacker status when hacker status is not accepted or confirmed", function (done) { - util.auth.login(agent, util.account.waitlistedHacker0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/confirmation/${util.hacker.waitlistedHacker0._id}`) - .type("application/json") - .send({ - confirm: true - }) - .end(function (err, res) { - if (err) { - return done(err); - } - res.should.have.status(409); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.HACKER_STATUS_409_MESSAGE); - res.body.should.have.property("data"); - - done(); - }); + }); + + // fail for a hacker that's not accepted + it("should fail to update hacker status when hacker status is not accepted or confirmed", function(done) { + util.auth.login(agent, util.account.waitlistedHacker0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/confirmation/${util.hacker.waitlistedHacker0._id}`) + .type("application/json") + .send({ + confirm: true + }) + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(409); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Error.HACKER_STATUS_409_MESSAGE + ); + res.body.should.have.property("data"); + + done(); }); }); - - // fail for a hacker that's not accepted - it("should fail for hacker trying to confirm someone else", function (done) { - util.auth.login(agent, util.account.waitlistedHacker0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/confirmation/${noTeamHacker0._id}`) - .type("application/json") - .send({ - confirm: true - }) - .end(function (err, res) { - if (err) { - return done(err); - } - 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(); - }); + }); + + // fail for a hacker that's not accepted + it("should fail for hacker trying to confirm someone else", function(done) { + util.auth.login(agent, util.account.waitlistedHacker0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/confirmation/${noTeamHacker0._id}`) + .type("application/json") + .send({ + confirm: true + }) + .end(function(err, res) { + if (err) { + return done(err); + } + 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(); }); }); + }); }); -describe("POST add a hacker resume", function () { - it("It should SUCCEED and upload a resume for a hacker", function (done) { - //this takes a lot of time for some reason - util.auth.login(agent, noTeamHacker0, (error) => { - if (error) { - return done(error); - } - return agent - .post(`/api/hacker/resume/${noTeamHacker0._id}`) - .type("multipart/form-data") - .attach("resume", fs.createReadStream(path.join(__dirname, "testResume.pdf")), { - contentType: "application/pdf" - }) - .end(function (err, res) { - res.should.have.status(200); - res.should.have.property("body"); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.RESUME_UPLOAD); - res.body.should.have.property("data"); - res.body.data.should.have.property("filename"); - StorageService.download(res.body.data.filename).then((value) => { - const actualFile = fs.readFileSync(path.join(__dirname, "testResume.pdf")); - chai.assert.equal(value[0].length, actualFile.length); - StorageService.delete(res.body.data.filename).then(() => { - done(); - }).catch(done); - }); - }); +describe("POST add a hacker resume", function() { + it("It should SUCCEED and upload a resume for a hacker", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, noTeamHacker0, error => { + if (error) { + return done(error); + } + return agent + .post(`/api/hacker/resume/${noTeamHacker0._id}`) + .type("multipart/form-data") + .attach( + "resume", + fs.createReadStream(path.join(__dirname, "testResume.pdf")), + { + contentType: "application/pdf" + } + ) + .end(function(err, res) { + res.should.have.status(200); + res.should.have.property("body"); + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.RESUME_UPLOAD); + res.body.should.have.property("data"); + res.body.data.should.have.property("filename"); + StorageService.download(res.body.data.filename).then(value => { + const actualFile = fs.readFileSync( + path.join(__dirname, "testResume.pdf") + ); + chai.assert.equal(value[0].length, actualFile.length); + StorageService.delete(res.body.data.filename) + .then(() => { + done(); + }) + .catch(done); + }); }); }); + }); }); -describe("GET Hacker stats", function () { - it("It should FAIL and get hacker stats (invalid validation)", function (done) { - //this takes a lot of time for some reason - util.auth.login(agent, Admin0, (error) => { - if (error) { - return done(error); - } - return agent - .get(`/api/hacker/stats`) - .end(function (err, res) { - res.should.have.status(422); - res.should.have.property("body"); - res.body.should.have.property("message"); - done(); - }); - }); +describe("GET Hacker stats", function() { + it("It should FAIL and get hacker stats (invalid validation)", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, Admin0, error => { + if (error) { + return done(error); + } + return agent.get(`/api/hacker/stats`).end(function(err, res) { + res.should.have.status(422); + res.should.have.property("body"); + res.body.should.have.property("message"); + done(); + }); }); - it("It should SUCCEED and get hacker stats", function (done) { - //this takes a lot of time for some reason - util.auth.login(agent, Admin0, (error) => { - if (error) { - return done(error); - } - return agent - .get(`/api/hacker/stats`) - .query({ - model: "hacker", - q: JSON.stringify([]) - }) - .end(function (err, res) { - res.should.have.status(200); - res.should.have.property("body"); - res.body.should.have.property("message"); - res.body.message.should.equal("Retrieved stats"); - res.body.should.have.property("data"); - res.body.data.should.have.property("stats"); - res.body.data.stats.should.have.property("total"); - res.body.data.stats.should.have.property("status"); - res.body.data.stats.should.have.property("school"); - res.body.data.stats.should.have.property("degree"); - res.body.data.stats.should.have.property("gender"); - res.body.data.stats.should.have.property("needsBus"); - res.body.data.stats.should.have.property("ethnicity"); - res.body.data.stats.should.have.property("jobInterest"); - res.body.data.stats.should.have.property("major"); - res.body.data.stats.should.have.property("graduationYear"); - res.body.data.stats.should.have.property("dietaryRestrictions"); - res.body.data.stats.should.have.property("shirtSize"); - res.body.data.stats.should.have.property("age"); - done(); - }); - }); - }); - it("It should FAIL and get hacker stats due to invalid Authorization", function (done) { - //this takes a lot of time for some reason - util.auth.login(agent, teamHackerAccount0, (error) => { - if (error) { - return done(error); - } - return agent - .get(`/api/hacker/stats`) - .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("It should SUCCEED and get hacker stats", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, Admin0, error => { + if (error) { + return done(error); + } + return agent + .get(`/api/hacker/stats`) + .query({ + model: "hacker", + q: JSON.stringify([]) + }) + .end(function(err, res) { + res.should.have.status(200); + res.should.have.property("body"); + res.body.should.have.property("message"); + res.body.message.should.equal("Retrieved stats"); + res.body.should.have.property("data"); + res.body.data.should.have.property("stats"); + res.body.data.stats.should.have.property("total"); + res.body.data.stats.should.have.property("status"); + res.body.data.stats.should.have.property("school"); + res.body.data.stats.should.have.property("degree"); + res.body.data.stats.should.have.property("gender"); + res.body.data.stats.should.have.property("needsBus"); + res.body.data.stats.should.have.property("ethnicity"); + res.body.data.stats.should.have.property("jobInterest"); + res.body.data.stats.should.have.property("fieldOfStudy"); + res.body.data.stats.should.have.property("graduationYear"); + res.body.data.stats.should.have.property("dietaryRestrictions"); + res.body.data.stats.should.have.property("shirtSize"); + res.body.data.stats.should.have.property("age"); + done(); }); }); - it("It should FAIL and get hacker stats due to invalid Authentication", function (done) { - //this takes a lot of time for some reason - chai.request(server.app) - .get(`/api/hacker/stats`) - .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(); - }); + }); + it("It should FAIL and get hacker stats due to invalid Authorization", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, teamHackerAccount0, error => { + if (error) { + return done(error); + } + return agent.get(`/api/hacker/stats`).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("It should FAIL and get hacker stats due to invalid Authentication", function(done) { + //this takes a lot of time for some reason + chai + .request(server.app) + .get(`/api/hacker/stats`) + .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(); + }); + }); }); -describe("POST send week-of email", function () { - it("It should FAIL to send the week-of email due to invalid Authentication", function (done) { - //this takes a lot of time for some reason - chai.request(server.app) - .post(`/api/hacker/email/weekOf/${noTeamHacker0._id}`) - .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(); - }); - }); - it("It should FAIL to send the week-of email due to invalid Authorization", function (done) { - //this takes a lot of time for some reason - util.auth.login(agent, noTeamHacker0, (error) => { - if (error) { - return done(error); - } - return agent - .post(`/api/hacker/email/weekOf/${noTeamHacker0._id}`) - .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(); - }); +describe("POST send week-of email", function() { + it("It should FAIL to send the week-of email due to invalid Authentication", function(done) { + //this takes a lot of time for some reason + chai + .request(server.app) + .post(`/api/hacker/email/weekOf/${noTeamHacker0._id}`) + .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(); + }); + }); + it("It should FAIL to send the week-of email due to invalid Authorization", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, noTeamHacker0, error => { + if (error) { + return done(error); + } + return agent + .post(`/api/hacker/email/weekOf/${noTeamHacker0._id}`) + .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("It should SUCCEED to send the week-of email", function (done) { - //this takes a lot of time for some reason - util.auth.login(agent, Admin0, (error) => { - if (error) { - return done(error); - } - return agent - .post(`/api/hacker/email/weekOf/${TeamHacker0._id}`) - .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_SENT_WEEK_OF); - res.body.should.have.property("data"); - done(); - }); + }); + it("It should SUCCEED to send the week-of email", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, Admin0, error => { + if (error) { + return done(error); + } + return agent + .post(`/api/hacker/email/weekOf/${TeamHacker0._id}`) + .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_SENT_WEEK_OF); + res.body.should.have.property("data"); + done(); }); }); + }); }); -describe("POST send day-of email", function () { - it("It should FAIL to send the day-of email due to invalid Authentication", function (done) { - //this takes a lot of time for some reason - chai.request(server.app) - .post(`/api/hacker/email/dayOf/${TeamHacker1._id}`) - .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(); - }); - }); - it("It should FAIL to send the day-of email due to invalid Authorization", function (done) { - //this takes a lot of time for some reason - util.auth.login(agent, teamHackerAccount1, (error) => { - if (error) { - return done(error); - } - return agent - .post(`/api/hacker/email/dayOf/${TeamHacker1._id}`) - .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(); - }); +describe("POST send day-of email", function() { + it("It should FAIL to send the day-of email due to invalid Authentication", function(done) { + //this takes a lot of time for some reason + chai + .request(server.app) + .post(`/api/hacker/email/dayOf/${TeamHacker1._id}`) + .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(); + }); + }); + it("It should FAIL to send the day-of email due to invalid Authorization", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, teamHackerAccount1, error => { + if (error) { + return done(error); + } + return agent + .post(`/api/hacker/email/dayOf/${TeamHacker1._id}`) + .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("It should SUCCEED to send the day-of email", function (done) { - //this takes a lot of time for some reason - util.auth.login(agent, Admin0, (error) => { - if (error) { - return done(error); - } - return agent - .post(`/api/hacker/email/dayOf/${TeamHacker1._id}`) - .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_SENT_DAY_OF); - res.body.should.have.property("data"); - done(); - }); + }); + it("It should SUCCEED to send the day-of email", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, Admin0, error => { + if (error) { + return done(error); + } + return agent + .post(`/api/hacker/email/dayOf/${TeamHacker1._id}`) + .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_SENT_DAY_OF); + res.body.should.have.property("data"); + done(); }); }); -}); \ No newline at end of file + }); +}); diff --git a/tests/search.service.spec.js b/tests/search.service.spec.js index 78bf1d59..bf141a76 100644 --- a/tests/search.service.spec.js +++ b/tests/search.service.spec.js @@ -11,281 +11,297 @@ const should = chai.should(); const logger = require("../services/logger.service"); const Constants = { - Error: require("../constants/error.constant"), + Error: require("../constants/error.constant") }; const util = { - hacker: require("./util/hacker.test.util"), - account: require("./util/account.test.util"), - auth: require("./util/auth.test.util") + hacker: require("./util/hacker.test.util"), + account: require("./util/account.test.util"), + auth: require("./util/auth.test.util") }; -const queryToExecute = [{ - param: "gender", +const queryToExecute = [ + { + param: "application.other.gender", operation: "equals", value: "Female" -}]; + } +]; -const query2 = [{ - param: "school", +const query2 = [ + { + param: "application.general.school", operation: "ne", value: "McGill" -}]; + } +]; -const badQuery = [{ +const badQuery = [ + { param: "password", operation: "equals", value: "passowrd" -}]; + } +]; const Admin0 = util.account.staffAccounts.stored[0]; const noTeamHackerAccount0 = util.account.hackerAccounts.stored.noTeam[0]; -describe("Searching for hackers", function () { - it("Should FAIL to search due to invalid authentication", function (done) { - util.auth.login(agent, { - email: "abc", - password: "def" - }, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(queryToExecute) - }) - .end(function (err, res) { - res.should.have.status(401); - res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); - res.body.should.have.property("data"); - done(); - }); - }); - }); +describe("Searching for hackers", function() { + it("Should FAIL to search due to invalid authentication", function(done) { + util.auth.login( + agent, + { + email: "abc", + password: "def" + }, + error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(queryToExecute) + }) + .end(function(err, res) { + res.should.have.status(401); + res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); + res.body.should.have.property("data"); + done(); + }); + } + ); + }); - it("Should FAIL to search due to invalid authorization", function (done) { - util.auth.login(agent, noTeamHackerAccount0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(queryToExecute) - }) - .end(function (err, res) { - res.should.have.status(403); - res.body.message.should.equal(Constants.Error.AUTH_403_MESSAGE); - res.body.should.have.property("data"); - done(); - }); + it("Should FAIL to search due to invalid authorization", function(done) { + util.auth.login(agent, noTeamHackerAccount0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(queryToExecute) + }) + .end(function(err, res) { + res.should.have.status(403); + res.body.message.should.equal(Constants.Error.AUTH_403_MESSAGE); + res.body.should.have.property("data"); + done(); }); }); - it("Should return all female hackers", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(queryToExecute) - }) - .end(function (err, res) { - res.should.have.status(200); - res.body.should.have.property("data"); - res.body.data.should.have.length(6); - done(); - }); + }); + it("Should return all female hackers", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(queryToExecute) + }) + .end(function(err, res) { + res.should.have.status(200); + res.body.should.have.property("data"); + res.body.data.should.have.length(6); + done(); }); }); - it("Should return an error as hackers don't have password stored", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(badQuery) - }) - .end(function (err, res) { - res.should.have.status(422); - done(); - }); + }); + it("Should return an error as hackers don't have password stored", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(badQuery) + }) + .end(function(err, res) { + res.should.have.status(422); + done(); }); }); + }); - it("Should return an error as staff aren't searchable", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "staff", - q: JSON.stringify(badQuery) - }) - .end(function (err, res) { - res.should.have.status(422); - res.body.data.model.msg.should.equal("Must be a valid searchable model"); - done(); - }); + it("Should return an error as staff aren't searchable", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "staff", + q: JSON.stringify(badQuery) + }) + .end(function(err, res) { + res.should.have.status(422); + res.body.data.model.msg.should.equal( + "Must be a valid searchable model" + ); + done(); }); }); - it("Should throw an error because model is not lowercase", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "Hacker", - q: JSON.stringify(query2) - }) - .end(function (err, res) { - res.should.have.status(422); - res.body.data.model.msg.should.equal("Model must be lower case"); - done(); - }); + }); + it("Should throw an error because model is not lowercase", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "Hacker", + q: JSON.stringify(query2) + }) + .end(function(err, res) { + res.should.have.status(422); + res.body.data.model.msg.should.equal("Model must be lower case"); + done(); }); }); - it("Should throw an error because of a fake model", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hackerz", - q: JSON.stringify(query2) - }) - .end(function (err, res) { - res.should.have.status(422); - res.body.data.model.msg.should.equal("Must be a valid searchable model"); - done(); - }); + }); + it("Should throw an error because of a fake model", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hackerz", + q: JSON.stringify(query2) + }) + .end(function(err, res) { + res.should.have.status(422); + res.body.data.model.msg.should.equal( + "Must be a valid searchable model" + ); + done(); }); }); - it("Should only return 1 hacker (page size)", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(query2), - limit: 1 - }) - .end(function (err, res) { - res.should.have.status(200); - res.body.data.should.have.length(1); - done(); - }); + }); + it("Should only return 1 hacker (page size)", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(query2), + limit: 1 + }) + .end(function(err, res) { + res.should.have.status(200); + res.body.data.should.have.length(1); + done(); }); }); - it("Should only return 1 hacker (pagination)", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - //There are two test samples so by making limit 1, there will be something on the second page - .query({ - model: "hacker", - q: JSON.stringify(query2), - limit: 1, - page: 1 - }) - .end(function (err, res) { - res.should.have.status(200); - res.body.data.should.have.length(1); - done(); - }); - }); + }); + it("Should only return 1 hacker (pagination)", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .get("/api/search") + //There are two test samples so by making limit 1, there will be something on the second page + .query({ + model: "hacker", + q: JSON.stringify(query2), + limit: 1, + page: 1 + }) + .end(function(err, res) { + res.should.have.status(200); + res.body.data.should.have.length(1); + done(); + }) + ); }); - it("Should throw an error because out of bounds (page size)", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(query2), - limit: 5000 - }) - .end(function (err, res) { - res.should.have.status(422); - done(); - }); + }); + it("Should throw an error because out of bounds (page size)", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(query2), + limit: 5000 + }) + .end(function(err, res) { + res.should.have.status(422); + done(); }); }); - it("Should throw an error because out of bounds (pagination)", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(query2), - limit: 1, - page: -1 - }) - .end(function (err, res) { - res.should.have.status(422); - done(); - }); + }); + it("Should throw an error because out of bounds (pagination)", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(query2), + limit: 1, + page: -1 + }) + .end(function(err, res) { + res.should.have.status(422); + done(); }); }); + }); - it("Should expand the accountId when expand is set to true", function (done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(queryToExecute), - expand: true - }) - .end(function (err, res) { - res.should.have.status(200); - res.body.should.have.property("data"); - res.body.data.should.have.length(6); - res.body.data[0].should.have.property("accountId"); - res.body.data[0].accountId.should.have.property("email"); - done(); - }); + it("Should expand the accountId when expand is set to true", function(done) { + util.auth.login(agent, Admin0, error => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(queryToExecute), + expand: true + }) + .end(function(err, res) { + res.should.have.status(200); + res.body.should.have.property("data"); + res.body.data.should.have.length(6); + res.body.data[0].should.have.property("accountId"); + res.body.data[0].accountId.should.have.property("email"); + done(); }); }); -}); \ No newline at end of file + }); +}); diff --git a/tests/util/hacker.test.util.js b/tests/util/hacker.test.util.js index a34c6565..c32402aa 100644 --- a/tests/util/hacker.test.util.js +++ b/tests/util/hacker.test.util.js @@ -1,9 +1,9 @@ "use strict"; const Util = { - Account: require("./account.test.util"), + Account: require("./account.test.util") }; const Constants = { - MongoId: require("../../constants/testMongoId.constant"), + MongoId: require("../../constants/testMongoId.constant") }; const mongoose = require("mongoose"); @@ -11,410 +11,555 @@ const Hacker = require("../../models/hacker.model"); const logger = require("../../services/logger.service"); const TeamHacker0 = { - "_id": Constants.MongoId.hackerAId, - "accountId": Util.Account.hackerAccounts.stored.team[0]._id, - "status": "Confirmed", - "school": "University of Blah", - "degree": "Masters", - "gender": "Male", - "needsBus": true, - "application": { - "portfolioURL": { - //gcloud bucket link - "resume": "www.gcloud.com/myResume100", - "github": "www.github.com/Person1", - "dropler": undefined, - "personal": "www.person1.com", - "linkedIn": "www.linkedin.com/in/Person1", - "other": undefined - }, - "jobInterest": "Full-time", - "skills": ["CSS", "HTML", "JS"], - }, - "ethnicity": ["Native American"], - "major": ["EE"], - "graduationYear": 2019, - "codeOfConduct": true, - "teamId": Constants.MongoId.team1Id, + _id: Constants.MongoId.hackerAId, + accountId: Util.Account.hackerAccounts.stored.team[0]._id, + status: "Confirmed", + application: { + general: { + school: "University of Blah", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Full-time", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume100", + github: "www.github.com/Person1", + dribbble: undefined, + personal: "www.person1.com", + linkedIn: "www.linkedin.com/in/Person1", + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Male", + ethnicity: ["Native American"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: true + } + }, + teamId: Constants.MongoId.team1Id }; const TeamHacker1 = { - "_id": Constants.MongoId.hackerDId, - "accountId": Util.Account.hackerAccounts.stored.team[1]._id, - "status": "Checked-in", - "school": "University of Blah1", - "degree": "Masters", - "gender": "Female", - "needsBus": false, - "application": { - "portfolioURL": { - //gcloud bucket link - "resume": "www.gcloud.com/myResume2", - "github": "www.github.com/Personasdf", - "dropler": undefined, - "personal": undefined, - "linkedIn": undefined, - "other": undefined - }, - "jobInterest": "Internship", - "skills": ["CSS", "HTML", "JS"], - }, - "ethnicity": ["European"], - "major": ["EE"], - "graduationYear": 2019, - "codeOfConduct": true, - "teamId": Constants.MongoId.team3Id, + _id: Constants.MongoId.hackerDId, + accountId: Util.Account.hackerAccounts.stored.team[1]._id, + status: "Checked-in", + application: { + general: { + school: "University of Blah", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Internship", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume2", + github: "www.github.com/Personasdf", + dribbble: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } + }, + teamId: Constants.MongoId.team3Id }; const TeamHacker2 = { - "_id": Constants.MongoId.hackerEId, - "accountId": Util.Account.hackerAccounts.stored.team[2]._id, - "status": "Waitlisted", - "school": "University of Blah1", - "degree": "Masters", - "gender": "Female", - "needsBus": false, - "application": { - "portfolioURL": { - //gcloud bucket link - "resume": "www.gcloud.com/myResume2", - "github": "www.github.com/Personasdf", - "dropler": undefined, - "personal": undefined, - "linkedIn": undefined, - "other": undefined - }, - "jobInterest": "Internship", - "skills": ["CSS", "HTML", "JS"], - }, - "ethnicity": ["European"], - "major": ["EE"], - "graduationYear": 2019, - "codeOfConduct": true, - "teamId": Constants.MongoId.team3Id, + _id: Constants.MongoId.hackerEId, + accountId: Util.Account.hackerAccounts.stored.team[2]._id, + status: "Waitlisted", + application: { + general: { + school: "University of Blah", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Internship", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume2", + github: "www.github.com/Personasdf", + dribbble: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } + }, + teamId: Constants.MongoId.team3Id }; const TeamHacker3 = { - "_id": Constants.MongoId.hackerFId, - "accountId": Util.Account.hackerAccounts.stored.team[3]._id, - "status": "Waitlisted", - "school": "University of Blah1", - "degree": "Masters", - "gender": "Female", - "needsBus": false, - "application": { - "portfolioURL": { - //gcloud bucket link - "resume": "www.gcloud.com/myResume2", - "github": "www.github.com/Personasdf", - "dropler": undefined, - "personal": undefined, - "linkedIn": undefined, - "other": undefined - }, - "jobInterest": "Internship", - "skills": ["CSS", "HTML", "JS"], - }, - "ethnicity": ["European"], - "major": ["EE"], - "graduationYear": 2019, - "codeOfConduct": true, - "teamId": Constants.MongoId.team3Id, + _id: Constants.MongoId.hackerFId, + accountId: Util.Account.hackerAccounts.stored.team[3]._id, + status: "Waitlisted", + application: { + general: { + school: "University of Blah", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Internship", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume2", + github: "www.github.com/Personasdf", + dribbble: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } + }, + teamId: Constants.MongoId.team3Id }; const TeamHacker4 = { - "_id": Constants.MongoId.hackerGId, - "accountId": Util.Account.hackerAccounts.stored.team[4]._id, - "status": "Waitlisted", - "school": "University of Blah1", - "degree": "Masters", - "gender": "Female", - "needsBus": false, - "application": { - "portfolioURL": { - //gcloud bucket link - "resume": "www.gcloud.com/myResume2", - "github": "www.github.com/Personasdf", - "dropler": undefined, - "personal": undefined, - "linkedIn": undefined, - "other": undefined - }, - "jobInterest": "Internship", - "skills": ["CSS", "HTML", "JS"], - }, - "ethnicity": ["European"], - "major": ["EE"], - "graduationYear": 2019, - "codeOfConduct": true, - "teamId": Constants.MongoId.team3Id, + _id: Constants.MongoId.hackerGId, + accountId: Util.Account.hackerAccounts.stored.team[4]._id, + status: "Waitlisted", + application: { + general: { + school: "University of Blah", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Internship", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume2", + github: "www.github.com/Personasdf", + dribbble: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } + }, + teamId: Constants.MongoId.team3Id }; const NoTeamHacker0 = { - "_id": Constants.MongoId.hackerBId, - "accountId": Util.Account.hackerAccounts.stored.noTeam[0]._id, - "status": "Accepted", - "school": "University of Blah1", - "degree": "Masters", - "gender": "Female", - "needsBus": false, - "application": { - "portfolioURL": { - //gcloud bucket link - "resume": "www.gcloud.com/myResume1", - "github": "www.github.com/Person4", - "dropler": undefined, - "personal": undefined, - "linkedIn": undefined, - "other": undefined - }, - "jobInterest": "Internship", - "skills": ["CSS", "HTML", "JS"], - }, - "ethnicity": ["European"], - "major": ["EE"], - "graduationYear": 2019, - "codeOfConduct": true, + _id: Constants.MongoId.hackerBId, + accountId: Util.Account.hackerAccounts.stored.noTeam[0]._id, + status: "Accepted", + application: { + general: { + school: "University of Blah", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Internship", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume1", + github: "www.github.com/Person4", + dribbble: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } + } }; const newHacker0 = { - "accountId": Util.Account.hackerAccounts.new[0]._id, - "school": "University of ASDF", - "degree": "Masters", - "gender": "Female", - "needsBus": true, - "application": { - "portfolioURL": { - //gcloud bucket link - "resume": "www.gcloud.com/myResume100", - "github": "www.github.com/Person1", - "dropler": undefined, - "personal": "www.person1.com", - "linkedIn": "www.linkedin.com/in/Person1", - "other": undefined - }, - "jobInterest": "Full-time", - "skills": ["CSS", "HTML", "JS"], - }, - "ethnicity": ["Caucasian"], - "major": ["EE"], - "graduationYear": 2019, - "codeOfConduct": true, + accountId: Util.Account.hackerAccounts.new[0]._id, + application: { + general: { + school: "University of ASDF", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Full-time", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume100", + github: "www.github.com/Person1", + dribbble: undefined, + personal: "www.person1.com", + linkedIn: "www.linkedin.com/in/Person1", + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["Caucasian"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } + } }; const newHacker1 = { - "accountId": Util.Account.hackerAccounts.new[1]._id, - "school": "University of YIKES", - "degree": "PhD", - "gender": "Female", - "needsBus": true, - "application": { - "portfolioURL": { - //gcloud bucket link - "resume": "www.gcloud.com/myResume100", - "github": "www.github.com/Person1", - "dropler": undefined, - "personal": "www.person1.com", - "linkedIn": "www.linkedin.com/in/Person1", - "other": undefined - }, - "jobInterest": "Full-time", - "skills": ["CSS", "HTML", "JS"], - }, - "ethnicity": ["African American"], - "major": ["EE"], - "graduationYear": 2019, - "codeOfConduct": true, + accountId: Util.Account.hackerAccounts.new[1]._id, + application: { + general: { + school: "University of YIKES", + degree: "PhD", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Full-time", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume100", + github: "www.github.com/Person1", + dribbble: undefined, + personal: "www.person1.com", + linkedIn: "www.linkedin.com/in/Person1", + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["African American"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: true + } + } }; -// duplicate of newHack1, but with false for code of conduct +// duplicate of newHack0, but with false for code of conduct const invalidHacker0 = { - "accountId": Util.Account.hackerAccounts.invalid[0]._id, - "school": "University of ASDF", - "degree": "Masters", - "gender": "Female", - "needsBus": true, - "application": { - "portfolioURL": { - //gcloud bucket link - "resume": "www.gcloud.com/myResume100", - "github": "www.github.com/Person1", - "dropler": undefined, - "personal": "www.person1.com", - "linkedIn": "www.linkedin.com/in/Person1", - "other": undefined - }, - "jobInterest": "Full-time", - "skills": ["CSS", "HTML", "JS"], - }, - "ethnicity": ["Caucasian"], - "major": ["EE"], - "graduationYear": 2019, - "codeOfConduct": false, + accountId: Util.Account.hackerAccounts.invalid[0]._id, + application: { + general: { + school: "University of ASDF", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Full-time", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume100", + github: "www.github.com/Person1", + dribbble: undefined, + personal: "www.person1.com", + linkedIn: "www.linkedin.com/in/Person1", + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["Caucasian"], + codeOfConduct_MCHACKS: false, + codeOfConduct_MLH: false + }, + accomodation: { + needsBus: true + } + } }; const invalidHacker1 = { - "_id": mongoose.Types.ObjectId(), - // invalid mongoID - "accountId": Util.Account.hackerAccounts.invalid[1]._invalidId, - // invalid missing school attribute - "degree": "Undersaduate", - "gender": "Female", - "needsBus": true, - "application": { - // invalid portflio with no resume - "portfolioURL": {}, - // invalid jobInterest - "jobInterest": "ASDF", - }, - "ethnicity": ["Asian", "Caucasian"], - "major": ["CS"], - "graduationYear": 2020, - "codeOfConduct": true, + _id: mongoose.Types.ObjectId(), + // invalid mongoID + accountId: Util.Account.hackerAccounts.invalid[1]._invalidId, + application: { + general: { + // invalid missing school attribute + degree: "Undersaduate", + fieldOfStudy: ["EE"], + graduationYear: 2019, + // invalid job interest + jobInterest: "ASDF", + URL: { + // invalid URL links with no resume + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["Caucasian"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: true + } + } }; const duplicateAccountLinkHacker0 = { - "_id": mongoose.Types.ObjectId(), - "accountId": Util.Account.hackerAccounts.stored.team[0]._id, - "status": "Applied", - "school": "University of Blah", - "degree": "Undergraduate", - "gender": "Male", - "needsBus": true, - "application": { - "portfolioURL": { - //gcloud bucket link - "resume": "www.gcloud.com/myResume100", - "github": "www.github.com/Person1", - "dropler": undefined, - "personal": "www.person1.com", - "linkedIn": "www.linkedin.com/in/Person1", - "other": undefined - }, - "jobInterest": "Full-time", - "skills": ["CSS", "HTML", "JS"], - }, - "ethnicity": ["Caucasian"], - "major": ["CS"], - "graduationYear": 2019, - "codeOfConduct": true, + _id: mongoose.Types.ObjectId(), + accountId: Util.Account.hackerAccounts.stored.team[0]._id, + status: "Applied", + application: { + general: { + school: "University of Blah", + degree: "Masters", + fieldOfStudy: ["CS"], + graduationYear: 2019, + jobInterest: "Full-time", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume100", + github: "www.github.com/Person1", + dribbble: undefined, + personal: "www.person1.com", + linkedIn: "www.linkedin.com/in/Person1", + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Male", + ethnicity: ["Caucasian"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: true + } + } }; const waitlistedHacker0 = { - "_id": Constants.MongoId.hackerCId, - "accountId": Util.Account.waitlistedHacker0._id, - "status": "Waitlisted", - "school": "University of Blah1", - "degree": "Masters", - "gender": "Female", - "needsBus": false, - "application": { - "portfolioURL": { - //gcloud bucket link - "resume": "www.gcloud.com/myResume2", - "github": "www.github.com/Personasdf", - "dropler": undefined, - "personal": undefined, - "linkedIn": undefined, - "other": undefined - }, - "jobInterest": "Internship", - "skills": ["CSS", "HTML", "JS"], - }, - "ethnicity": ["European"], - "major": ["EE"], - "graduationYear": 2019, - "codeOfConduct": true, - "teamId": Constants.MongoId.team2Id, + _id: Constants.MongoId.hackerCId, + accountId: Util.Account.waitlistedHacker0._id, + status: "Waitlisted", + application: { + general: { + school: "University of Blah", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Intership", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume2", + github: "www.github.com/Personasdf", + dribbble: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } + }, + teamId: Constants.MongoId.team2Id }; const unconfirmedAccountHacker0 = { - "_id": Constants.MongoId.hackerCId, - "accountId": Util.Account.NonConfirmedAccount3._id, - "status": "Waitlisted", - "school": "University of Blah1", - "degree": "Masters", - "gender": "Female", - "needsBus": false, - "application": { - "portfolioURL": { - //gcloud bucket link - "resume": "www.gcloud.com/myResume123", - "github": "www.github.com/Personasdf", - "dropler": undefined, - "personal": undefined, - "linkedIn": undefined, - "other": undefined - }, - "jobInterest": "Internship", - "skills": ["CSS", "HTML", "JS"], - }, - "ethnicity": ["European"], - "major": ["EE"], - "graduationYear": 2019, - "codeOfConduct": true, + _id: Constants.MongoId.hackerCId, + accountId: Util.Account.NonConfirmedAccount2._id, + status: "Waitlisted", + application: { + general: { + school: "University of Blah1", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Internship", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume123", + github: "www.github.com/Personasdf", + dribbble: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } + } }; const Hackers = [ - TeamHacker0, - TeamHacker1, - TeamHacker2, - TeamHacker3, - TeamHacker4, + TeamHacker0, + TeamHacker1, + TeamHacker2, + TeamHacker3, + TeamHacker4, - NoTeamHacker0, + NoTeamHacker0, - duplicateAccountLinkHacker0, - waitlistedHacker0 + duplicateAccountLinkHacker0, + waitlistedHacker0 ]; module.exports = { - TeamHacker0: TeamHacker0, - TeamHacker1: TeamHacker1, - TeamHacker2: TeamHacker2, - TeamHacker3: TeamHacker3, - TeamHacker4: TeamHacker4, + TeamHacker0: TeamHacker0, + TeamHacker1: TeamHacker1, + TeamHacker2: TeamHacker2, + TeamHacker3: TeamHacker3, + TeamHacker4: TeamHacker4, - NoTeamHacker0: NoTeamHacker0, + NoTeamHacker0: NoTeamHacker0, - newHacker0: newHacker0, - newHacker1: newHacker1, + newHacker0: newHacker0, + newHacker1: newHacker1, - invalidHacker0: invalidHacker0, - invalidHacker1: invalidHacker1, + invalidHacker0: invalidHacker0, + invalidHacker1: invalidHacker1, - duplicateAccountLinkHacker0: duplicateAccountLinkHacker0, - waitlistedHacker0: waitlistedHacker0, - unconfirmedAccountHacker0: unconfirmedAccountHacker0, + duplicateAccountLinkHacker0: duplicateAccountLinkHacker0, + waitlistedHacker0: waitlistedHacker0, + unconfirmedAccountHacker0: unconfirmedAccountHacker0, - Hackers: Hackers, - storeAll: storeAll, - dropAll: dropAll + Hackers: Hackers, + storeAll: storeAll, + dropAll: dropAll }; function store(attributes) { - const hackerDocs = []; - const hackerIds = []; - for (var i = 0; i < attributes.length; i++) { - hackerDocs.push(new Hacker(attributes[i])); - hackerIds.push(attributes[i]._id); - } + const hackerDocs = []; + const hackerIds = []; + for (var i = 0; i < attributes.length; i++) { + hackerDocs.push(new Hacker(attributes[i])); + hackerIds.push(attributes[i]._id); + } - return Hacker.collection.insertMany(hackerDocs); + return Hacker.collection.insertMany(hackerDocs); } async function storeAll() { - await store(Hackers); + await store(Hackers); } async function dropAll() { - try { - await Hacker.collection.drop(); - } catch (e) { - if (e.code === 26) { - logger.info("namespace %s not found", Hacker.collection.name); - } else { - throw e; - } + try { + await Hacker.collection.drop(); + } catch (e) { + if (e.code === 26) { + logger.info("namespace %s not found", Hacker.collection.name); + } else { + throw e; } -} \ No newline at end of file + } +} From 28e4dbd088b80b7383b59df2025eba26a360e509 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Sun, 1 Dec 2019 12:41:20 -0500 Subject: [PATCH 05/28] Don't worry about this for now --- tests/account.test.js | 1 - tests/hacker.test.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/account.test.js b/tests/account.test.js index 8acc1624..ace87f33 100644 --- a/tests/account.test.js +++ b/tests/account.test.js @@ -45,7 +45,6 @@ describe("GET user account", function() { .request(server.app) .get("/api/account/self") .end(function(err, res) { - console.log(res); res.should.have.status(401); res.should.be.json; res.body.should.have.property("message"); diff --git a/tests/hacker.test.js b/tests/hacker.test.js index 28b45c4e..c77d27e5 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -415,7 +415,7 @@ describe("POST create hacker", function() { .type("application/json") .send(invalidHacker0) .end(function(err, res) { - console.log(res); + // console.log(res); res.should.have.status(422); res.should.be.json; res.body.should.have.property("message"); From 8bd4545979fa681cd4fbe4710737d8e81e786c9f Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Wed, 4 Dec 2019 15:02:36 -0500 Subject: [PATCH 06/28] Fixing tests --- middlewares/hacker.middleware.js | 2 +- routes/api/hacker.js | 2 +- tests/hacker.test.js | 5 +-- tests/util/hacker.test.util.js | 55 ++++++++++++++++++++------------ 4 files changed, 40 insertions(+), 24 deletions(-) diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index 645bdc38..baefabeb 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -487,7 +487,6 @@ function checkStatus(statuses) { * @param {*} next */ async function updateHacker(req, res, next) { - console.log("does it reach?"); const hacker = await Services.Hacker.updateOne(req.params.id, req.body); if (hacker) { const acct = await Services.Account.findById(hacker.accountId); @@ -524,6 +523,7 @@ async function updateHacker(req, res, next) { * Creates hacker document after making sure there is no other hacker with the same linked accountId */ async function createHacker(req, res, next) { + console.log("here we are"); const hackerDetails = req.body.hackerDetails; const exists = await Services.Hacker.findByAccountId(hackerDetails.accountId); diff --git a/routes/api/hacker.js b/routes/api/hacker.js index 9496d41e..cd7fbe5c 100644 --- a/routes/api/hacker.js +++ b/routes/api/hacker.js @@ -179,7 +179,6 @@ module.exports = { hackerRouter.route("/").post( Middleware.Auth.ensureAuthenticated(), Middleware.Auth.ensureAuthorized(), - Middleware.Validator.Hacker.newHackerValidator, Middleware.parseBody.middleware, @@ -194,6 +193,7 @@ module.exports = { Middleware.Auth.createRoleBindings(CONSTANTS.HACKER), Middleware.Hacker.createHacker, Middleware.Hacker.sendAppliedStatusEmail, + Controllers.Hacker.createdHacker ); diff --git a/tests/hacker.test.js b/tests/hacker.test.js index 03c42bc6..516f52de 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -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[0]; const invalidHacker0 = util.hacker.invalidHacker0; const newHacker1 = util.hacker.newHacker1; @@ -421,7 +422,7 @@ describe("POST create hacker", function() { .type("application/json") .send(invalidHacker0) .end(function(err, res) { - // console.log(res); + console.log(res.body); res.should.have.status(422); res.should.be.json; res.body.should.have.property("message"); @@ -801,7 +802,7 @@ describe("PATCH update one hacker", function() { chai.assert.equal( JSON.stringify(res.body.data), JSON.stringify({ - status: Constants.General.HACKER_STATUS_CANCELLED + status: Constants.General.HACKER_STATUS_WITHDRAWN }) ); diff --git a/tests/util/hacker.test.util.js b/tests/util/hacker.test.util.js index ac9866e6..a575a6f5 100644 --- a/tests/util/hacker.test.util.js +++ b/tests/util/hacker.test.util.js @@ -317,6 +317,7 @@ const newHacker1 = { // duplicate of newHack0, but with false for code of conduct const invalidHacker0 = { + _id: mongoose.Types.ObjectId(), accountId: Util.Account.hackerAccounts.invalid[0]._id, application: { general: { @@ -343,6 +344,7 @@ const invalidHacker0 = { other: { gender: "Female", ethnicity: ["Caucasian"], + // must accept code of conduct to be valid codeOfConduct_MCHACKS: false, codeOfConduct_MLH: false }, @@ -504,27 +506,38 @@ const unconfirmedAccountHacker1 = { _id: Constants.MongoId.hackerHId, accountId: Util.Account.hackerAccounts.stored.unconfirmed[0]._id, status: "Accepted", - school: "University of Blah2", - degree: "Underggraduate", - gender: "Female", - needsBus: false, application: { - portfolioURL: { - //gcloud bucket link - resume: "www.gcloud.com/myResume123", - github: "www.github.com/Personasdf", - dropler: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined - }, - jobInterest: "Internship", - skills: ["CSS", "HTML", "JS"] - }, - ethnicity: ["European"], - major: ["EE"], - graduationYear: 2019, - codeOfConduct: true + general: { + school: "University of Blah2", + degree: "Underggraduate", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Internship", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume123", + github: "www.github.com/Personasdf", + dropler: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } + } }; const Hackers = [ @@ -535,6 +548,8 @@ const Hackers = [ TeamHacker4, NoTeamHacker0, + + invalidHacker0, unconfirmedAccountHacker1, duplicateAccountLinkHacker0, From 4b5e06e99b10fff0bfb96e21636eb349288ae2c8 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Thu, 5 Dec 2019 20:57:52 -0500 Subject: [PATCH 07/28] Who knows what is going on with this test now? --- controllers/hacker.controller.js | 2 +- middlewares/hacker.middleware.js | 2 +- models/hacker.model.js | 3 ++- tests/hacker.test.js | 2 +- tests/util/hacker.test.util.js | 5 ++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/controllers/hacker.controller.js b/controllers/hacker.controller.js index cecd9449..77ec15d5 100644 --- a/controllers/hacker.controller.js +++ b/controllers/hacker.controller.js @@ -64,7 +64,7 @@ function downloadedResume(req, res) { message: Constants.Success.RESUME_DOWNLOAD, data: { id: req.body.id, - resume: req.body.gcfilename + resume: req.body.resume } }); } diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index baefabeb..0fe477bc 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -248,7 +248,7 @@ function ensureAccountLinkedToHacker(req, res, next) { * @param {(err?)=>void} next */ async function uploadResume(req, res, next) { - const gcfilename = `resume/${Date.now()}-${req.hacker.id}`; + const gcfilename = `resumes/${Date.now()}-${req.hacker.id}`; await Services.Storage.upload(req.file, gcfilename); req.body.gcfilename = gcfilename; await Services.Hacker.updateOne(req.hacker.id, { diff --git a/models/hacker.model.js b/models/hacker.model.js index f977b3ec..97bb9143 100644 --- a/models/hacker.model.js +++ b/models/hacker.model.js @@ -44,7 +44,8 @@ const HackerSchema = new mongoose.Schema({ URL: { //gcloud bucket link resume: { - type: String + type: String, + default: "" }, github: { type: String diff --git a/tests/hacker.test.js b/tests/hacker.test.js index 516f52de..0662307a 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -412,7 +412,7 @@ describe("POST create hacker", function() { // should fail due to 'false' on code of conduct it("should FAIL if the new hacker does not accept code of conduct", function(done) { - util.auth.login(agent, Admin0, error => { + util.auth.login(agent, newHacker0, error => { if (error) { agent.close(); return done(error); diff --git a/tests/util/hacker.test.util.js b/tests/util/hacker.test.util.js index a575a6f5..debf5cc3 100644 --- a/tests/util/hacker.test.util.js +++ b/tests/util/hacker.test.util.js @@ -317,7 +317,6 @@ const newHacker1 = { // duplicate of newHack0, but with false for code of conduct const invalidHacker0 = { - _id: mongoose.Types.ObjectId(), accountId: Util.Account.hackerAccounts.invalid[0]._id, application: { general: { @@ -363,7 +362,7 @@ const invalidHacker1 = { // invalid missing school attribute degree: "Undersaduate", fieldOfStudy: ["EE"], - graduationYear: 2019, + graduationYear: 2020, // invalid job interest jobInterest: "ASDF", URL: { @@ -394,7 +393,7 @@ const duplicateAccountLinkHacker0 = { application: { general: { school: "University of Blah", - degree: "Masters", + degree: "Undergraduate", fieldOfStudy: ["CS"], graduationYear: 2019, jobInterest: "Full-time", From c1693699fc97a3139e16e682ba94d8135d1c2c52 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Thu, 5 Dec 2019 22:12:18 -0500 Subject: [PATCH 08/28] Ignore this --- middlewares/hacker.middleware.js | 1 - tests/hacker.test.js | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index 0fe477bc..6a115ac6 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -523,7 +523,6 @@ async function updateHacker(req, res, next) { * Creates hacker document after making sure there is no other hacker with the same linked accountId */ async function createHacker(req, res, next) { - console.log("here we are"); const hackerDetails = req.body.hackerDetails; const exists = await Services.Hacker.findByAccountId(hackerDetails.accountId); diff --git a/tests/hacker.test.js b/tests/hacker.test.js index 0662307a..146eef24 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -412,7 +412,7 @@ describe("POST create hacker", function() { // should fail due to 'false' on code of conduct it("should FAIL if the new hacker does not accept code of conduct", function(done) { - util.auth.login(agent, newHacker0, error => { + util.auth.login(agent, Admin0, error => { if (error) { agent.close(); return done(error); @@ -422,7 +422,6 @@ describe("POST create hacker", function() { .type("application/json") .send(invalidHacker0) .end(function(err, res) { - console.log(res.body); res.should.have.status(422); res.should.be.json; res.body.should.have.property("message"); From 770c7b805cefb8471041cc7ab21de9bb68bff986 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Thu, 5 Dec 2019 22:45:28 -0500 Subject: [PATCH 09/28] Spacing fixed --- controllers/hacker.controller.js | 66 +- docs/api/api_data.js | 7072 +++++++++++++------------- docs/api/vendor/jquery.min.js | 9 +- middlewares/auth.middleware.js | 728 +-- middlewares/hacker.middleware.js | 776 +-- middlewares/parse-body.middleware.js | 24 +- package-lock.json | 2873 +++++------ routes/api/hacker.js | 740 +-- services/account.service.js | 96 +- services/hacker.service.js | 281 +- services/storage.service.js | 153 +- tests/account.test.js | 1090 ++-- tests/hacker.test.js | 2057 ++++---- tests/search.service.spec.js | 510 +- tests/util/hacker.test.util.js | 1046 ++-- 15 files changed, 8839 insertions(+), 8682 deletions(-) diff --git a/controllers/hacker.controller.js b/controllers/hacker.controller.js index bad5c74e..bbcdd3bd 100644 --- a/controllers/hacker.controller.js +++ b/controllers/hacker.controller.js @@ -12,10 +12,10 @@ const Constants = { * @description Returns the JSON of hacker object located in req.body.hacker */ function showHacker(req, res) { - return res.status(200).json({ - message: Constants.Success.HACKER_READ, - data: req.body.hacker.toJSON() - }); + return res.status(200).json({ + message: Constants.Success.HACKER_READ, + data: req.body.hacker.toJSON() + }); } /** @@ -26,10 +26,10 @@ function showHacker(req, res) { * @description returns success message */ function createdHacker(req, res) { - return res.status(200).json({ - message: Constants.Success.HACKER_CREATE, - data: req.body.hacker.toJSON() - }); + return res.status(200).json({ + message: Constants.Success.HACKER_CREATE, + data: req.body.hacker.toJSON() + }); } /** @@ -44,29 +44,29 @@ function createdHacker(req, res) { * The new information is located in req.body. */ function updatedHacker(req, res) { - return res.status(200).json({ - message: Constants.Success.HACKER_UPDATE, - data: req.body - }); + return res.status(200).json({ + message: Constants.Success.HACKER_UPDATE, + data: req.body + }); } function uploadedResume(req, res) { - return res.status(200).json({ - message: Constants.Success.RESUME_UPLOAD, - data: { - filename: req.body.gcfilename - } - }); + return res.status(200).json({ + message: Constants.Success.RESUME_UPLOAD, + data: { + filename: req.body.gcfilename + } + }); } function downloadedResume(req, res) { - return res.status(200).json({ - message: Constants.Success.RESUME_DOWNLOAD, - data: { - id: req.body.id, - resume: req.body.resume - } - }); + return res.status(200).json({ + message: Constants.Success.RESUME_DOWNLOAD, + data: { + id: req.body.id, + resume: req.body.resume + } + }); } function gotStats(req, res) { @@ -79,17 +79,17 @@ function gotStats(req, res) { } function sentWeekOfEmail(req, res) { - return res.status(200).json({ - message: Constants.Success.HACKER_SENT_WEEK_OF, - data: {} - }); + return res.status(200).json({ + message: Constants.Success.HACKER_SENT_WEEK_OF, + data: {} + }); } function sentDayOfEmail(req, res) { - return res.status(200).json({ - message: Constants.Success.HACKER_SENT_DAY_OF, - data: {} - }); + return res.status(200).json({ + message: Constants.Success.HACKER_SENT_DAY_OF, + data: {} + }); } module.exports = { diff --git a/docs/api/api_data.js b/docs/api/api_data.js index 61823902..5ce704b5 100644 --- a/docs/api/api_data.js +++ b/docs/api/api_data.js @@ -1,3604 +1,3646 @@ define({ - api: [ - { - type: "post", - url: "/account/", - title: "create a new account", - name: "create", - group: "Account", - version: "0.0.8", - parameter: { - fields: { - body: [ - { - group: "body", - type: "String", - optional: false, - field: "firstName", - description: "

First name of the account creator.

" - }, - { - group: "body", - type: "String", - optional: false, - field: "lastName", - description: "

Last name of the account creator.

" - }, - { - group: "body", - type: "String", - optional: false, - field: "pronoun", - description: "

the pronoun of the account creator.

" - }, - { - group: "body", - type: "String", - optional: false, - field: "email", - description: "

Email of the account.

" - }, - { - group: "body", - type: "String[]", - optional: false, - field: "dietaryRestrictions", - description: - "

Any dietary restrictions for the user. 'None' if there are no restrictions

" - }, - { - group: "body", - type: "String", - optional: false, - field: "shirtSize", - description: - "

Size of the shirt that the user will receive.

" - }, - { - group: "body", - type: "String", - optional: false, - field: "password", - description: "

The password of the account.

" - }, - { - group: "body", - type: "String", - optional: false, - field: "birthDate", - description: "

a Date parsable string.

" - }, - { - group: "body", - type: "Number", - optional: false, - field: "phoneNumber", - description: - "

the user's phone number, represented as a string.

" - } - ], - header: [ - { - group: "header", - type: "JWT", - optional: true, - field: "token", - description: "

the user's invite token.

" - } - ] - }, - examples: [ - { - title: "Request-Example:", - content: - '{ \n "firstName": "Theo",\n "lastName":"Klein",\n "pronoun":"he/him",\n "email":"theo@klein.com",\n "password":"hunter2",\n "dietaryRestrictions":["Halal"],\n "phoneNumber":1234567890,\n "shirtSize":"S",\n "birthDate":"10/30/1997"\n}', - type: "json" - } - ] - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Account object

" - } - ] - }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Account creation successful", \n "data": {\n "id": ObjectId("5bff8b9f3274cf001bc71048"),\n \t"firstName": "Theo",\n "lastName":"Klein",\n "pronoun":"he/him",\n "email":"theo@klein.com",\n "dietaryRestrictions":["Halal"],\n "phoneNumber":1234567890,\n \t"shirtSize":"S",\n "birthDate":Date("10/30/1997")\n }\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "string", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] - }, - examples: [ - { - title: "Error-Response: ", - content: - '{\n "message": "Account already exists", \n "data": {\n "route": "/"\n }\n}', - type: "object" - } - ] - }, - filename: "routes/api/account.js", - groupTitle: "Account", - sampleRequest: [ + api: [ { - url: "https://api.mchacks.ca/api/account/" - } - ] - }, - { - type: "get", - url: "/account/:id", - title: "gets information from an account with mongoid ':id'", - name: "getAccount", - group: "Account", - version: "0.0.8", - parameter: { - fields: { - param: [ - { - group: "param", - type: "ObjectId", - optional: false, - field: "id", - description: "

MongoId of an account

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Account object

" - } - ] + type: "post", + url: "/account/", + title: "create a new account", + name: "create", + group: "Account", + version: "0.0.8", + parameter: { + fields: { + body: [ + { + group: "body", + type: "String", + optional: false, + field: "firstName", + description: + "

First name of the account creator.

" + }, + { + group: "body", + type: "String", + optional: false, + field: "lastName", + description: + "

Last name of the account creator.

" + }, + { + group: "body", + type: "String", + optional: false, + field: "pronoun", + description: + "

the pronoun of the account creator.

" + }, + { + group: "body", + type: "String", + optional: false, + field: "email", + description: "

Email of the account.

" + }, + { + group: "body", + type: "String[]", + optional: false, + field: "dietaryRestrictions", + description: + "

Any dietary restrictions for the user. 'None' if there are no restrictions

" + }, + { + group: "body", + type: "String", + optional: false, + field: "shirtSize", + description: + "

Size of the shirt that the user will receive.

" + }, + { + group: "body", + type: "String", + optional: false, + field: "password", + description: "

The password of the account.

" + }, + { + group: "body", + type: "String", + optional: false, + field: "birthDate", + description: "

a Date parsable string.

" + }, + { + group: "body", + type: "Number", + optional: false, + field: "phoneNumber", + description: + "

the user's phone number, represented as a string.

" + } + ], + header: [ + { + group: "header", + type: "JWT", + optional: true, + field: "token", + description: "

the user's invite token.

" + } + ] + }, + examples: [ + { + title: "Request-Example:", + content: + '{ \n "firstName": "Theo",\n "lastName":"Klein",\n "pronoun":"he/him",\n "email":"theo@klein.com",\n "password":"hunter2",\n "dietaryRestrictions":["Halal"],\n "phoneNumber":1234567890,\n "shirtSize":"S",\n "birthDate":"10/30/1997"\n}', + type: "json" + } + ] + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Account object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Account creation successful", \n "data": {\n "id": ObjectId("5bff8b9f3274cf001bc71048"),\n \t"firstName": "Theo",\n "lastName":"Klein",\n "pronoun":"he/him",\n "email":"theo@klein.com",\n "dietaryRestrictions":["Halal"],\n "phoneNumber":1234567890,\n \t"shirtSize":"S",\n "birthDate":Date("10/30/1997")\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{\n "message": "Account already exists", \n "data": {\n "route": "/"\n }\n}', + type: "object" + } + ] + }, + filename: "routes/api/account.js", + groupTitle: "Account", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/account/" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Account found by user id", \n "data": {\n "id": ObjectId("5bff8b9f3274cf001bc71048"),\n "firstName": "Theo",\n "lastName":"Klein",\n "pronoun":"he/him",\n "email":"theo@klein.com",\n "dietaryRestrictions":["Halal"],\n "phoneNumber":1234567890,\n "shirtSize":"S",\n "birthDate":Date("10/30/1997")\n }\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "string", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] - }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Account not found", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/account.js", - groupTitle: "Account", - sampleRequest: [ - { - url: "https://api.mchacks.ca/api/account/:id" - } - ] - }, - { - type: "get", - url: "/account/invite", - title: "Get all of the invites.", - name: "getAllInvites", - group: "Account", - version: "0.0.8", - description: - "

Get all of the invites that currently exist in the database.

", - success: { - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Invite retrieval successful.", \n "data": [{\n "email":"abc@def.com",\n "accountType":"Hacker"\n }]\n }', - type: "object" - } - ] - }, - filename: "routes/api/account.js", - groupTitle: "Account", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/account/invite" - } - ] - }, - { - type: "post", - url: "/account/invite", - title: - "invites a user to create an account with the specified accountType", - name: "inviteAccount", - group: "Account", - version: "0.0.8", - description: - "

sends link with token to be used with the account/create route

", - parameter: { - fields: { - body: [ - { - group: "body", - type: "String", - optional: true, - field: "email", - description: - "

email of the account to be created and where to send the link

" - }, - { - group: "body", - type: "String", - optional: true, - field: "accountType", - description: - "

the type of the account which the user can create, for sponsor this should specify tier as well

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Account object

" - } - ] - }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Successfully invited user", \n "data": {}\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "string", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "object", - optional: false, - field: "data", - description: "

Error object

" - } - ] + type: "get", + url: "/account/:id", + title: "gets information from an account with mongoid ':id'", + name: "getAccount", + group: "Account", + version: "0.0.8", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "id", + description: "

MongoId of an account

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Account object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Account found by user id", \n "data": {\n "id": ObjectId("5bff8b9f3274cf001bc71048"),\n "firstName": "Theo",\n "lastName":"Klein",\n "pronoun":"he/him",\n "email":"theo@klein.com",\n "dietaryRestrictions":["Halal"],\n "phoneNumber":1234567890,\n "shirtSize":"S",\n "birthDate":Date("10/30/1997")\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Account not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/account.js", + groupTitle: "Account", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/account/:id" + } + ] }, - examples: [ - { - title: "Error-Response:", - content: - '{\n "message": "Invalid Authentication",\n "data": {\n "route": "/invite"\n }\n }', - type: "object" - } - ] - }, - filename: "routes/api/account.js", - groupTitle: "Account", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/account/invite" - } - ] - }, - { - type: "get", - url: "/account/self", - title: "get information about own account", - name: "self", - group: "Account", - version: "0.0.8", - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Account object

" - } - ] + type: "get", + url: "/account/invite", + title: "Get all of the invites.", + name: "getAllInvites", + group: "Account", + version: "0.0.8", + description: + "

Get all of the invites that currently exist in the database.

", + success: { + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Invite retrieval successful.", \n "data": [{\n "email":"abc@def.com",\n "accountType":"Hacker"\n }]\n }', + type: "object" + } + ] + }, + filename: "routes/api/account.js", + groupTitle: "Account", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/account/invite" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Account found by user email", \n "data": {\n \t"id": ObjectId("5bff8b9f3274cf001bc71048"),\n \t"firstName": "Theo",\n "lastName":"Klein",\n "pronoun":"he/him",\n "email":"theo@klein.com",\n "dietaryRestrictions":["Halal"],\n "phoneNumber":1234567890,\n \t"shirtSize":"S",\n "birthDate":Date("10/30/1997")\n }\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "string", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "object", - optional: false, - field: "data", - description: "

empty object

" - } - ] - }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Account not found", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/account.js", - groupTitle: "Account", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/account/self" - } - ] - }, - { - type: "patch", - url: "/account/:id", - title: "update an account's information", - name: "updateOneUser", - group: "Account", - version: "0.0.8", - parameter: { - fields: { - body: [ - { - group: "body", - type: "String", - optional: true, - field: "firstName", - description: "

First name of the account creator.

" - }, - { - group: "body", - type: "String", - optional: true, - field: "lastName", - description: "

Last name of the account creator.

" - }, - { - group: "body", - type: "String", - optional: true, - field: "pronoun", - description: "

the pronoun of the account creator.

" - }, - { - group: "body", - type: "String", - optional: true, - field: "email", - description: "

Email of the account.

" - }, - { - group: "body", - type: "String[]", - optional: true, - field: "dietaryRestrictions", - description: - "

Any dietary restrictions for the user. 'None' if there are no restrictions

" - }, - { - group: "body", - type: "String", - optional: true, - field: "shirtSize", - description: - "

Size of the shirt that the user will receive.

" - }, - { - group: "body", - type: "String", - optional: true, - field: "birthDate", - description: "

a Date parsable string.

" - }, - { - group: "body", - type: "Number", - optional: true, - field: "phoneNumber", - description: - "

the user's phone number, represented as a string.

" - } - ] + type: "post", + url: "/account/invite", + title: + "invites a user to create an account with the specified accountType", + name: "inviteAccount", + group: "Account", + version: "0.0.8", + description: + "

sends link with token to be used with the account/create route

", + parameter: { + fields: { + body: [ + { + group: "body", + type: "String", + optional: true, + field: "email", + description: + "

email of the account to be created and where to send the link

" + }, + { + group: "body", + type: "String", + optional: true, + field: "accountType", + description: + "

the type of the account which the user can create, for sponsor this should specify tier as well

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Account object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Successfully invited user", \n "data": {}\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

Error object

" + } + ] + }, + examples: [ + { + title: "Error-Response:", + content: + '{\n "message": "Invalid Authentication",\n "data": {\n "route": "/invite"\n }\n }', + type: "object" + } + ] + }, + filename: "routes/api/account.js", + groupTitle: "Account", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/account/invite" + } + ] }, - examples: [ - { - title: "Request-Example:", - content: '{ "shirtSize": "M" }', - type: "json" - } - ] - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Account object

" - } - ] - }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Changed account information", \n "data": {\n "id": ObjectId("5bff8b9f3274cf001bc71048"),\n \t"firstName": "Theo",\n "lastName":"Klein",\n "pronoun":"he/him",\n "email":"theo@klein.com",\n "dietaryRestrictions":["Halal"],\n "phoneNumber":1234567890,\n \t"shirtSize":"M",\n "birthDate":Date("10/30/1997")\n }\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "string", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] - }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Error while updating account", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/account.js", - groupTitle: "Account", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/account/:id" - } - ] - }, - { - type: "patch", - url: "/auth/password/change", - title: "change password for logged in user", - name: "changePassword", - group: "Authentication", - version: "0.0.8", - parameter: { - fields: { - Parameter: [ - { - group: "Parameter", - type: "String", - optional: false, - field: "oldPassword", - description: "

The current password of the user

" - }, - { - group: "Parameter", - type: "String", - optional: false, - field: "newPassword", - description: "

The new password of the user

" - } - ] - }, - examples: [ - { - title: "Request-Example:", - content: - '{ \n "oldPassword": "password12345",\n "newPassword": "password123456"\n}', - type: "json" - } - ] - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "get", + url: "/account/self", + title: "get information about own account", + name: "self", + group: "Account", + version: "0.0.8", + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Account object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Account found by user email", \n "data": {\n \t"id": ObjectId("5bff8b9f3274cf001bc71048"),\n \t"firstName": "Theo",\n "lastName":"Klein",\n "pronoun":"he/him",\n "email":"theo@klein.com",\n "dietaryRestrictions":["Halal"],\n "phoneNumber":1234567890,\n \t"shirtSize":"S",\n "birthDate":Date("10/30/1997")\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty object

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Account not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/account.js", + groupTitle: "Account", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/account/self" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: '{"message": "Successfully reset password", "data": {}}', - type: "json" - } - ] - }, - permission: [ { - name: ": Must be logged in" - } - ], - filename: "routes/api/auth.js", - groupTitle: "Authentication", - sampleRequest: [ - { - url: "https://api.mchacks.ca/api/auth/password/change" - } - ] - }, - { - type: "post", - url: "/auth/confirm/:token", - title: "confirm account using the JWT in :token", - name: "confirmAccount", - group: "Authentication", - version: "0.0.8", - parameter: { - fields: { - Parameter: [ - { - group: "Parameter", - type: "String", - optional: false, - field: "JWT", - description: "

for confirming the account

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - } - ] - }, - examples: [ - { - title: "Error-Response: ", - content: - '{\n "message": "Account already exists", \n "data": {\n "route": "/"\n }\n}', - type: "object" - } - ], - filename: "routes/api/account.js", - groupTitle: "Account", - sampleRequest: [ - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ], - examples: [ - { - title: "Success-Response:", - content: - '{"message": "Successfully confirmed account", "data": {}}', - type: "json" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "string", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "patch", + url: "/account/:id", + title: "update an account's information", + name: "updateOneUser", + group: "Account", + version: "0.0.8", + parameter: { + fields: { + body: [ + { + group: "body", + type: "String", + optional: true, + field: "firstName", + description: + "

First name of the account creator.

" + }, + { + group: "body", + type: "String", + optional: true, + field: "lastName", + description: + "

Last name of the account creator.

" + }, + { + group: "body", + type: "String", + optional: true, + field: "pronoun", + description: + "

the pronoun of the account creator.

" + }, + { + group: "body", + type: "String", + optional: true, + field: "email", + description: "

Email of the account.

" + }, + { + group: "body", + type: "String[]", + optional: true, + field: "dietaryRestrictions", + description: + "

Any dietary restrictions for the user. 'None' if there are no restrictions

" + }, + { + group: "body", + type: "String", + optional: true, + field: "shirtSize", + description: + "

Size of the shirt that the user will receive.

" + }, + { + group: "body", + type: "String", + optional: true, + field: "birthDate", + description: "

a Date parsable string.

" + }, + { + group: "body", + type: "Number", + optional: true, + field: "phoneNumber", + description: + "

the user's phone number, represented as a string.

" + } + ] + }, + examples: [ + { + title: "Request-Example:", + content: '{ "shirtSize": "M" }', + type: "json" + } + ] + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Account object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Changed account information", \n "data": {\n "id": ObjectId("5bff8b9f3274cf001bc71048"),\n \t"firstName": "Theo",\n "lastName":"Klein",\n "pronoun":"he/him",\n "email":"theo@klein.com",\n "dietaryRestrictions":["Halal"],\n "phoneNumber":1234567890,\n \t"shirtSize":"M",\n "birthDate":Date("10/30/1997")\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{"message": "Error while updating account", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/account.js", + groupTitle: "Account", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/account/:id" + } + ] }, - examples: [ - { - title: "Error-Response: ", - content: - '{"message": "Invalid token for confirming account, "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/auth.js", - groupTitle: "Authentication", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/auth/confirm/:token" - } - ] - }, - { - type: "post", - url: "/auth/password/forgot", - title: "forgot password route", - name: "forgotPassword", - group: "Authentication", - version: "0.0.8", - parameter: { - fields: { - Parameter: [ - { - group: "Parameter", - type: "String", - optional: false, - field: "email", - description: "

the email address of the account

" - } - ] - }, - examples: [ - { - title: "Request-Example:", - content: '{ "email": "myemail@mchacks.ca" }', - type: "json" - } - ] - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "patch", + url: "/auth/password/change", + title: "change password for logged in user", + name: "changePassword", + group: "Authentication", + version: "0.0.8", + parameter: { + fields: { + Parameter: [ + { + group: "Parameter", + type: "String", + optional: false, + field: "oldPassword", + description: + "

The current password of the user

" + }, + { + group: "Parameter", + type: "String", + optional: false, + field: "newPassword", + description: "

The new password of the user

" + } + ] + }, + examples: [ + { + title: "Request-Example:", + content: + '{ \n "oldPassword": "password12345",\n "newPassword": "password123456"\n}', + type: "json" + } + ] + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{"message": "Successfully reset password", "data": {}}', + type: "json" + } + ] + }, + permission: [ + { + name: ": Must be logged in" + } + ], + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/password/change" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: '{"message": "Sent reset email", "data": {}}', - type: "json" - } - ] - }, - permission: [ - { - name: ": public" - } - ], - filename: "routes/api/auth.js", - groupTitle: "Authentication", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/auth/password/forgot" - } - ] - }, - { - type: "get", - url: "/auth/rolebindings/:id", - title: "retrieve rolebindings for a user given by their user id :id", - name: "getRoleBindings", - group: "Authentication", - version: "0.0.8", - parameter: { - fields: { - param: [ - { - group: "param", - type: "ObjectId", - optional: false, - field: "id", - description: "

MongoId of an account

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Rolebindings object

" - } - ] + type: "post", + url: "/auth/confirm/:token", + title: "confirm account using the JWT in :token", + name: "confirmAccount", + group: "Authentication", + version: "0.0.8", + parameter: { + fields: { + Parameter: [ + { + group: "Parameter", + type: "String", + optional: false, + field: "JWT", + description: "

for confirming the account

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{\n "message": "Account already exists", \n "data": {\n "route": "/"\n }\n}', + type: "object" + } + ], + filename: "routes/api/account.js", + groupTitle: "Account", + sampleRequest: [ + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ], + examples: [ + { + title: "Success-Response:", + content: + '{"message": "Successfully confirmed account", "data": {}}', + type: "json" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{"message": "Invalid token for confirming account, "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/confirm/:token" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Successfully retrieved role bindings",\n "data": {\n accountId:"5beca4ab2e069a34f91697b2"\n id:"5beca4ae2e069a34f91698b1"\n roles: [\n {\n _id:"5beca4ab2e069a34f91697d9",\n name:"hacker",\n routes: [\n {_id: "5beca4ae2e069a34f9169852", requestType: "POST", uri: "/api/auth/login"},\n {_id: "5beca4ae2e069a34f9169851", requestType: "POST", uri: "/api/auth/logout"},\n {_id: "5beca4ae2e069a34f9169850", requestType: "GET", uri: "/api/auth/rolebindings/:self"},\n {_id: "5beca4ae2e069a34f916984f", requestType: "GET", uri: "/api/account/self"},\n {_id: "5beca4ae2e069a34f916984e", requestType: "GET", uri: "/api/account/:self"},\n {_id: "5beca4ae2e069a34f916984d", requestType: "PATCH", uri: "/api/account/:self"},\n {_id: "5beca4ae2e069a34f916984c", requestType: "POST", uri: "/api/hacker/"},\n {_id: "5beca4ae2e069a34f916984b", requestType: "GET", uri: "/api/hacker/:self"},\n {_id: "5beca4ae2e069a34f916984a", requestType: "GET", uri: "/api/hacker/:self/resume"},\n {_id: "5beca4ae2e069a34f9169849", requestType: "PATCH", uri: "/api/hacker/:self"}\n ]\n }\n ]\n }\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "string", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] - }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Role Bindings not found", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/auth.js", - groupTitle: "Authentication", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/auth/rolebindings/:id" - } - ] - }, - { - type: "get", - url: "/auth/roles", - title: "get roles", - name: "getRoles", - description: "

get all roles that exist in the database

", - group: "Authentication", - version: "0.0.8", - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - } - ], - examples: [ - { - title: "Error-Response:", - content: - '{\n "message": "Invalid Authentication",\n "data": {\n "route": "/invite"\n }\n }', - type: "object" - } - ], - filename: "routes/api/account.js", - groupTitle: "Account", - sampleRequest: [ - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "post", + url: "/auth/password/forgot", + title: "forgot password route", + name: "forgotPassword", + group: "Authentication", + version: "0.0.8", + parameter: { + fields: { + Parameter: [ + { + group: "Parameter", + type: "String", + optional: false, + field: "email", + description: + "

the email address of the account

" + } + ] + }, + examples: [ + { + title: "Request-Example:", + content: '{ "email": "myemail@mchacks.ca" }', + type: "json" + } + ] + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: '{"message": "Sent reset email", "data": {}}', + type: "json" + } + ] + }, + permission: [ + { + name: ": public" + } + ], + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/password/forgot" + } + ] }, - examples: [ - { - title: "Success-Response:", - content: - '{"message": "Sucessfully retrieved all roles", "data":\n[{name: "GodStaff", routes: Array(27), id: "5bee20ef3ca9dd4754382880"},\n {name: "Hacker", routes: Array(10), id: "5bee20ef3ca9dd4754382881"},\n {name: "Volunteer", routes: Array(4), id: "5bee20ef3ca9dd4754382882"}]', - type: "json" - } - ] - }, - filename: "routes/api/auth.js", - groupTitle: "Authentication", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/auth/roles" - } - ] - }, - { - type: "post", - url: "/auth/login", - title: "login to the service", - name: "login", - group: "Authentication", - version: "0.0.8", - parameter: { - fields: { - Parameter: [ - { - group: "Parameter", - type: "string", - optional: false, - field: "email", - description: "

Account email

" - }, - { - group: "Parameter", - type: "string", - optional: false, - field: "password", - description: "

Account password

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "get", + url: "/auth/rolebindings/:id", + title: + "retrieve rolebindings for a user given by their user id :id", + name: "getRoleBindings", + group: "Authentication", + version: "0.0.8", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "id", + description: "

MongoId of an account

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Rolebindings object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Successfully retrieved role bindings",\n "data": {\n accountId:"5beca4ab2e069a34f91697b2"\n id:"5beca4ae2e069a34f91698b1"\n roles: [\n {\n _id:"5beca4ab2e069a34f91697d9",\n name:"hacker",\n routes: [\n {_id: "5beca4ae2e069a34f9169852", requestType: "POST", uri: "/api/auth/login"},\n {_id: "5beca4ae2e069a34f9169851", requestType: "POST", uri: "/api/auth/logout"},\n {_id: "5beca4ae2e069a34f9169850", requestType: "GET", uri: "/api/auth/rolebindings/:self"},\n {_id: "5beca4ae2e069a34f916984f", requestType: "GET", uri: "/api/account/self"},\n {_id: "5beca4ae2e069a34f916984e", requestType: "GET", uri: "/api/account/:self"},\n {_id: "5beca4ae2e069a34f916984d", requestType: "PATCH", uri: "/api/account/:self"},\n {_id: "5beca4ae2e069a34f916984c", requestType: "POST", uri: "/api/hacker/"},\n {_id: "5beca4ae2e069a34f916984b", requestType: "GET", uri: "/api/hacker/:self"},\n {_id: "5beca4ae2e069a34f916984a", requestType: "GET", uri: "/api/hacker/:self/resume"},\n {_id: "5beca4ae2e069a34f9169849", requestType: "PATCH", uri: "/api/hacker/:self"}\n ]\n }\n ]\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{"message": "Role Bindings not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/rolebindings/:id" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: '{"message": "Successfully logged in", "data": {}}', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "string", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] - }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Invalid Authentication", "data": {}}', - type: "object" - } - ] - }, - permission: [ { - name: ": public" - } - ], - filename: "routes/api/auth.js", - groupTitle: "Authentication", - sampleRequest: [ - { - url: "https://api.mchacks.ca/api/auth/login" - } - ] - }, - { - type: "get", - url: "/auth/logout", - title: "logout of service", - name: "logout", - group: "Authentication", - version: "0.0.8", - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "get", + url: "/auth/roles", + title: "get roles", + name: "getRoles", + description: "

get all roles that exist in the database

", + group: "Authentication", + version: "0.0.8", + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + } + ], + examples: [ + { + title: "Error-Response:", + content: + '{\n "message": "Invalid Authentication",\n "data": {\n "route": "/invite"\n }\n }', + type: "object" + } + ], + filename: "routes/api/account.js", + groupTitle: "Account", + sampleRequest: [ + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response:", + content: + '{"message": "Sucessfully retrieved all roles", "data":\n[{name: "GodStaff", routes: Array(27), id: "5bee20ef3ca9dd4754382880"},\n {name: "Hacker", routes: Array(10), id: "5bee20ef3ca9dd4754382881"},\n {name: "Volunteer", routes: Array(4), id: "5bee20ef3ca9dd4754382882"}]', + type: "json" + } + ] + }, + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/roles" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: '{"message": "Successfully logged out", "data": {}}', - type: "object" - } - ] - }, - permission: [ { - name: ": public" - } - ], - filename: "routes/api/auth.js", - groupTitle: "Authentication", - sampleRequest: [ - { - url: "https://api.mchacks.ca/api/auth/logout" - } - ] - }, - { - type: "get", - url: "/auth/confirm/resend", - title: "resend confirmation token", - name: "resendConfirmAccount", - group: "Authentication", - version: "0.0.8", - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "post", + url: "/auth/login", + title: "login to the service", + name: "login", + group: "Authentication", + version: "0.0.8", + parameter: { + fields: { + Parameter: [ + { + group: "Parameter", + type: "string", + optional: false, + field: "email", + description: "

Account email

" + }, + { + group: "Parameter", + type: "string", + optional: false, + field: "password", + description: "

Account password

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{"message": "Successfully logged in", "data": {}}', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{"message": "Invalid Authentication", "data": {}}', + type: "object" + } + ] + }, + permission: [ + { + name: ": public" + } + ], + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/login" + } + ] }, - examples: [ - { - title: "Success-Response:", - content: - '{"message": "Successfully resent confirmation email", "data": {}}', - type: "json" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "string", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] - }, - examples: [ - { - title: "Error-Response:", - content: - ' HTTP/1.1 422\n{"message": "Account already confirmed", "data": {}}', - type: "json" - }, - { - title: "Error-Response:", - content: - ' HTTP/1.1 428\n{"message": "Account confirmation token does not exist", "data": {}}', - type: "json" - } - ] - }, - filename: "routes/api/auth.js", - groupTitle: "Authentication", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/auth/confirm/resend" - } - ] - }, - { - type: "post", - url: "/auth/password/reset", - title: "reset password", - name: "resetPassword", - group: "Authentication", - version: "0.0.8", - parameter: { - fields: { - Parameter: [ - { - group: "Parameter", - type: "String", - optional: false, - field: "password", - description: "

the password of the account

" - } - ] - }, - examples: [ - { - title: "Request-Example:", - content: '{ "password": "hunter2" }', - type: "json" - } - ] - }, - header: { - fields: { - Header: [ - { - group: "Header", - type: "String", - optional: false, - field: "Authentication", - description: - "

the token that was provided in the reset password email

" - } - ] - }, - examples: [ - { - title: "Header-Example:", - content: - '{\n "X-Reset-Token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"\n}', - type: "json" - } - ] - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "get", + url: "/auth/logout", + title: "logout of service", + name: "logout", + group: "Authentication", + version: "0.0.8", + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{"message": "Successfully logged out", "data": {}}', + type: "object" + } + ] + }, + permission: [ + { + name: ": public" + } + ], + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/logout" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: '{"message": "Successfully reset password", "data": {}}', - type: "json" - } - ] - }, - permission: [ { - name: ": must have authentication token" - } - ], - filename: "routes/api/auth.js", - groupTitle: "Authentication", - sampleRequest: [ - { - url: "https://api.mchacks.ca/api/auth/password/reset" - } - ] - }, - { - type: "patch", - url: "/hacker/checkin/:id", - title: - "update a hacker's status to be 'Checked-in'. Note that the Hacker must eitehr be Accepted or Confirmed.", - name: "checkinHacker", - group: "Hacker", - version: "0.0.9", - parameter: { - fields: { - body: [ - { - group: "body", - type: "string", - optional: true, - field: "status", - description: "

Check-in status. "Checked-in"

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Hacker object

" - } - ] + type: "get", + url: "/auth/confirm/resend", + title: "resend confirmation token", + name: "resendConfirmAccount", + group: "Authentication", + version: "0.0.8", + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response:", + content: + '{"message": "Successfully resent confirmation email", "data": {}}', + type: "json" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response:", + content: + ' HTTP/1.1 422\n{"message": "Account already confirmed", "data": {}}', + type: "json" + }, + { + title: "Error-Response:", + content: + ' HTTP/1.1 428\n{"message": "Account confirmation token does not exist", "data": {}}', + type: "json" + } + ] + }, + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/confirm/resend" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Changed hacker information", \n "data": {\n "status": "Checked-in"\n }\n}', - type: "object" - } - ] - }, - permission: [ { - name: "Administrator" + type: "post", + url: "/auth/password/reset", + title: "reset password", + name: "resetPassword", + group: "Authentication", + version: "0.0.8", + parameter: { + fields: { + Parameter: [ + { + group: "Parameter", + type: "String", + optional: false, + field: "password", + description: "

the password of the account

" + } + ] + }, + examples: [ + { + title: "Request-Example:", + content: '{ "password": "hunter2" }', + type: "json" + } + ] + }, + header: { + fields: { + Header: [ + { + group: "Header", + type: "String", + optional: false, + field: "Authentication", + description: + "

the token that was provided in the reset password email

" + } + ] + }, + examples: [ + { + title: "Header-Example:", + content: + '{\n "X-Reset-Token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"\n}', + type: "json" + } + ] + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{"message": "Successfully reset password", "data": {}}', + type: "json" + } + ] + }, + permission: [ + { + name: ": must have authentication token" + } + ], + filename: "routes/api/auth.js", + groupTitle: "Authentication", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/auth/password/reset" + } + ] }, { - name: "Volunteer" - } - ], - filename: "routes/api/hacker.js", - groupTitle: "Hacker", - sampleRequest: [ - { - url: "https://api.mchacks.ca/api/hacker/checkin/:id" - } - ] - }, - { - type: "post", - url: "/hacker/", - title: "create a new hacker", - name: "createHacker", - group: "Hacker", - version: "0.0.8", - parameter: { - fields: { - body: [ - { - group: "body", - type: "MongoID", - optional: false, - field: "accountId", - description: "

ObjectID of the respective account

" - }, - { - group: "body", - type: "String", - optional: false, - field: "school", - description: "

Name of the school the hacker goes to

" - }, - { - group: "body", - type: "String", - optional: false, - field: "gender", - description: "

Gender of the hacker

" - }, - { - group: "body", - type: "Boolean", - optional: false, - field: "needsBus", - description: - "

Whether the hacker requires a bus for transportation

" - }, - { - group: "body", - type: "String[]", - optional: false, - field: "ethnicity", - description: "

the ethnicities of the hacker

" - }, - { - group: "body", - type: "String[]", - optional: false, - field: "major", - description: "

the major of the hacker

" - }, - { - group: "body", - type: "Number", - optional: false, - field: "graduationYear", - description: "

the graduation year of the hacker

" - }, - { - group: "body", - type: "Boolean", - optional: false, - field: "codeOfConduct", - description: "

acceptance of the code of conduct

" - }, - { - group: "body", - type: "Json", - optional: false, - field: "application", - description: - "

The hacker's application. Resume and jobInterest fields are required.

" - } - ] - }, - examples: [ - { - title: "application: ", - content: - '{\n "application":{\n "portfolioURL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n}', - type: "Json" - } - ] - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Hacker object

" - } - ] + type: "patch", + url: "/hacker/checkin/:id", + title: + "update a hacker's status to be 'Checked-in'. Note that the Hacker must eitehr be Accepted or Confirmed.", + name: "checkinHacker", + group: "Hacker", + version: "0.0.9", + parameter: { + fields: { + body: [ + { + group: "body", + type: "string", + optional: true, + field: "status", + description: + "

Check-in status. "Checked-in"

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Changed hacker information", \n "data": {\n "status": "Checked-in"\n }\n}', + type: "object" + } + ] + }, + permission: [ + { + name: "Administrator" + }, + { + name: "Volunteer" + } + ], + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/checkin/:id" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Hacker creation successful", \n "data": {\n "id":"5bff4d736f86be0a41badb91",\n "application":{\n "portfolioURL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n },\n "status":"Applied",\n "ethnicity":["White or Caucasian"," Asian or Pacific Islander"],\n "accountId":"5bff2a35e533b0f6562b4998",\n "school":"McPherson College",\n "gender":"Female",\n "needsBus":false,\n "major":"Accounting",\n "graduationYear":2019,\n "codeOfConduct":true,\n }\n}', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "string", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] - }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Error while creating hacker", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/hacker.js", - groupTitle: "Hacker", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/hacker/" - } - ] - }, - { - type: "get", - url: "/hacker/email/:email", - title: "get a hacker's information", - name: "getHacker", - group: "Hacker", - version: "0.0.8", - parameter: { - fields: { - param: [ - { - group: "param", - type: "String", - optional: false, - field: "email", - description: "

a hacker's unique email

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "String", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "Object", - optional: false, - field: "data", - description: "

Hacker object

" - } - ] + type: "post", + url: "/hacker/", + title: "create a new hacker", + name: "createHacker", + group: "Hacker", + version: "0.0.8", + parameter: { + fields: { + body: [ + { + group: "body", + type: "MongoID", + optional: false, + field: "accountId", + description: + "

ObjectID of the respective account

" + }, + { + group: "body", + type: "String", + optional: false, + field: "school", + description: + "

Name of the school the hacker goes to

" + }, + { + group: "body", + type: "String", + optional: false, + field: "gender", + description: "

Gender of the hacker

" + }, + { + group: "body", + type: "Boolean", + optional: false, + field: "needsBus", + description: + "

Whether the hacker requires a bus for transportation

" + }, + { + group: "body", + type: "String[]", + optional: false, + field: "ethnicity", + description: "

the ethnicities of the hacker

" + }, + { + group: "body", + type: "String[]", + optional: false, + field: "major", + description: "

the major of the hacker

" + }, + { + group: "body", + type: "Number", + optional: false, + field: "graduationYear", + description: + "

the graduation year of the hacker

" + }, + { + group: "body", + type: "Boolean", + optional: false, + field: "codeOfConduct", + description: + "

acceptance of the code of conduct

" + }, + { + group: "body", + type: "Json", + optional: false, + field: "application", + description: + "

The hacker's application. Resume and jobInterest fields are required.

" + } + ] + }, + examples: [ + { + title: "application: ", + content: + '{\n "application":{\n "portfolioURL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n}', + type: "Json" + } + ] + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Hacker creation successful", \n "data": {\n "id":"5bff4d736f86be0a41badb91",\n "application":{\n "portfolioURL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n },\n "status":"Applied",\n "ethnicity":["White or Caucasian"," Asian or Pacific Islander"],\n "accountId":"5bff2a35e533b0f6562b4998",\n "school":"McPherson College",\n "gender":"Female",\n "needsBus":false,\n "major":"Accounting",\n "graduationYear":2019,\n "codeOfConduct":true,\n }\n}', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{"message": "Error while creating hacker", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Successfully retrieved hacker information", \n "data": {\n "id":"5bff4d736f86be0a41badb91",\n "application":{\n "portfolioURL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n },\n "status":"Applied",\n "ethnicity":["White or Caucasian"," Asian or Pacific Islander"],\n "accountId":"5bff2a35e533b0f6562b4998",\n "school":"McPherson College",\n "gender":"Female",\n "needsBus":false,\n "major":"Accounting",\n "graduationYear":2019,\n "codeOfConduct":true,\n }\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "String", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "Object", - optional: false, - field: "data", - description: "

empty

" - } - ] - }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Hacker not found", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/hacker.js", - groupTitle: "Hacker", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/hacker/email/:email" - } - ] - }, - { - type: "get", - url: "/hacker/:id", - title: "get a hacker's information", - name: "getHacker", - group: "Hacker", - version: "0.0.8", - parameter: { - fields: { - param: [ - { - group: "param", - type: "String", - optional: false, - field: "id", - description: "

a hacker's unique mongoID

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "String", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "Object", - optional: false, - field: "data", - description: "

Hacker object

" - } - ] - }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Successfully retrieved hacker information", \n "data": {\n "id":"5bff4d736f86be0a41badb91",\n "application":{\n "portfolioURL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n },\n "status":"Applied",\n "ethnicity":["White or Caucasian"," Asian or Pacific Islander"],\n "accountId":"5bff2a35e533b0f6562b4998",\n "school":"McPherson College",\n "gender":"Female",\n "needsBus":false,\n "major":"Accounting",\n "graduationYear":2019,\n "codeOfConduct":true,\n }\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "String", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "Object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "get", + url: "/hacker/email/:email", + title: "get a hacker's information", + name: "getHacker", + group: "Hacker", + version: "0.0.8", + parameter: { + fields: { + param: [ + { + group: "param", + type: "String", + optional: false, + field: "email", + description: "

a hacker's unique email

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Successfully retrieved hacker information", \n "data": {\n "id":"5bff4d736f86be0a41badb91",\n "application":{\n "portfolioURL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n },\n "status":"Applied",\n "ethnicity":["White or Caucasian"," Asian or Pacific Islander"],\n "accountId":"5bff2a35e533b0f6562b4998",\n "school":"McPherson College",\n "gender":"Female",\n "needsBus":false,\n "major":"Accounting",\n "graduationYear":2019,\n "codeOfConduct":true,\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Hacker not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/email/:email" + } + ] }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Hacker not found", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/hacker.js", - groupTitle: "Hacker", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/hacker/:id" - } - ] - }, - { - type: "get", - url: "/hacker/resume:id", - title: "get the resume for a hacker.", - name: "getHackerResume", - group: "Hacker", - version: "0.0.8", - parameter: { - fields: { - param: [ - { - group: "param", - type: "ObjectId", - optional: false, - field: "id", - description: "

Hacker id

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "String", - optional: false, - field: "message", - description: "

Success message

" - } - ] + type: "get", + url: "/hacker/:id", + title: "get a hacker's information", + name: "getHacker", + group: "Hacker", + version: "0.0.8", + parameter: { + fields: { + param: [ + { + group: "param", + type: "String", + optional: false, + field: "id", + description: "

a hacker's unique mongoID

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Successfully retrieved hacker information", \n "data": {\n "id":"5bff4d736f86be0a41badb91",\n "application":{\n "portfolioURL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n },\n "status":"Applied",\n "ethnicity":["White or Caucasian"," Asian or Pacific Islander"],\n "accountId":"5bff2a35e533b0f6562b4998",\n "school":"McPherson College",\n "gender":"Female",\n "needsBus":false,\n "major":"Accounting",\n "graduationYear":2019,\n "codeOfConduct":true,\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Hacker not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/:id" + } + ] }, - examples: [ - { - title: "Success-Response:", - content: - 'HTTP/1.1 200 OK \n{ \n message: "Downloaded resume", \n data: { \n id: "507f191e810c19729de860ea", \n resume: [Buffer] \n } \n}', - type: "json" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "String", - optional: false, - field: "message", - description: "

"Resume does not exist"

" - } - ] - }, - examples: [ - { - title: "Error-Response:", - content: - 'HTTP/1.1 404 \n{ \n message: "Resume not found", \n data: {} \n}', - type: "json" - } - ] - }, - permission: [ { - name: - "Must be logged in, and the account id must be linked to the hacker." - } - ], - filename: "routes/api/hacker.js", - groupTitle: "Hacker" - }, - { - type: "get", - url: "/hacker/stats", - title: "Gets the stats of all of the hackers who have applied.", - name: "getHackerStats", - group: "Hacker", - version: "0.0.9", - parameter: { - fields: { - query: [ - { - group: "query", - type: "String", - optional: false, - field: "model", - description: - "

the model to be searched (Only hacker supported)

" - }, - { - group: "query", - type: "Array", - optional: false, - field: "q", - description: - "

the query to be executed. For more information on how to format this, please see https://docs.mchacks.ca/architecture/

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Hacker object

" - } - ] + type: "get", + url: "/hacker/resume:id", + title: "get the resume for a hacker.", + name: "getHackerResume", + group: "Hacker", + version: "0.0.8", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "id", + description: "

Hacker id

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + } + ] + }, + examples: [ + { + title: "Success-Response:", + content: + 'HTTP/1.1 200 OK \n{ \n message: "Downloaded resume", \n data: { \n id: "507f191e810c19729de860ea", \n resume: [Buffer] \n } \n}', + type: "json" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: + "

"Resume does not exist"

" + } + ] + }, + examples: [ + { + title: "Error-Response:", + content: + 'HTTP/1.1 404 \n{ \n message: "Resume not found", \n data: {} \n}', + type: "json" + } + ] + }, + permission: [ + { + name: + "Must be logged in, and the account id must be linked to the hacker." + } + ], + filename: "routes/api/hacker.js", + groupTitle: "Hacker" }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Retrieved stats",\n "data": {\n "stats" : {\n "total": 10,\n "status": { "Applied": 10 },\n "school": { "McGill University": 3, "Harvard University": 7 },\n degree: { "Undergraduate": 10 },\n gender: { "Male": 1, "Female": 9 },\n needsBus: { "true": 7, "false": 3 },\n ethnicity: { "White": 10, },\n jobInterest: { "Internship": 10 },\n major: { "Computer Science": 10 },\n graduationYear: { "2019": 10 },\n dietaryRestrictions: { "None": 10 },\n shirtSize: { "M": 3, "XL": 7 },\n age: { "22": 10 }\n }\n }\n}', - type: "object" - } - ] - }, - filename: "routes/api/hacker.js", - groupTitle: "Hacker", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/hacker/stats" - } - ] - }, - { - type: "patch", - url: "/hacker/:id", - title: "update a hacker's information.", - description: - "

This route only contains the ability to update a subset of a hacker's information. If you want to update a status, you must have Admin priviledges and use PATCH /hacker/status/:id.

", - name: "patchHacker", - group: "Hacker", - version: "0.0.8", - parameter: { - fields: { - body: [ - { - group: "body", - type: "String", - optional: true, - field: "school", - description: "

Name of the school the hacker goes to

" - }, - { - group: "body", - type: "String", - optional: true, - field: "gender", - description: "

Gender of the hacker

" - }, - { - group: "body", - type: "Boolean", - optional: true, - field: "needsBus", - description: - "

Whether the hacker requires a bus for transportation

" - }, - { - group: "body", - type: "String[]", - optional: true, - field: "ethnicity", - description: "

the ethnicities of the hacker

" - }, - { - group: "body", - type: "String[]", - optional: true, - field: "major", - description: "

the major of the hacker

" - }, - { - group: "body", - type: "Number", - optional: true, - field: "graduationYear", - description: "

the graduation year of the hacker

" - }, - { - group: "body", - type: "Json", - optional: true, - field: "application", - description: "

The hacker's application

" - } - ] + type: "get", + url: "/hacker/stats", + title: "Gets the stats of all of the hackers who have applied.", + name: "getHackerStats", + group: "Hacker", + version: "0.0.9", + parameter: { + fields: { + query: [ + { + group: "query", + type: "String", + optional: false, + field: "model", + description: + "

the model to be searched (Only hacker supported)

" + }, + { + group: "query", + type: "Array", + optional: false, + field: "q", + description: + "

the query to be executed. For more information on how to format this, please see https://docs.mchacks.ca/architecture/

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Retrieved stats",\n "data": {\n "stats" : {\n "total": 10,\n "status": { "Applied": 10 },\n "school": { "McGill University": 3, "Harvard University": 7 },\n degree: { "Undergraduate": 10 },\n gender: { "Male": 1, "Female": 9 },\n needsBus: { "true": 7, "false": 3 },\n ethnicity: { "White": 10, },\n jobInterest: { "Internship": 10 },\n major: { "Computer Science": 10 },\n graduationYear: { "2019": 10 },\n dietaryRestrictions: { "None": 10 },\n shirtSize: { "M": 3, "XL": 7 },\n age: { "22": 10 }\n }\n }\n}', + type: "object" + } + ] + }, + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/stats" + } + ] }, - examples: [ - { - title: "application: ", - content: - '{\n "portfolioURL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n }', - type: "Json" - } - ] - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Hacker object

" - } - ] - }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Changed hacker information", \n "data": {\n "id":"5bff4d736f86be0a41badb91",\n "application":{\n "portfolioURL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n },\n "status":"Applied",\n "ethnicity":["White or Caucasian"," Asian or Pacific Islander"],\n "accountId":"5bff2a35e533b0f6562b4998",\n "school":"McPherson College",\n "gender":"Female",\n "needsBus":false,\n "major":"Accounting",\n "graduationYear":2019,\n "codeOfConduct":true,\n}', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "string", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] - }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Error while updating hacker", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/hacker.js", - groupTitle: "Hacker", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/hacker/:id" - } - ] - }, - { - type: "patch", - url: "/hacker/confirmation/:id", - title: - "Allows confirmation of hacker attendence if they are accepted. Also allows change from 'confirmed' to 'withdrawn'.", - name: "patchHackerConfirmed", - group: "Hacker", - version: "0.0.9", - parameter: { - fields: { - body: [ - { - group: "body", - type: "string", - optional: true, - field: "status", - description: - "

The new status of the hacker. "Accepted", "Confirmed", or "Withdrawn"

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Hacker object

" - } - ] + type: "patch", + url: "/hacker/:id", + title: "update a hacker's information.", + description: + "

This route only contains the ability to update a subset of a hacker's information. If you want to update a status, you must have Admin priviledges and use PATCH /hacker/status/:id.

", + name: "patchHacker", + group: "Hacker", + version: "0.0.8", + parameter: { + fields: { + body: [ + { + group: "body", + type: "String", + optional: true, + field: "school", + description: + "

Name of the school the hacker goes to

" + }, + { + group: "body", + type: "String", + optional: true, + field: "gender", + description: "

Gender of the hacker

" + }, + { + group: "body", + type: "Boolean", + optional: true, + field: "needsBus", + description: + "

Whether the hacker requires a bus for transportation

" + }, + { + group: "body", + type: "String[]", + optional: true, + field: "ethnicity", + description: "

the ethnicities of the hacker

" + }, + { + group: "body", + type: "String[]", + optional: true, + field: "major", + description: "

the major of the hacker

" + }, + { + group: "body", + type: "Number", + optional: true, + field: "graduationYear", + description: + "

the graduation year of the hacker

" + }, + { + group: "body", + type: "Json", + optional: true, + field: "application", + description: "

The hacker's application

" + } + ] + }, + examples: [ + { + title: "application: ", + content: + '{\n "portfolioURL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n }', + type: "Json" + } + ] + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Changed hacker information", \n "data": {\n "id":"5bff4d736f86be0a41badb91",\n "application":{\n "portfolioURL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n },\n "status":"Applied",\n "ethnicity":["White or Caucasian"," Asian or Pacific Islander"],\n "accountId":"5bff2a35e533b0f6562b4998",\n "school":"McPherson College",\n "gender":"Female",\n "needsBus":false,\n "major":"Accounting",\n "graduationYear":2019,\n "codeOfConduct":true,\n}', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{"message": "Error while updating hacker", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/:id" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Changed hacker information", \n "data": {\n "status": "Confirmed"\n }\n}', - type: "object" - } - ] - }, - permission: [ { - name: "Administrator" + type: "patch", + url: "/hacker/confirmation/:id", + title: + "Allows confirmation of hacker attendence if they are accepted. Also allows change from 'confirmed' to 'withdrawn'.", + name: "patchHackerConfirmed", + group: "Hacker", + version: "0.0.9", + parameter: { + fields: { + body: [ + { + group: "body", + type: "string", + optional: true, + field: "status", + description: + "

The new status of the hacker. "Accepted", "Confirmed", or "Withdrawn"

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Changed hacker information", \n "data": {\n "status": "Confirmed"\n }\n}', + type: "object" + } + ] + }, + permission: [ + { + name: "Administrator" + }, + { + name: "Hacker" + } + ], + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/confirmation/:id" + } + ] }, { - name: "Hacker" - } - ], - filename: "routes/api/hacker.js", - groupTitle: "Hacker", - sampleRequest: [ - { - url: "https://api.mchacks.ca/api/hacker/confirmation/:id" - } - ] - }, - { - type: "patch", - url: "/hacker/status/:id", - title: "update a hacker's status", - name: "patchHackerStatus", - group: "Hacker", - version: "0.0.9", - parameter: { - fields: { - body: [ - { - group: "body", - type: "string", - optional: true, - field: "status", - description: - "

Status of the hacker's application ("None"|"Applied"|"Waitlisted"|"Declined"|"Confirmed"|"Withdrawn"|"Checked-in")

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Hacker object

" - } - ] + type: "patch", + url: "/hacker/status/:id", + title: "update a hacker's status", + name: "patchHackerStatus", + group: "Hacker", + version: "0.0.9", + parameter: { + fields: { + body: [ + { + group: "body", + type: "string", + optional: true, + field: "status", + description: + "

Status of the hacker's application ("None"|"Applied"|"Waitlisted"|"Declined"|"Confirmed"|"Withdrawn"|"Checked-in")

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Changed hacker information", \n "data": {\n "status": "Accepted"\n }\n}', + type: "object" + } + ] + }, + permission: [ + { + name: "Administrator" + } + ], + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/status/:id" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Changed hacker information", \n "data": {\n "status": "Accepted"\n }\n}', - type: "object" - } - ] - }, - permission: [ { - name: "Administrator" - } - ], - filename: "routes/api/hacker.js", - groupTitle: "Hacker", - sampleRequest: [ - { - url: "https://api.mchacks.ca/api/hacker/status/:id" - } - ] - }, - { - type: "post", - url: "/hacker/resume/:id", - title: "upload or update resume for a hacker.", - name: "postHackerResume", - group: "Hacker", - version: "0.0.8", - description: - "

NOTE: This must be sent via multipart/form-data POST request

", - parameter: { - fields: { - param: [ - { - group: "param", - type: "ObjectId", - optional: false, - field: "id", - description: "

Hacker id

" - } - ], - body: [ - { - group: "body", - type: "File", - optional: false, - field: "resume", - description: "

The uploaded file.

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "String", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "Object", - optional: false, - field: "data", - description: - "

Location in the bucket that the file was stored.

" - } - ] + type: "post", + url: "/hacker/resume/:id", + title: "upload or update resume for a hacker.", + name: "postHackerResume", + group: "Hacker", + version: "0.0.8", + description: + "

NOTE: This must be sent via multipart/form-data POST request

", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "id", + description: "

Hacker id

" + } + ], + body: [ + { + group: "body", + type: "File", + optional: false, + field: "resume", + description: "

The uploaded file.

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: + "

Location in the bucket that the file was stored.

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + 'HTTP/1.1 200 OK\n{\n message: "Uploaded resume", \n data: {\n filename: "resumes/1535032624768-507f191e810c19729de860ea"\n }\n}', + type: "json" + } + ] + }, + permission: [ + { + name: + "Must be logged in, and the account id must be linked to the hacker." + } + ], + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/resume/:id" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - 'HTTP/1.1 200 OK\n{\n message: "Uploaded resume", \n data: {\n filename: "resumes/1535032624768-507f191e810c19729de860ea"\n }\n}', - type: "json" - } - ] - }, - permission: [ - { - name: - "Must be logged in, and the account id must be linked to the hacker." - } - ], - filename: "routes/api/hacker.js", - groupTitle: "Hacker", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/hacker/resume/:id" - } - ] - }, - { - type: "post", - url: "/hacker/email/weekOf/:id", - title: "", - description: - "

Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in.

", - name: "postHackerSendWeekOfEmail", - group: "Hacker", - version: "0.0.9", - parameter: { - fields: { - param: [ - { - group: "param", - type: "string", - optional: true, - field: "status", - description: "

The hacker ID

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "post", + url: "/hacker/email/weekOf/:id", + title: "", + description: + "

Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in.

", + name: "postHackerSendWeekOfEmail", + group: "Hacker", + version: "0.0.9", + parameter: { + fields: { + param: [ + { + group: "param", + type: "string", + optional: true, + field: "status", + description: "

The hacker ID

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Hacker week-of email sent.", \n "data": {}\n}', + type: "object" + } + ] + }, + permission: [ + { + name: "Administrator" + } + ], + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/email/weekOf/:id" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Hacker week-of email sent.", \n "data": {}\n}', - type: "object" - } - ] - }, - permission: [ - { - name: "Administrator" - } - ], - filename: "routes/api/hacker.js", - groupTitle: "Hacker", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/hacker/email/weekOf/:id" - } - ] - }, - { - type: "post", - url: "/hacker/email/weekOf/:id", - title: "", - description: - "

Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in.

", - name: "postHackerSendWeekOfEmail", - group: "Hacker", - version: "0.0.9", - parameter: { - fields: { - param: [ - { - group: "param", - type: "string", - optional: true, - field: "status", - description: "

The hacker ID

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "post", + url: "/hacker/email/weekOf/:id", + title: "", + description: + "

Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in.

", + name: "postHackerSendWeekOfEmail", + group: "Hacker", + version: "0.0.9", + parameter: { + fields: { + param: [ + { + group: "param", + type: "string", + optional: true, + field: "status", + description: "

The hacker ID

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Hacker week-of email sent.", \n "data": {}\n}', + type: "object" + } + ] + }, + permission: [ + { + name: "Administrator" + } + ], + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/email/weekOf/:id" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Hacker week-of email sent.", \n "data": {}\n}', - type: "object" - } - ] - }, - permission: [ - { - name: "Administrator" - } - ], - filename: "routes/api/hacker.js", - groupTitle: "Hacker", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/hacker/email/weekOf/:id" - } - ] - }, - { - type: "get", - url: "/sponsor/self", - title: "get information about logged in sponsor", - name: "self", - group: "Hacker", - version: "1.4.1", - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "String", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "Object", - optional: false, - field: "data", - description: "

Sponsor object

" - } - ] - }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Successfully retrieved sponsor information", \n "data": {\n "id": "5bff4d736f86be0a41badb91",\n "accountId": "5bff4d736f86be0a41badb99",\n "tier": 3,\n "company": "companyName",\n "contractURL": "https://www.contractHere.com",\n "nominees": ["5bff4d736f86be0a41badb93","5bff4d736f86be0a41badb94"]\n }\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "String", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "Object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "get", + url: "/sponsor/self", + title: "get information about logged in sponsor", + name: "self", + group: "Hacker", + version: "1.4.1", + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Sponsor object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Successfully retrieved sponsor information", \n "data": {\n "id": "5bff4d736f86be0a41badb91",\n "accountId": "5bff4d736f86be0a41badb99",\n "tier": 3,\n "company": "companyName",\n "contractURL": "https://www.contractHere.com",\n "nominees": ["5bff4d736f86be0a41badb93","5bff4d736f86be0a41badb94"]\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Sponsor not found", "data": {}}', + type: "object" + } + ] + }, + permission: [ + { + name: ": Sponsor" + } + ], + filename: "routes/api/sponsor.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/sponsor/self" + } + ] }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Sponsor not found", "data": {}}', - type: "object" - } - ] - }, - permission: [ { - name: ": Sponsor" - } - ], - filename: "routes/api/sponsor.js", - groupTitle: "Hacker", - sampleRequest: [ - { - url: "https://api.mchacks.ca/api/sponsor/self" - } - ] - }, - { - type: "get", - url: "/hacker/self", - title: "get information about own hacker", - name: "self", - group: "Hacker", - version: "0.0.8", - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Hacker object

" - } - ] + type: "get", + url: "/hacker/self", + title: "get information about own hacker", + name: "self", + group: "Hacker", + version: "0.0.8", + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Hacker object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Hacker found by logged in account id", \n "data": {\n "id":"5bff4d736f86be0a41badb91",\n "application":{\n "portfolioURL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n },\n "status":"Applied",\n "ethnicity":["White or Caucasian"," Asian or Pacific Islander"],\n "accountId":"5bff2a35e533b0f6562b4998",\n "school":"McPherson College",\n "gender":"Female",\n "needsBus":false,\n "major":["Accounting"],\n "graduationYear":2019,\n "codeOfConduct":true,\n } \n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Hacker not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/hacker.js", + groupTitle: "Hacker", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/hacker/self" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Hacker found by logged in account id", \n "data": {\n "id":"5bff4d736f86be0a41badb91",\n "application":{\n "portfolioURL":{\n "resume":"resumes/1543458163426-5bff4d736f86be0a41badb91",\n "github":"https://github.com/abcd",\n "dropler":"https://dribbble.com/abcd",\n "personal":"https://www.hi.com/",\n "linkedIn":"https://linkedin.com/in/abcd",\n "other":"https://github.com/hackmcgill/hackerAPI/issues/168"\n },\n "jobInterest":"Internship",\n "skills":["Javascript","Typescript"],\n "comments":"hi!",\n "essay":"Pls accept me"\n },\n "status":"Applied",\n "ethnicity":["White or Caucasian"," Asian or Pacific Islander"],\n "accountId":"5bff2a35e533b0f6562b4998",\n "school":"McPherson College",\n "gender":"Female",\n "needsBus":false,\n "major":["Accounting"],\n "graduationYear":2019,\n "codeOfConduct":true,\n } \n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "string", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] - }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Hacker not found", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/hacker.js", - groupTitle: "Hacker", - sampleRequest: [ - { - url: "https://api.mchacks.ca/api/hacker/self" - } - ] - }, - { - type: "get", - url: "/", - title: "version", - version: "0.0.8", - name: "index", - group: "Index", - permission: [ { - name: "public" - } - ], - filename: "routes/index.js", - groupTitle: "Index", - sampleRequest: [ - { - url: "https://api.mchacks.ca/api/" - } - ] - }, - { - type: "post", - url: "/api/role/", - title: "create a new role", - name: "createRole", - group: "Role", - version: "1.1.1", - parameter: { - fields: { - body: [ - { - group: "body", - type: "String", - optional: false, - field: "name", - description: "

Name of the route

" - }, - { - group: "body", - type: "Route[]", - optional: false, - field: "routes", - description: "

The routes that this role gives access to

" - } - ] - }, - examples: [ - { - title: "application: ", - content: - '{\n "name": "routename",\n "routes": [\n {\n uri: "/api/hacker/"\n requestType: "POST"\n }\n ]\n}', - type: "Json" - } - ] - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Role object

" - } - ] - }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Role creation successful", \n "data": {\n "name": "routename",\n "routes": [\n {\n uri: "/api/hacker/"\n requestType: "POST"\n }\n ]\n }\n}', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "string", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "get", + url: "/", + title: "version", + version: "0.0.8", + name: "index", + group: "Index", + permission: [ + { + name: "public" + } + ], + filename: "routes/index.js", + groupTitle: "Index", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/" + } + ] }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Error while creating role", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/role.js", - groupTitle: "Role", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/api/role/" - } - ] - }, - { - type: "get", - url: "/search/", - title: "provide a specific query for any defined model", - name: "search", - group: "Search", - version: "0.0.8", - parameter: { - fields: { - query: [ - { - group: "query", - type: "String", - optional: false, - field: "model", - description: "

the model to be searched

" - }, - { - group: "query", - type: "Array", - optional: false, - field: "q", - description: - "

the query to be executed. For more information on how to format this, please see https://docs.mchacks.ca/architecture/

" - }, - { - group: "query", - type: "String", - optional: false, - field: "sort", - description: "

either "asc" or "desc"

" - }, - { - group: "query", - type: "number", - optional: false, - field: "page", - description: "

the page number that you would like

" - }, - { - group: "query", - type: "number", - optional: false, - field: "limit", - description: - "

the maximum number of results that you would like returned

" - }, - { - group: "query", - type: "any", - optional: false, - field: "sort_by", - description: - "

any parameter you want to sort the results by

" - }, - { - group: "query", - type: "boolean", - optional: false, - field: "expand", - description: - "

whether you want to expand sub documents within the results

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "String", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "Object", - optional: false, - field: "data", - description: "

Results

" - } - ] + type: "post", + url: "/api/role/", + title: "create a new role", + name: "createRole", + group: "Role", + version: "1.1.1", + parameter: { + fields: { + body: [ + { + group: "body", + type: "String", + optional: false, + field: "name", + description: "

Name of the route

" + }, + { + group: "body", + type: "Route[]", + optional: false, + field: "routes", + description: + "

The routes that this role gives access to

" + } + ] + }, + examples: [ + { + title: "application: ", + content: + '{\n "name": "routename",\n "routes": [\n {\n uri: "/api/hacker/"\n requestType: "POST"\n }\n ]\n}', + type: "Json" + } + ] + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Role object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Role creation successful", \n "data": {\n "name": "routename",\n "routes": [\n {\n uri: "/api/hacker/"\n requestType: "POST"\n }\n ]\n }\n}', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{"message": "Error while creating role", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/role.js", + groupTitle: "Role", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/api/role/" + } + ] }, - examples: [ - { - title: "Success-Response:", - content: - '{\n "message": "Successfully executed query, returning all results",\n "data": [\n {...}\n ]\n }', - type: "object" - }, - { - title: "Success-Response:", - content: - '{\n "message": "No results found.",\n "data": {}\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "String", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "Object", - optional: false, - field: "data", - description: "

empty

" - } - ] - }, - examples: [ - { - title: "Error-Response:", - content: '{"message": "Validation failed", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/search.js", - groupTitle: "Search", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/search/" - } - ] - }, - { - type: "get", - url: "/settings/", - title: "Get the settings for the current hackathon", - name: "getSettings", - group: "Settings", - version: "1.1.1", - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Settings Object

" - } - ] + type: "get", + url: "/search/", + title: "provide a specific query for any defined model", + name: "search", + group: "Search", + version: "0.0.8", + parameter: { + fields: { + query: [ + { + group: "query", + type: "String", + optional: false, + field: "model", + description: "

the model to be searched

" + }, + { + group: "query", + type: "Array", + optional: false, + field: "q", + description: + "

the query to be executed. For more information on how to format this, please see https://docs.mchacks.ca/architecture/

" + }, + { + group: "query", + type: "String", + optional: false, + field: "sort", + description: + "

either "asc" or "desc"

" + }, + { + group: "query", + type: "number", + optional: false, + field: "page", + description: + "

the page number that you would like

" + }, + { + group: "query", + type: "number", + optional: false, + field: "limit", + description: + "

the maximum number of results that you would like returned

" + }, + { + group: "query", + type: "any", + optional: false, + field: "sort_by", + description: + "

any parameter you want to sort the results by

" + }, + { + group: "query", + type: "boolean", + optional: false, + field: "expand", + description: + "

whether you want to expand sub documents within the results

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Results

" + } + ] + }, + examples: [ + { + title: "Success-Response:", + content: + '{\n "message": "Successfully executed query, returning all results",\n "data": [\n {...}\n ]\n }', + type: "object" + }, + { + title: "Success-Response:", + content: + '{\n "message": "No results found.",\n "data": {}\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response:", + content: '{"message": "Validation failed", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/search.js", + groupTitle: "Search", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/search/" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Settings creation successful.", \n "data": {\n "settings": {\n openTime: "Wed Feb 06 2019 00:00:00 GMT-0500 (GMT-05:00)",\n closeTime: "Sat Feb 01 2020 00:00:00 GMT-0500 (GMT-05:00)",\n confirmTime: "Sat Feb 20 2020 00:00:00 GMT-0500 (GMT-05:00)"\n }\n }\n}', - type: "object" - } - ] - }, - permission: [ { - name: "public" - } - ], - filename: "routes/api/settings.js", - groupTitle: "Settings", - sampleRequest: [ - { - url: "https://api.mchacks.ca/api/settings/" - } - ] - }, - { - type: "patch", - url: "/settings/", - title: "Patch the settings for the current hackathon", - name: "patchSettings", - group: "Settings", - version: "1.1.1", - parameter: { - fields: { - body: [ - { - group: "body", - type: "Date", - optional: true, - field: "openTime", - description: "

The opening time for the hackathon.

" - }, - { - group: "body", - type: "Date", - optional: true, - field: "closeTime", - description: "

The closing time for the hackathon.

" - }, - { - group: "body", - type: "Date", - optional: true, - field: "confirmTime", - description: - "

The deadline for confirmation for the hackathon.

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Settings Object

" - } - ] + type: "get", + url: "/settings/", + title: "Get the settings for the current hackathon", + name: "getSettings", + group: "Settings", + version: "1.1.1", + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Settings Object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Settings creation successful.", \n "data": {\n "settings": {\n openTime: "Wed Feb 06 2019 00:00:00 GMT-0500 (GMT-05:00)",\n closeTime: "Sat Feb 01 2020 00:00:00 GMT-0500 (GMT-05:00)",\n confirmTime: "Sat Feb 20 2020 00:00:00 GMT-0500 (GMT-05:00)"\n }\n }\n}', + type: "object" + } + ] + }, + permission: [ + { + name: "public" + } + ], + filename: "routes/api/settings.js", + groupTitle: "Settings", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/settings/" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Settings patch successful.", \n "data": {\n "settings": {\n openTime: "Wed Feb 06 2019 00:00:00 GMT-0500 (GMT-05:00)",\n closeTime: "Sat Feb 01 2020 00:00:00 GMT-0500 (GMT-05:00)",\n confirmTime: "Sat Feb 20 2020 00:00:00 GMT-0500 (GMT-05:00)"\n }\n }\n}', - type: "object" - } - ] - }, - permission: [ { - name: "Administrators" - } - ], - filename: "routes/api/settings.js", - groupTitle: "Settings", - sampleRequest: [ - { - url: "https://api.mchacks.ca/api/settings/" - } - ] - }, - { - type: "post", - url: "/sponsor/", - title: "create a new sponsor", - name: "createSponsor", - group: "Sponsor", - version: "0.0.8", - parameter: { - fields: { - body: [ - { - group: "body", - type: "MongoID", - optional: false, - field: "accountId", - description: "

ObjectID of the respective account.

" - }, - { - group: "body", - type: "Number", - optional: false, - field: "tier", - description: - "

Tier of the sponsor, from 0 to 5. 0 is lowest tier, and 5 is the custom tier.

" - }, - { - group: "body", - type: "String", - optional: false, - field: "company", - description: "

Name of the company.

" - }, - { - group: "body", - type: "String", - optional: false, - field: "contractURL", - description: "

URL link to the contract with the company.

" - }, - { - group: "body", - type: "MongoID[]", - optional: false, - field: "nominees", - description: - "

Array of accounts that the company wish to nominate as hackers.

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "String", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "Object", - optional: false, - field: "data", - description: "

Sponsor object

" - } - ] - }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Sponsor creation successful", \n "data": {...}\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "String", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "Object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "patch", + url: "/settings/", + title: "Patch the settings for the current hackathon", + name: "patchSettings", + group: "Settings", + version: "1.1.1", + parameter: { + fields: { + body: [ + { + group: "body", + type: "Date", + optional: true, + field: "openTime", + description: + "

The opening time for the hackathon.

" + }, + { + group: "body", + type: "Date", + optional: true, + field: "closeTime", + description: + "

The closing time for the hackathon.

" + }, + { + group: "body", + type: "Date", + optional: true, + field: "confirmTime", + description: + "

The deadline for confirmation for the hackathon.

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Settings Object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Settings patch successful.", \n "data": {\n "settings": {\n openTime: "Wed Feb 06 2019 00:00:00 GMT-0500 (GMT-05:00)",\n closeTime: "Sat Feb 01 2020 00:00:00 GMT-0500 (GMT-05:00)",\n confirmTime: "Sat Feb 20 2020 00:00:00 GMT-0500 (GMT-05:00)"\n }\n }\n}', + type: "object" + } + ] + }, + permission: [ + { + name: "Administrators" + } + ], + filename: "routes/api/settings.js", + groupTitle: "Settings", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/settings/" + } + ] }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Error while creating sponsor", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/sponsor.js", - groupTitle: "Sponsor", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/sponsor/" - } - ] - }, - { - type: "get", - url: "/sponsor/:id", - title: "get a sponsor's information", - name: "getSponsor", - group: "Sponsor", - version: "0.0.8", - parameter: { - fields: { - param: [ - { - group: "param", - type: "string", - optional: false, - field: "id", - description: "

a sponsor's unique mongoID

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "String", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "Object", - optional: false, - field: "data", - description: "

Sponsor object

" - } - ] + type: "post", + url: "/sponsor/", + title: "create a new sponsor", + name: "createSponsor", + group: "Sponsor", + version: "0.0.8", + parameter: { + fields: { + body: [ + { + group: "body", + type: "MongoID", + optional: false, + field: "accountId", + description: + "

ObjectID of the respective account.

" + }, + { + group: "body", + type: "Number", + optional: false, + field: "tier", + description: + "

Tier of the sponsor, from 0 to 5. 0 is lowest tier, and 5 is the custom tier.

" + }, + { + group: "body", + type: "String", + optional: false, + field: "company", + description: "

Name of the company.

" + }, + { + group: "body", + type: "String", + optional: false, + field: "contractURL", + description: + "

URL link to the contract with the company.

" + }, + { + group: "body", + type: "MongoID[]", + optional: false, + field: "nominees", + description: + "

Array of accounts that the company wish to nominate as hackers.

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Sponsor object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Sponsor creation successful", \n "data": {...}\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{"message": "Error while creating sponsor", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/sponsor.js", + groupTitle: "Sponsor", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/sponsor/" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Successfully retrieved sponsor information", \n "data": {\n "id": "5bff4d736f86be0a41badb91",\n "accountId": "5bff4d736f86be0a41badb99",\n "tier": 3,\n "company": "companyName",\n "contractURL": "https://www.contractHere.com",\n "nominees": ["5bff4d736f86be0a41badb93","5bff4d736f86be0a41badb94"]\n }\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "String", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "Object", - optional: false, - field: "data", - description: "

empty

" - } - ] - }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Sponsor not found", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/sponsor.js", - groupTitle: "Sponsor", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/sponsor/:id" - } - ] - }, - { - type: "patch", - url: "/sponsor/", - title: "update a sponsor", - name: "patchSponsor", - group: "Sponsor", - version: "1.3.0", - parameter: { - fields: { - param: [ - { - group: "param", - type: "ObjectId", - optional: false, - field: "id", - description: "

ObjectID of the sponsor

" - } - ], - body: [ - { - group: "body", - type: "String", - optional: false, - field: "company", - description: "

Name of the company.

" - }, - { - group: "body", - type: "String", - optional: false, - field: "contractURL", - description: "

URL link to the contract with the company.

" - }, - { - group: "body", - type: "ObjectId[]", - optional: false, - field: "nominees", - description: - "

Array of accounts that the company wish to nominate as hackers.

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "String", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "Object", - optional: false, - field: "data", - description: "

Sponsor object

" - } - ] - }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Sponsor update successful", \n "data": {...}\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "String", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "Object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "get", + url: "/sponsor/:id", + title: "get a sponsor's information", + name: "getSponsor", + group: "Sponsor", + version: "0.0.8", + parameter: { + fields: { + param: [ + { + group: "param", + type: "string", + optional: false, + field: "id", + description: "

a sponsor's unique mongoID

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Sponsor object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Successfully retrieved sponsor information", \n "data": {\n "id": "5bff4d736f86be0a41badb91",\n "accountId": "5bff4d736f86be0a41badb99",\n "tier": 3,\n "company": "companyName",\n "contractURL": "https://www.contractHere.com",\n "nominees": ["5bff4d736f86be0a41badb93","5bff4d736f86be0a41badb94"]\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Sponsor not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/sponsor.js", + groupTitle: "Sponsor", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/sponsor/:id" + } + ] }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Error while updating sponsor", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/sponsor.js", - groupTitle: "Sponsor", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/sponsor/" - } - ] - }, - { - type: "post", - url: "/team/", - title: "create a new team consisting of only the logged in user", - name: "createTeam", - group: "Team", - version: "0.0.8", - parameter: { - fields: { - body: [ - { - group: "body", - type: "String", - optional: false, - field: "name", - description: "

Name of the team.

" - }, - { - group: "body", - type: "String", - optional: true, - field: "devpostURL", - description: - "

Devpost link to hack. Once the link is sent, the hack will be considered to be submitted.

" - }, - { - group: "body", - type: "String", - optional: true, - field: "projectName", - description: "

Name of the team.

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Team object

" - } - ] + type: "patch", + url: "/sponsor/", + title: "update a sponsor", + name: "patchSponsor", + group: "Sponsor", + version: "1.3.0", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "id", + description: "

ObjectID of the sponsor

" + } + ], + body: [ + { + group: "body", + type: "String", + optional: false, + field: "company", + description: "

Name of the company.

" + }, + { + group: "body", + type: "String", + optional: false, + field: "contractURL", + description: + "

URL link to the contract with the company.

" + }, + { + group: "body", + type: "ObjectId[]", + optional: false, + field: "nominees", + description: + "

Array of accounts that the company wish to nominate as hackers.

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Sponsor object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Sponsor update successful", \n "data": {...}\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{"message": "Error while updating sponsor", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/sponsor.js", + groupTitle: "Sponsor", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/sponsor/" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Team creation successful", \n "data": {...}\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "string", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] - }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Error while creating team", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/team.js", - groupTitle: "Team", - sampleRequest: [ { - 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: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

{}

" - } - ] + type: "post", + url: "/team/", + title: "create a new team consisting of only the logged in user", + name: "createTeam", + group: "Team", + version: "0.0.8", + parameter: { + fields: { + body: [ + { + group: "body", + type: "String", + optional: false, + field: "name", + description: "

Name of the team.

" + }, + { + group: "body", + type: "String", + optional: true, + field: "devpostURL", + description: + "

Devpost link to hack. Once the link is sent, the hack will be considered to be submitted.

" + }, + { + group: "body", + type: "String", + optional: true, + field: "projectName", + description: "

Name of the team.

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Team object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Team creation successful", \n "data": {...}\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{"message": "Error while creating team", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/team.js", + groupTitle: "Team", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/team/" + } + ] }, - 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", - title: "get a team's information", - name: "getTeam", - group: "Team", - version: "0.0.8", - parameter: { - fields: { - param: [ - { - group: "param", - type: "ObjectId", - optional: false, - field: "id", - description: "

MongoId of the team

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "String", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "Object", - optional: false, - field: "data", - description: "

Team object

" - } - ] - }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Team retrieval successful", \n "data": { \n "team": {\n "name":"foo",\n "members": [\n ObjectId(\'...\')\n ],\n "devpostURL": "www.devpost.com/foo",\n "projectName": "fooey"\n },\n "members": [\n {\n "firstName": "John",\n "lastName": "Doe"\n }\n ],\n }\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "String", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "Object", - optional: false, - field: "data", - description: "

empty

" - } - ] + 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: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

{}

" + } + ] + }, + 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/" + } + ] }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Team not found", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/team.js", - groupTitle: "Team", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/team/:id" - } - ] - }, - { - type: "patch", - url: "/team/join/", - title: "Allows a logged in hacker to join a team by name", - name: "patchJoinTeam", - group: "Team", - version: "1.1.1", - parameter: { - fields: { - body: [ - { - group: "body", - type: "string", - optional: true, - field: "name", - description: "

Name of the team to join

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

{}

" - } - ] + type: "get", + url: "/team/:id", + title: "get a team's information", + name: "getTeam", + group: "Team", + version: "0.0.8", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "id", + description: "

MongoId of the team

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Team object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Team retrieval successful", \n "data": { \n "team": {\n "name":"foo",\n "members": [\n ObjectId(\'...\')\n ],\n "devpostURL": "www.devpost.com/foo",\n "projectName": "fooey"\n },\n "members": [\n {\n "firstName": "John",\n "lastName": "Doe"\n }\n ],\n }\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: '{"message": "Team not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/team.js", + groupTitle: "Team", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/team/:id" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Team join successful.", \n "data": {}\n}', - type: "object" - } - ] - }, - filename: "routes/api/team.js", - groupTitle: "Team", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/team/join/" - } - ] - }, - { - type: "patch", - url: "/team/:hackerId", - title: - "Update a team's information. The team is specified by the hacker belonging to it.", - name: "patchTeam", - group: "Team", - version: "0.0.8", - description: - "

We use hackerId instead of teamId because authorization requires a one-to-one mapping from param id to accountId, but we are not able to have that from teamId to accountId due to multiple members in a team. Instead, we use hackerId, as there is a 1 to 1 link between hackerId to teamId, and a 1 to 1 link between hackerId and accountId

", - parameter: { - fields: { - param: [ - { - group: "param", - type: "ObjectId", - optional: false, - field: "hackerId", - description: "

a hacker's unique Id

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "String", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "Object", - optional: false, - field: "data", - description: "

Team object

" - } - ] + type: "patch", + url: "/team/join/", + title: "Allows a logged in hacker to join a team by name", + name: "patchJoinTeam", + group: "Team", + version: "1.1.1", + parameter: { + fields: { + body: [ + { + group: "body", + type: "string", + optional: true, + field: "name", + description: "

Name of the team to join

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

{}

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Team join successful.", \n "data": {}\n}', + type: "object" + } + ] + }, + filename: "routes/api/team.js", + groupTitle: "Team", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/team/join/" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Team update successful.", \n "data": {...}\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "String", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "Object", - optional: false, - field: "data", - description: "

Query input that caused the error.

" - } - ] - }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Team not found", "data": {teamId}}', - type: "object" - } - ] - }, - filename: "routes/api/team.js", - groupTitle: "Team", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/team/:hackerId" - } - ] - }, - { - type: "post", - url: "/volunteer/", - title: "create a new volunteer", - name: "createVolunteer", - group: "Volunteer", - version: "0.0.8", - parameter: { - fields: { - body: [ - { - group: "body", - type: "MongoID", - optional: false, - field: "accountId", - description: "

MongoID of the account of the volunteer

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "string", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "object", - optional: false, - field: "data", - description: "

Volunteer object

" - } - ] - }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Volunteer creation successful", \n "data": {...}\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "string", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "object", - optional: false, - field: "data", - description: "

empty

" - } - ] + type: "patch", + url: "/team/:hackerId", + title: + "Update a team's information. The team is specified by the hacker belonging to it.", + name: "patchTeam", + group: "Team", + version: "0.0.8", + description: + "

We use hackerId instead of teamId because authorization requires a one-to-one mapping from param id to accountId, but we are not able to have that from teamId to accountId due to multiple members in a team. Instead, we use hackerId, as there is a 1 to 1 link between hackerId to teamId, and a 1 to 1 link between hackerId and accountId

", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "hackerId", + description: "

a hacker's unique Id

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Team object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Team update successful.", \n "data": {...}\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: + "

Query input that caused the error.

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{"message": "Team not found", "data": {teamId}}', + type: "object" + } + ] + }, + filename: "routes/api/team.js", + groupTitle: "Team", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/team/:hackerId" + } + ] }, - examples: [ - { - title: "Error-Response: ", - content: - '{"message": "Error while creating volunteer", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/volunteer.js", - groupTitle: "Volunteer", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/volunteer/" - } - ] - }, - { - type: "get", - url: "/volunteer/:id", - title: "get a volunteer's information", - name: "getVolunteer", - group: "Volunteer", - version: "1.3.0", - parameter: { - fields: { - param: [ - { - group: "param", - type: "ObjectId", - optional: false, - field: "id", - description: "

a volunteer's unique mongoID

" - } - ] - } - }, - success: { - fields: { - "Success 200": [ - { - group: "Success 200", - type: "String", - optional: false, - field: "message", - description: "

Success message

" - }, - { - group: "Success 200", - type: "Object", - optional: false, - field: "data", - description: "

Volunteer object

" - } - ] + type: "post", + url: "/volunteer/", + title: "create a new volunteer", + name: "createVolunteer", + group: "Volunteer", + version: "0.0.8", + parameter: { + fields: { + body: [ + { + group: "body", + type: "MongoID", + optional: false, + field: "accountId", + description: + "

MongoID of the account of the volunteer

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "string", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "object", + optional: false, + field: "data", + description: "

Volunteer object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Volunteer creation successful", \n "data": {...}\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "string", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{"message": "Error while creating volunteer", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/volunteer.js", + groupTitle: "Volunteer", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/volunteer/" + } + ] }, - examples: [ - { - title: "Success-Response: ", - content: - '{\n "message": "Successfully retrieved volunteer information", \n "data": {...}\n }', - type: "object" - } - ] - }, - error: { - fields: { - "Error 4xx": [ - { - group: "Error 4xx", - type: "String", - optional: false, - field: "message", - description: "

Error message

" - }, - { - group: "Error 4xx", - type: "Object", - optional: false, - field: "data", - description: "

empty

" - } - ] - }, - examples: [ - { - title: "Error-Response: ", - content: '{"message": "Volunteer not found", "data": {}}', - type: "object" - } - ] - }, - filename: "routes/api/volunteer.js", - groupTitle: "Volunteer", - sampleRequest: [ { - url: "https://api.mchacks.ca/api/volunteer/:id" - } - ] - } - ] + type: "get", + url: "/volunteer/:id", + title: "get a volunteer's information", + name: "getVolunteer", + group: "Volunteer", + version: "1.3.0", + parameter: { + fields: { + param: [ + { + group: "param", + type: "ObjectId", + optional: false, + field: "id", + description: "

a volunteer's unique mongoID

" + } + ] + } + }, + success: { + fields: { + "Success 200": [ + { + group: "Success 200", + type: "String", + optional: false, + field: "message", + description: "

Success message

" + }, + { + group: "Success 200", + type: "Object", + optional: false, + field: "data", + description: "

Volunteer object

" + } + ] + }, + examples: [ + { + title: "Success-Response: ", + content: + '{\n "message": "Successfully retrieved volunteer information", \n "data": {...}\n }', + type: "object" + } + ] + }, + error: { + fields: { + "Error 4xx": [ + { + group: "Error 4xx", + type: "String", + optional: false, + field: "message", + description: "

Error message

" + }, + { + group: "Error 4xx", + type: "Object", + optional: false, + field: "data", + description: "

empty

" + } + ] + }, + examples: [ + { + title: "Error-Response: ", + content: + '{"message": "Volunteer not found", "data": {}}', + type: "object" + } + ] + }, + filename: "routes/api/volunteer.js", + groupTitle: "Volunteer", + sampleRequest: [ + { + url: "https://api.mchacks.ca/api/volunteer/:id" + } + ] + } + ] }); diff --git a/docs/api/vendor/jquery.min.js b/docs/api/vendor/jquery.min.js index 41c6cfc9..fd1f8aec 100644 --- a/docs/api/vendor/jquery.min.js +++ b/docs/api/vendor/jquery.min.js @@ -1448,12 +1448,9 @@ i > 1 && sa(m), i > 1 && qa( - a - .slice(0, i - 1) - .concat({ - value: - " " === a[i - 2].type ? "*" : "" - }) + a.slice(0, i - 1).concat({ + value: " " === a[i - 2].type ? "*" : "" + }) ).replace(Q, "$1"), c, e > i && wa(a.slice(i, e)), diff --git a/middlewares/auth.middleware.js b/middlewares/auth.middleware.js index de130b0f..ca2c26e4 100644 --- a/middlewares/auth.middleware.js +++ b/middlewares/auth.middleware.js @@ -3,24 +3,24 @@ const jwt = require("jsonwebtoken"); const passport = require("passport"); const Services = { - Auth: require("../services/auth.service"), - ResetPasswordToken: require("../services/resetPassword.service"), - Account: require("../services/account.service"), - Email: require("../services/email.service"), - AccountConfirmation: require("../services/accountConfirmation.service"), - Role: require("../services/role.service"), - RoleBinding: require("../services/roleBinding.service"), - Env: require("../services/env.service") + Auth: require("../services/auth.service"), + ResetPasswordToken: require("../services/resetPassword.service"), + Account: require("../services/account.service"), + Email: require("../services/email.service"), + AccountConfirmation: require("../services/accountConfirmation.service"), + Role: require("../services/role.service"), + RoleBinding: require("../services/roleBinding.service"), + Env: require("../services/env.service") }; const Middleware = { - Util: require("./util.middleware") + Util: require("./util.middleware") }; const Constants = { - General: require("../constants/general.constant"), - Error: require("../constants/error.constant"), - Role: require("../constants/role.constant") + General: require("../constants/general.constant"), + Error: require("../constants/error.constant"), + Role: require("../constants/role.constant") }; /** @@ -31,32 +31,32 @@ const Constants = { * Failed authentication returns a AUTH 401 error, and errors during login will return res with a LOGIN 500 error. */ function login(req, res, next) { - passport.authenticate("emailAndPass", function(err, user) { - if (err) { - return next({ - status: 500, - message: Constants.Error.GENERIC_500_MESSAGE, - error: {} - }); - } - if (!user) { - return next({ - status: 401, - message: Constants.Error.AUTH_401_MESSAGE, - error: {} - }); - } - req.login(user, loginErr => { - if (loginErr) { - return next({ - status: 500, - message: Constants.Error.LOGIN_500_MESSAGE, - error: {} + passport.authenticate("emailAndPass", function(err, user) { + if (err) { + return next({ + status: 500, + message: Constants.Error.GENERIC_500_MESSAGE, + error: {} + }); + } + if (!user) { + return next({ + status: 401, + message: Constants.Error.AUTH_401_MESSAGE, + error: {} + }); + } + req.login(user, (loginErr) => { + if (loginErr) { + return next({ + status: 500, + message: Constants.Error.LOGIN_500_MESSAGE, + error: {} + }); + } + return next(); }); - } - return next(); - }); - })(req, res, next); + })(req, res, next); } /** @@ -64,19 +64,19 @@ function login(req, res, next) { * Calls next() if the user is properly authenticated. */ function ensureAuthenticated() { - return function(req, res, next) { - if (req.isUnauthenticated()) { - return next({ - status: 401, - message: Constants.Error.AUTH_401_MESSAGE, - error: { - route: req.path + return function(req, res, next) { + if (req.isUnauthenticated()) { + return next({ + status: 401, + message: Constants.Error.AUTH_401_MESSAGE, + error: { + route: req.path + } + }); + } else { + return next(); } - }); - } else { - return next(); - } - }; + }; } /** @@ -85,26 +85,26 @@ function ensureAuthenticated() { * Calls next() if the user is properly authorized. */ function ensureAuthorized(findByIdFns) { - return function(req, res, next) { - Services.Auth.ensureAuthorized(req, findByIdFns).then( - auth => { - if (!auth) { - return next({ - status: 403, - message: Constants.Error.AUTH_403_MESSAGE, - error: { - route: req.path + return function(req, res, next) { + Services.Auth.ensureAuthorized(req, findByIdFns).then( + (auth) => { + if (!auth) { + return next({ + status: 403, + message: Constants.Error.AUTH_403_MESSAGE, + error: { + route: req.path + } + }); + } else { + return next(); + } + }, + (err) => { + return next(err); } - }); - } else { - return next(); - } - }, - err => { - return next(err); - } - ); - }; + ); + }; } /** @@ -114,17 +114,17 @@ function ensureAuthorized(findByIdFns) { * @param {(err?)=>void} next */ async function retrieveRoleBindings(req, res, next) { - const roleBindings = await Services.RoleBinding.getRoleBindingForAcct( - req.params.id - ); - if (!roleBindings) { - return next({ - status: 404, - message: "Role Bindings not found" - }); - } - req.roleBindings = roleBindings; - return next(); + const roleBindings = await Services.RoleBinding.getRoleBindingForAcct( + req.params.id + ); + if (!roleBindings) { + return next({ + status: 404, + message: "Role Bindings not found" + }); + } + req.roleBindings = roleBindings; + return next(); } /** @@ -135,23 +135,23 @@ async function retrieveRoleBindings(req, res, next) { * @param {*} next */ async function changePassword(req, res, next) { - const acc = await Services.Account.getAccountIfValid( - req.user.email, - req.body.oldPassword - ); - // user's old password is correct - if (!!acc) { - req.body.account = await Services.Account.updatePassword( - req.user.id, - req.body.newPassword + const acc = await Services.Account.getAccountIfValid( + req.user.email, + req.body.oldPassword ); - return next(); - } else { - return next({ - status: 401, - message: Constants.Error.AUTH_401_MESSAGE - }); - } + // user's old password is correct + if (!!acc) { + req.body.account = await Services.Account.updatePassword( + req.user.id, + req.body.newPassword + ); + return next(); + } else { + return next({ + status: 401, + message: Constants.Error.AUTH_401_MESSAGE + }); + } } /** @@ -161,44 +161,44 @@ async function changePassword(req, res, next) { * @param {(err?)=>void} next */ async function sendResetPasswordEmailMiddleware(req, res, next) { - const user = await Services.Account.findByEmail(req.body.email); - if (user) { - //create the reset password token - await Services.ResetPasswordToken.create(user.id); - //find the thing we just created - const ResetPasswordTokenModel = await Services.ResetPasswordToken.findByAccountId( - user.id - ); - //generate email - const token = Services.ResetPasswordToken.generateToken( - ResetPasswordTokenModel.id, - user.id - ); - const address = Services.Env.isProduction() - ? process.env.FRONTEND_ADDRESS_DEPLOY - : process.env.FRONTEND_ADDRESS_DEV; - const mailData = Services.ResetPasswordToken.generateResetPasswordEmail( - address, - req.body.email, - token - ); - if (mailData !== undefined) { - Services.Email.send(mailData, err => { - if (err) { - return next(err); + const user = await Services.Account.findByEmail(req.body.email); + if (user) { + //create the reset password token + await Services.ResetPasswordToken.create(user.id); + //find the thing we just created + const ResetPasswordTokenModel = await Services.ResetPasswordToken.findByAccountId( + user.id + ); + //generate email + const token = Services.ResetPasswordToken.generateToken( + ResetPasswordTokenModel.id, + user.id + ); + const address = Services.Env.isProduction() + ? process.env.FRONTEND_ADDRESS_DEPLOY + : process.env.FRONTEND_ADDRESS_DEV; + const mailData = Services.ResetPasswordToken.generateResetPasswordEmail( + address, + req.body.email, + token + ); + if (mailData !== undefined) { + Services.Email.send(mailData, (err) => { + if (err) { + return next(err); + } else { + return next(); + } + }); } else { - return next(); + return next({ + message: Constants.Error.EMAIL_500_MESSAGE + }); } - }); } else { - return next({ - message: Constants.Error.EMAIL_500_MESSAGE - }); + //Didn't find the user, but we don't want to throw an error because someone might be trying to see who has an account. + return next(); } - } else { - //Didn't find the user, but we don't want to throw an error because someone might be trying to see who has an account. - return next(); - } } /** @@ -210,44 +210,44 @@ async function sendResetPasswordEmailMiddleware(req, res, next) { * @param {(err?)=>void} next */ async function sendConfirmAccountEmail(req, res, next) { - const account = req.body.account; - if (account.confirmed) { - return next(); - } - await Services.AccountConfirmation.create( - Constants.General.HACKER, - account.email, - account.id - ); - const accountConfirmationToken = await Services.AccountConfirmation.findByAccountId( - account.id - ); - const token = Services.AccountConfirmation.generateToken( - accountConfirmationToken.id, - account.id - ); - const address = Services.Env.isProduction() - ? process.env.FRONTEND_ADDRESS_DEPLOY - : process.env.FRONTEND_ADDRESS_DEV; - const mailData = Services.AccountConfirmation.generateAccountConfirmationEmail( - address, - account.email, - Constants.General.HACKER, - token - ); - if (mailData !== undefined) { - Services.Email.send(mailData, err => { - if (err) { - return next(err); - } else { + const account = req.body.account; + if (account.confirmed) { return next(); - } - }); - } else { - return next({ - message: Constants.Error.EMAIL_500_MESSAGE - }); - } + } + await Services.AccountConfirmation.create( + Constants.General.HACKER, + account.email, + account.id + ); + const accountConfirmationToken = await Services.AccountConfirmation.findByAccountId( + account.id + ); + const token = Services.AccountConfirmation.generateToken( + accountConfirmationToken.id, + account.id + ); + const address = Services.Env.isProduction() + ? process.env.FRONTEND_ADDRESS_DEPLOY + : process.env.FRONTEND_ADDRESS_DEV; + const mailData = Services.AccountConfirmation.generateAccountConfirmationEmail( + address, + account.email, + Constants.General.HACKER, + token + ); + if (mailData !== undefined) { + Services.Email.send(mailData, (err) => { + if (err) { + return next(err); + } else { + return next(); + } + }); + } else { + return next({ + message: Constants.Error.EMAIL_500_MESSAGE + }); + } } /** @@ -257,48 +257,48 @@ async function sendConfirmAccountEmail(req, res, next) { * @param {(err?)=>void} next */ async function resendConfirmAccountEmail(req, res, next) { - const account = await Services.Account.findById(req.user.id); - if (account.confirmed) { - return next({ - status: 422, - message: "Account already confirmed" - }); - } - const accountConfirmationToken = await Services.AccountConfirmation.findByAccountId( - account.id - ); - if (!accountConfirmationToken) { - return next({ - status: 428, - message: "Account confirmation token does not exist" - }); - } - const token = Services.AccountConfirmation.generateToken( - accountConfirmationToken.id, - account.id - ); - const address = Services.Env.isProduction() - ? process.env.FRONTEND_ADDRESS_DEPLOY - : process.env.FRONTEND_ADDRESS_DEV; - const mailData = Services.AccountConfirmation.generateAccountConfirmationEmail( - address, - account.email, - accountConfirmationToken.accountType, - token - ); - if (mailData !== undefined) { - Services.Email.send(mailData, err => { - if (err) { - return next(err); - } else { - return next(); - } - }); - } else { - return next({ - message: "Error while generating email" - }); - } + const account = await Services.Account.findById(req.user.id); + if (account.confirmed) { + return next({ + status: 422, + message: "Account already confirmed" + }); + } + const accountConfirmationToken = await Services.AccountConfirmation.findByAccountId( + account.id + ); + if (!accountConfirmationToken) { + return next({ + status: 428, + message: "Account confirmation token does not exist" + }); + } + const token = Services.AccountConfirmation.generateToken( + accountConfirmationToken.id, + account.id + ); + const address = Services.Env.isProduction() + ? process.env.FRONTEND_ADDRESS_DEPLOY + : process.env.FRONTEND_ADDRESS_DEV; + const mailData = Services.AccountConfirmation.generateAccountConfirmationEmail( + address, + account.email, + accountConfirmationToken.accountType, + token + ); + if (mailData !== undefined) { + Services.Email.send(mailData, (err) => { + if (err) { + return next(err); + } else { + return next(); + } + }); + } else { + return next({ + message: "Error while generating email" + }); + } } /** @@ -309,18 +309,18 @@ async function resendConfirmAccountEmail(req, res, next) { * @param {(err?)=>void} next */ function parseResetToken(req, res, next) { - jwt.verify( - req.body["x-reset-token"], - process.env.JWT_RESET_PWD_SECRET, - function(err, decoded) { - if (err) { - return next(err); - } else { - req.body.decodedToken = decoded; - return next(); - } - } - ); + jwt.verify( + req.body["x-reset-token"], + process.env.JWT_RESET_PWD_SECRET, + function(err, decoded) { + if (err) { + return next(err); + } else { + req.body.decodedToken = decoded; + return next(); + } + } + ); } /** @@ -332,19 +332,19 @@ function parseResetToken(req, res, next) { * @param {(err?)=>void} next */ function parseAccountConfirmationToken(req, res, next) { - if (!!req.body.token) { - jwt.verify(req.body.token, process.env.JWT_CONFIRM_ACC_SECRET, function( - err, - decoded - ) { - if (err) { - return next(err); - } else { - req.body.decodedToken = decoded; - } - }); - } - return next(); + if (!!req.body.token) { + jwt.verify(req.body.token, process.env.JWT_CONFIRM_ACC_SECRET, function( + err, + decoded + ) { + if (err) { + return next(err); + } else { + req.body.decodedToken = decoded; + } + }); + } + return next(); } /** @@ -354,20 +354,20 @@ function parseAccountConfirmationToken(req, res, next) { * @param {(err?)=>void} next */ async function getAccountTypeFromConfirmationToken(req, res, next) { - const confirmationObj = await Services.AccountConfirmation.findById( - req.body.decodedToken.accountConfirmationId - ); - if (confirmationObj) { - req.body.accountType = confirmationObj.accountType; - return next(); - } else { - //Either the token was already used, it's invalid, or user does not exist. - return next({ - status: 401, - message: Constants.Error.ACCOUNT_TOKEN_401_MESSAGE, - error: {} - }); - } + const confirmationObj = await Services.AccountConfirmation.findById( + req.body.decodedToken.accountConfirmationId + ); + if (confirmationObj) { + req.body.accountType = confirmationObj.accountType; + return next(); + } else { + //Either the token was already used, it's invalid, or user does not exist. + return next({ + status: 401, + message: Constants.Error.ACCOUNT_TOKEN_401_MESSAGE, + error: {} + }); + } } /** @@ -377,23 +377,23 @@ async function getAccountTypeFromConfirmationToken(req, res, next) { * @param {(err?)=>void} next */ async function validateResetToken(req, res, next) { - const resetObj = await Services.ResetPasswordToken.findById( - req.body.decodedToken.resetId - ); - const userObj = await Services.Account.findById( - req.body.decodedToken.accountId - ); - if (resetObj && userObj) { - req.body.user = userObj; - return next(); - } else { - //Either the token was already used, it's invalid, or user does not exist. - return next({ - status: 401, - message: Constants.Error.ACCOUNT_TOKEN_401_MESSAGE, - error: {} - }); - } + const resetObj = await Services.ResetPasswordToken.findById( + req.body.decodedToken.resetId + ); + const userObj = await Services.Account.findById( + req.body.decodedToken.accountId + ); + if (resetObj && userObj) { + req.body.user = userObj; + return next(); + } else { + //Either the token was already used, it's invalid, or user does not exist. + return next({ + status: 401, + message: Constants.Error.ACCOUNT_TOKEN_401_MESSAGE, + error: {} + }); + } } /** @@ -403,26 +403,26 @@ async function validateResetToken(req, res, next) { * @param {(err?)=>void} next */ async function validateConfirmationToken(req, res, next) { - const confirmationObj = await Services.AccountConfirmation.findById( - req.body.decodedToken.accountConfirmationId - ); - const userObj = await Services.Account.findById( - req.body.decodedToken.accountId - ); - if (confirmationObj && userObj && confirmationObj.accountId == userObj.id) { - userObj.confirmed = true; - userObj.accountType = confirmationObj.accountType; - await Services.Account.updateOne(confirmationObj.accountId, userObj); - req.body.user = userObj; - return next(); - } else { - //Either the token was already used, it's invalid, or user does not exist. - return next({ - status: 401, - message: Constants.Error.ACCOUNT_TOKEN_401_MESSAGE, - error: {} - }); - } + const confirmationObj = await Services.AccountConfirmation.findById( + req.body.decodedToken.accountConfirmationId + ); + const userObj = await Services.Account.findById( + req.body.decodedToken.accountId + ); + if (confirmationObj && userObj && confirmationObj.accountId == userObj.id) { + userObj.confirmed = true; + userObj.accountType = confirmationObj.accountType; + await Services.Account.updateOne(confirmationObj.accountId, userObj); + req.body.user = userObj; + return next(); + } else { + //Either the token was already used, it's invalid, or user does not exist. + return next({ + status: 401, + message: Constants.Error.ACCOUNT_TOKEN_401_MESSAGE, + error: {} + }); + } } /** @@ -432,16 +432,16 @@ async function validateConfirmationToken(req, res, next) { * @param {*} next */ async function validateConfirmationTokenWithoutAccount(req, res, next) { - if (!!req.body.decodedToken) { - const confirmationObj = await Services.AccountConfirmation.findById( - req.body.decodedToken.accountConfirmationId - ); - if (!confirmationObj.accountId) { - req.body.accountDetails.confirmed = true; - req.body.accountDetails.accountType = confirmationObj.accountType; + if (!!req.body.decodedToken) { + const confirmationObj = await Services.AccountConfirmation.findById( + req.body.decodedToken.accountConfirmationId + ); + if (!confirmationObj.accountId) { + req.body.accountDetails.confirmed = true; + req.body.accountDetails.accountType = confirmationObj.accountType; + } } - } - return next(); + return next(); } /** @@ -451,14 +451,14 @@ async function validateConfirmationTokenWithoutAccount(req, res, next) { * @param {(err?)=>void} next */ function deleteResetToken(req, res, next) { - Services.ResetPasswordToken.deleteToken(req.body.decodedToken.resetId).then( - () => { - return next(); - }, - err => { - return next(err); - } - ); + Services.ResetPasswordToken.deleteToken(req.body.decodedToken.resetId).then( + () => { + return next(); + }, + (err) => { + return next(err); + } + ); } /** @@ -468,18 +468,18 @@ function deleteResetToken(req, res, next) { * @param {(err?)=>void} next */ async function addCreationRoleBindings(req, res, next) { - // Get the default role for the account type given - const roleName = Constants.General.POST_ROLES[req.body.account.accountType]; - await Services.RoleBinding.createRoleBindingByRoleName( - req.body.account.id, - roleName - ); - // Add default account role bindings - await Services.RoleBinding.createRoleBindingByRoleName( - req.body.account.id, - Constants.Role.accountRole.name - ); - return next(); + // Get the default role for the account type given + const roleName = Constants.General.POST_ROLES[req.body.account.accountType]; + await Services.RoleBinding.createRoleBindingByRoleName( + req.body.account.id, + roleName + ); + // Add default account role bindings + await Services.RoleBinding.createRoleBindingByRoleName( + req.body.account.id, + Constants.Role.accountRole.name + ); + return next(); } /** @@ -487,13 +487,13 @@ async function addCreationRoleBindings(req, res, next) { * @param {string} roleName name of the role to be added to account */ function createRoleBindings(roleName = undefined) { - return Middleware.Util.asyncMiddleware(async (req, res, next) => { - await Services.RoleBinding.createRoleBindingByRoleName( - req.user.id, - roleName - ); - return next(); - }); + return Middleware.Util.asyncMiddleware(async (req, res, next) => { + await Services.RoleBinding.createRoleBindingByRoleName( + req.user.id, + roleName + ); + return next(); + }); } /** @@ -503,11 +503,11 @@ function createRoleBindings(roleName = undefined) { * @param {(err?) => void} next */ async function addAccountTypeRoleBinding(req, res, next) { - await Services.RoleBinding.createRoleBindingByRoleName( - req.user.id, - req.user.accountType - ); - return next(); + await Services.RoleBinding.createRoleBindingByRoleName( + req.user.id, + req.user.accountType + ); + return next(); } /** @@ -517,46 +517,46 @@ async function addAccountTypeRoleBinding(req, res, next) { * @param {(err?) => void } next */ async function retrieveRoles(req, res, next) { - const roles = await Services.Role.getAll(); - req.roles = roles; - return next(); + const roles = await Services.Role.getAll(); + req.roles = roles; + return next(); } module.exports = { - //for each route, set up an authentication middleware for that route - login: login, - ensureAuthenticated: ensureAuthenticated, - ensureAuthorized: ensureAuthorized, - sendResetPasswordEmailMiddleware: Middleware.Util.asyncMiddleware( - sendResetPasswordEmailMiddleware - ), - parseResetToken: parseResetToken, - validateResetToken: Middleware.Util.asyncMiddleware(validateResetToken), - deleteResetToken: deleteResetToken, - sendConfirmAccountEmail: Middleware.Util.asyncMiddleware( - sendConfirmAccountEmail - ), - parseAccountConfirmationToken: parseAccountConfirmationToken, - validateConfirmationToken: Middleware.Util.asyncMiddleware( - validateConfirmationToken - ), - getAccountTypeFromConfirmationToken: Middleware.Util.asyncMiddleware( - getAccountTypeFromConfirmationToken - ), - validateConfirmationTokenWithoutAccount: Middleware.Util.asyncMiddleware( - validateConfirmationTokenWithoutAccount - ), - createRoleBindings: createRoleBindings, - addAccountTypeRoleBinding: Middleware.Util.asyncMiddleware( - addAccountTypeRoleBinding - ), - addCreationRoleBindings: Middleware.Util.asyncMiddleware( - addCreationRoleBindings - ), - resendConfirmAccountEmail: Middleware.Util.asyncMiddleware( - resendConfirmAccountEmail - ), - retrieveRoleBindings: Middleware.Util.asyncMiddleware(retrieveRoleBindings), - retrieveRoles: Middleware.Util.asyncMiddleware(retrieveRoles), - changePassword: Middleware.Util.asyncMiddleware(changePassword) + //for each route, set up an authentication middleware for that route + login: login, + ensureAuthenticated: ensureAuthenticated, + ensureAuthorized: ensureAuthorized, + sendResetPasswordEmailMiddleware: Middleware.Util.asyncMiddleware( + sendResetPasswordEmailMiddleware + ), + parseResetToken: parseResetToken, + validateResetToken: Middleware.Util.asyncMiddleware(validateResetToken), + deleteResetToken: deleteResetToken, + sendConfirmAccountEmail: Middleware.Util.asyncMiddleware( + sendConfirmAccountEmail + ), + parseAccountConfirmationToken: parseAccountConfirmationToken, + validateConfirmationToken: Middleware.Util.asyncMiddleware( + validateConfirmationToken + ), + getAccountTypeFromConfirmationToken: Middleware.Util.asyncMiddleware( + getAccountTypeFromConfirmationToken + ), + validateConfirmationTokenWithoutAccount: Middleware.Util.asyncMiddleware( + validateConfirmationTokenWithoutAccount + ), + createRoleBindings: createRoleBindings, + addAccountTypeRoleBinding: Middleware.Util.asyncMiddleware( + addAccountTypeRoleBinding + ), + addCreationRoleBindings: Middleware.Util.asyncMiddleware( + addCreationRoleBindings + ), + resendConfirmAccountEmail: Middleware.Util.asyncMiddleware( + resendConfirmAccountEmail + ), + retrieveRoleBindings: Middleware.Util.asyncMiddleware(retrieveRoleBindings), + retrieveRoles: Middleware.Util.asyncMiddleware(retrieveRoles), + changePassword: Middleware.Util.asyncMiddleware(changePassword) }; diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index 6a115ac6..f4a75753 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -3,18 +3,18 @@ const TAG = `[ HACKER.MIDDLEWARE.js ]`; const mongoose = require("mongoose"); const Services = { - Hacker: require("../services/hacker.service"), - Storage: require("../services/storage.service"), - Email: require("../services/email.service"), - Account: require("../services/account.service"), - Env: require("../services/env.service") + Hacker: require("../services/hacker.service"), + Storage: require("../services/storage.service"), + Email: require("../services/email.service"), + Account: require("../services/account.service"), + Env: require("../services/env.service") }; const Middleware = { - Util: require("./util.middleware") + Util: require("./util.middleware") }; const Constants = { - General: require("../constants/general.constant"), - Error: require("../constants/error.constant") + General: require("../constants/general.constant"), + Error: require("../constants/error.constant") }; /** @@ -26,8 +26,8 @@ const Constants = { * @description Delete the req.body.id that was added by the validation of route parameter. */ function parsePatch(req, res, next) { - delete req.body.id; - return next(); + delete req.body.id; + return next(); } /** @@ -41,21 +41,21 @@ function parsePatch(req, res, next) { * Adds _id to hackerDetails. */ function parseHacker(req, res, next) { - const hackerDetails = { - _id: mongoose.Types.ObjectId(), - accountId: req.body.accountId, - application: req.body.application, - teamId: req.body.teamId - }; - req.body.token = req.body.authorization; + const hackerDetails = { + _id: mongoose.Types.ObjectId(), + accountId: req.body.accountId, + application: req.body.application, + teamId: req.body.teamId + }; + req.body.token = req.body.authorization; - delete req.body.accountId; - delete req.body.application; - delete req.body.teamId; + delete req.body.accountId; + delete req.body.application; + delete req.body.teamId; - req.body.hackerDetails = hackerDetails; + req.body.hackerDetails = hackerDetails; - return next(); + return next(); } /** @@ -68,9 +68,9 @@ function parseHacker(req, res, next) { * Adds the checked-in status to req.body */ function parseCheckIn(req, res, next) { - req.body.status = Constants.General.HACKER_STATUS_CHECKED_IN; + req.body.status = Constants.General.HACKER_STATUS_CHECKED_IN; - return next(); + return next(); } /** @@ -84,16 +84,16 @@ function parseCheckIn(req, res, next) { * Deletes req.body.confirm afterwards */ function parseConfirmation(req, res, next) { - const confirm = req.body.confirm; + const confirm = req.body.confirm; - if (confirm) { - req.body.status = Constants.General.HACKER_STATUS_CONFIRMED; - } else { - req.body.status = Constants.General.HACKER_STATUS_WITHDRAWN; - } + if (confirm) { + req.body.status = Constants.General.HACKER_STATUS_CONFIRMED; + } else { + req.body.status = Constants.General.HACKER_STATUS_WITHDRAWN; + } - delete req.body.confirm; - return next(); + delete req.body.confirm; + return next(); } /** @@ -105,8 +105,8 @@ function parseConfirmation(req, res, next) { * @description Adds status to hackerDetails. */ function addDefaultStatus(req, res, next) { - req.body.hackerDetails.status = "Applied"; - return next(); + req.body.hackerDetails.status = "Applied"; + return next(); } /** @@ -115,26 +115,26 @@ function addDefaultStatus(req, res, next) { * @param {(err?) => void} next */ async function validateConfirmedStatus(account, next) { - if (!account) { - return next({ - status: 404, - message: Constants.Error.ACCOUNT_404_MESSAGE, - error: {} - }); - } else if (!account.confirmed) { - return next({ - status: 403, - message: Constants.Error.ACCOUNT_403_MESSAGE, - error: {} - }); - } else if (account.accountType !== Constants.General.HACKER) { - return next({ - status: 409, - message: Constants.Error.ACCOUNT_TYPE_409_MESSAGE - }); - } else { - return next(); - } + if (!account) { + return next({ + status: 404, + message: Constants.Error.ACCOUNT_404_MESSAGE, + error: {} + }); + } else if (!account.confirmed) { + return next({ + status: 403, + message: Constants.Error.ACCOUNT_403_MESSAGE, + error: {} + }); + } else if (account.accountType !== Constants.General.HACKER) { + return next({ + status: 409, + message: Constants.Error.ACCOUNT_TYPE_409_MESSAGE + }); + } else { + return next(); + } } /** @@ -144,8 +144,8 @@ async function validateConfirmedStatus(account, next) { * @param {(err?) => void} next */ async function validateConfirmedStatusFromAccountId(req, res, next) { - const account = await Services.Account.findById(req.body.accountId); - return validateConfirmedStatus(account, next); + const account = await Services.Account.findById(req.body.accountId); + return validateConfirmedStatus(account, next); } /** @@ -155,9 +155,9 @@ async function validateConfirmedStatusFromAccountId(req, res, next) { * @param {(err?) => void} next */ async function validateConfirmedStatusFromHackerId(req, res, next) { - const hacker = await Services.Hacker.findById(req.params.id); - const account = await Services.Account.findById(hacker.accountId); - return validateConfirmedStatus(account, next); + const hacker = await Services.Hacker.findById(req.params.id); + const account = await Services.Account.findById(hacker.accountId); + return validateConfirmedStatus(account, next); } /** @@ -167,7 +167,7 @@ async function validateConfirmedStatusFromHackerId(req, res, next) { * @param {(err?) => void} next */ async function validateConfirmedStatusFromObject(req, res, next) { - return validateConfirmedStatus(req.body.account, next); + return validateConfirmedStatus(req.body.account, next); } /** @@ -178,39 +178,39 @@ async function validateConfirmedStatusFromObject(req, res, next) { * @description Retrieves a hacker's information via req.body.id, moving result to req.body.hacker if succesful. */ async function findById(req, res, next) { - const hacker = await Services.Hacker.findById(req.body.id); + const hacker = await Services.Hacker.findById(req.body.id); - if (!hacker) { - return next({ - status: 404, - message: Constants.Error.HACKER_404_MESSAGE - }); - } + if (!hacker) { + return next({ + status: 404, + message: Constants.Error.HACKER_404_MESSAGE + }); + } - req.body.hacker = hacker; - next(); + req.body.hacker = hacker; + next(); } async function findByEmail(req, res, next) { - const account = await Services.Account.findByEmail(req.body.email); - if (!account) { - return next({ - status: 404, - message: Constants.Error.ACCOUNT_404_MESSAGE, - error: {} - }); - } - const hacker = await Services.Hacker.findByAccountId(account._id); - if (!hacker) { - return next({ - status: 404, - message: Constants.Error.HACKER_404_MESSAGE, - error: {} - }); - } + const account = await Services.Account.findByEmail(req.body.email); + if (!account) { + return next({ + status: 404, + message: Constants.Error.ACCOUNT_404_MESSAGE, + error: {} + }); + } + const hacker = await Services.Hacker.findByAccountId(account._id); + if (!hacker) { + return next({ + status: 404, + message: Constants.Error.HACKER_404_MESSAGE, + error: {} + }); + } - req.body.hacker = hacker; - next(); + req.body.hacker = hacker; + next(); } /** @@ -221,24 +221,25 @@ async function findByEmail(req, res, next) { */ // must check that the account id is in the hacker schema. function ensureAccountLinkedToHacker(req, res, next) { - Services.Hacker.findById(req.body.id) - .then(hacker => { - req.hacker = hacker; - if ( - hacker && - req.user && - String.toString(hacker.accountId) === String.toString(req.user.id) - ) { - return next(); - } else { - return next({ - status: 403, - message: Constants.Error.AUTH_403_MESSAGE, - error: {} - }); - } - }) - .catch(next); + Services.Hacker.findById(req.body.id) + .then((hacker) => { + req.hacker = hacker; + if ( + hacker && + req.user && + String.toString(hacker.accountId) === + String.toString(req.user.id) + ) { + return next(); + } else { + return next({ + status: 403, + message: Constants.Error.AUTH_403_MESSAGE, + error: {} + }); + } + }) + .catch(next); } /** @@ -248,15 +249,15 @@ function ensureAccountLinkedToHacker(req, res, next) { * @param {(err?)=>void} next */ async function uploadResume(req, res, next) { - const gcfilename = `resumes/${Date.now()}-${req.hacker.id}`; - await Services.Storage.upload(req.file, gcfilename); - req.body.gcfilename = gcfilename; - await Services.Hacker.updateOne(req.hacker.id, { - $set: { - "application.general.URL.resume": gcfilename - } - }); - return next(); + const gcfilename = `resumes/${Date.now()}-${req.hacker.id}`; + await Services.Storage.upload(req.file, gcfilename); + req.body.gcfilename = gcfilename; + await Services.Hacker.updateOne(req.hacker.id, { + $set: { + "application.general.URL.resume": gcfilename + } + }); + return next(); } /** @@ -266,25 +267,25 @@ async function uploadResume(req, res, next) { * @param {(err?)=>void} next */ async function downloadResume(req, res, next) { - const hacker = await Services.Hacker.findById(req.body.id); - if ( - hacker && - hacker.application && - hacker.application.general && - hacker.application.general.URL && - hacker.application.general.URL.resume - ) { - req.body.resume = await Services.Storage.download( - hacker.application.general.URL.resume - ); - } else { - return next({ - status: 404, - message: Constants.Error.RESUME_404_MESSAGE, - error: {} - }); - } - return next(); + const hacker = await Services.Hacker.findById(req.body.id); + if ( + hacker && + hacker.application && + hacker.application.general && + hacker.application.general.URL && + hacker.application.general.URL.resume + ) { + req.body.resume = await Services.Storage.download( + hacker.application.general.URL.resume + ); + } else { + return next({ + status: 404, + message: Constants.Error.RESUME_404_MESSAGE, + error: {} + }); + } + return next(); } /** * Sends a preset email to a user if a status change occured. @@ -293,31 +294,31 @@ async function downloadResume(req, res, next) { * @param {(err?:*)=>void} next */ async function sendStatusUpdateEmail(req, res, next) { - //skip if the status doesn't exist - if (!req.body.status) { - return next(); - } else { - // send it to the hacker that is being updated. - const hacker = await Services.Hacker.findById(req.params.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 - }); + //skip if the status doesn't exist + if (!req.body.status) { + return next(); + } else { + // send it to the hacker that is being updated. + const hacker = await Services.Hacker.findById(req.params.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.status, + next + ); } - Services.Email.sendStatusUpdate( - account.firstName, - account.email, - req.body.status, - next - ); - } } /** @@ -327,21 +328,21 @@ async function sendStatusUpdateEmail(req, res, next) { * @param {(err?:*)=>void} next */ async function sendAppliedStatusEmail(req, res, next) { - const hacker = req.body.hacker; - const account = await Services.Account.findById(hacker.accountId); - if (!account) { - return next({ - status: 500, - message: Constants.Error.GENERIC_500_MESSAGE, - error: {} - }); - } - Services.Email.sendStatusUpdate( - account.firstName, - account.email, - Constants.General.HACKER_STATUS_APPLIED, - next - ); + const hacker = req.body.hacker; + const account = await Services.Account.findById(hacker.accountId); + if (!account) { + return next({ + status: 500, + message: Constants.Error.GENERIC_500_MESSAGE, + error: {} + }); + } + Services.Email.sendStatusUpdate( + account.firstName, + account.email, + Constants.General.HACKER_STATUS_APPLIED, + next + ); } /** @@ -351,31 +352,33 @@ async function sendAppliedStatusEmail(req, res, next) { * @param {(err?:*)=>void} next */ async function sendWeekOfEmail(req, res, next) { - const hacker = req.body.hacker; - const address = Services.Env.isProduction() - ? process.env.FRONTEND_ADDRESS_DEPLOY - : process.env.FRONTEND_ADDRESS_DEV; - const httpOrHttps = address.includes("localhost") ? "http" : "https"; - const singleHackerViewLink = Services.Hacker.generateHackerViewLink( - httpOrHttps, - address, - hacker._id.toString() - ); - const ticketSVG = await Services.Hacker.generateQRCode(singleHackerViewLink); - const account = await Services.Account.findById(hacker.accountId); - if (!account || !ticketSVG) { - return next({ - status: 500, - message: Constants.Error.GENERIC_500_MESSAGE, - error: {} - }); - } - Services.Email.sendWeekOfEmail( - account.firstName, - account.email, - ticketSVG, - next - ); + const hacker = req.body.hacker; + const address = Services.Env.isProduction() + ? process.env.FRONTEND_ADDRESS_DEPLOY + : process.env.FRONTEND_ADDRESS_DEV; + const httpOrHttps = address.includes("localhost") ? "http" : "https"; + const singleHackerViewLink = Services.Hacker.generateHackerViewLink( + httpOrHttps, + address, + hacker._id.toString() + ); + const ticketSVG = await Services.Hacker.generateQRCode( + singleHackerViewLink + ); + const account = await Services.Account.findById(hacker.accountId); + if (!account || !ticketSVG) { + return next({ + status: 500, + message: Constants.Error.GENERIC_500_MESSAGE, + error: {} + }); + } + Services.Email.sendWeekOfEmail( + account.firstName, + account.email, + ticketSVG, + next + ); } /** @@ -385,16 +388,16 @@ async function sendWeekOfEmail(req, res, next) { * @param {(err?:*)=>void} next */ async function sendDayOfEmail(req, res, next) { - const hacker = req.body.hacker; - const account = await Services.Account.findById(hacker.accountId); - if (!account) { - return next({ - status: 500, - message: Constants.Error.GENERIC_500_MESSAGE, - error: {} - }); - } - Services.Email.sendDayOfEmail(account.firstName, account.email, next); + const hacker = req.body.hacker; + const account = await Services.Account.findById(hacker.accountId); + if (!account) { + return next({ + status: 500, + message: Constants.Error.GENERIC_500_MESSAGE, + error: {} + }); + } + Services.Email.sendDayOfEmail(account.firstName, account.email, next); } /** @@ -406,41 +409,41 @@ async function sendDayOfEmail(req, res, next) { * @param {(err?:*)=>void} next */ async function updateStatusIfApplicationCompleted(req, res, next) { - const hacker = await Services.Hacker.findById(req.params.id); - if (hacker) { - if ( - hacker.status === Constants.General.HACKER_STATUS_NONE && - hacker.isApplicationComplete() - ) { - await Services.Hacker.updateOne(req.params.id, { - status: Constants.General.HACKER_STATUS_APPLIED - }); - const account = await Services.Account.findById(hacker.accountId); - if (!account) { + const hacker = await Services.Hacker.findById(req.params.id); + if (hacker) { + if ( + hacker.status === Constants.General.HACKER_STATUS_NONE && + hacker.isApplicationComplete() + ) { + await Services.Hacker.updateOne(req.params.id, { + status: Constants.General.HACKER_STATUS_APPLIED + }); + const account = await Services.Account.findById(hacker.accountId); + if (!account) { + return next({ + status: 500, + message: Constants.Error.GENERIC_500_MESSAGE, + error: {} + }); + } + Services.Email.sendStatusUpdate( + account.firstName, + account.email, + Constants.General.HACKER_STATUS_APPLIED, + next + ); + } else { + return next(); + } + } else { return next({ - status: 500, - message: Constants.Error.GENERIC_500_MESSAGE, - error: {} + status: 404, + message: Constants.Error.HACKER_404_MESSAGE, + data: { + id: req.params.id + } }); - } - Services.Email.sendStatusUpdate( - account.firstName, - account.email, - Constants.General.HACKER_STATUS_APPLIED, - next - ); - } else { - return next(); } - } else { - return next({ - status: 404, - message: Constants.Error.HACKER_404_MESSAGE, - data: { - id: req.params.id - } - }); - } } /** @@ -449,34 +452,34 @@ async function updateStatusIfApplicationCompleted(req, res, next) { * @returns {(req, res, next) => {}} the middleware that will check hacker's status */ function checkStatus(statuses) { - return Middleware.Util.asyncMiddleware(async (req, res, next) => { - let hacker = await Services.Hacker.findById(req.params.id); - - if (!!hacker) { - const status = hacker.status; - // makes sure the hacker's status is in the accepted statuses list - if (statuses.indexOf(status) === -1) { - return next({ - status: 409, - message: Constants.Error.HACKER_STATUS_409_MESSAGE, - data: { - id: req.params.id, - validStatuses: statuses - } - }); - } - - return next(); - } else { - return next({ - status: 404, - message: Constants.Error.HACKER_404_MESSAGE, - data: { - id: req.params.id + return Middleware.Util.asyncMiddleware(async (req, res, next) => { + let hacker = await Services.Hacker.findById(req.params.id); + + if (!!hacker) { + const status = hacker.status; + // makes sure the hacker's status is in the accepted statuses list + if (statuses.indexOf(status) === -1) { + return next({ + status: 409, + message: Constants.Error.HACKER_STATUS_409_MESSAGE, + data: { + id: req.params.id, + validStatuses: statuses + } + }); + } + + return next(); + } else { + return next({ + status: 404, + message: Constants.Error.HACKER_404_MESSAGE, + data: { + id: req.params.id + } + }); } - }); - } - }); + }); } /** @@ -487,30 +490,30 @@ function checkStatus(statuses) { * @param {*} next */ async function updateHacker(req, res, next) { - const hacker = await Services.Hacker.updateOne(req.params.id, req.body); - 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 + const hacker = await Services.Hacker.updateOne(req.params.id, req.body); + 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 + } + }); } - req.email = acct.email; - return next(); - } else { - return next({ - status: 404, - message: Constants.Error.HACKER_404_MESSAGE, - data: { - id: req.params.id - } - }); - } } /** @@ -523,31 +526,33 @@ async function updateHacker(req, res, next) { * Creates hacker document after making sure there is no other hacker with the same linked accountId */ async function createHacker(req, res, next) { - const hackerDetails = req.body.hackerDetails; + const hackerDetails = req.body.hackerDetails; - const exists = await Services.Hacker.findByAccountId(hackerDetails.accountId); + const exists = await Services.Hacker.findByAccountId( + hackerDetails.accountId + ); - if (exists) { - return next({ - status: 422, - message: Constants.Error.ACCOUNT_DUPLICATE_422_MESSAGE, - data: { - id: hackerDetails.accountId - } - }); - } - const hacker = await Services.Hacker.createHacker(hackerDetails); + if (exists) { + return next({ + status: 422, + message: Constants.Error.ACCOUNT_DUPLICATE_422_MESSAGE, + data: { + id: hackerDetails.accountId + } + }); + } + const hacker = await Services.Hacker.createHacker(hackerDetails); - if (!!hacker) { - req.body.hacker = hacker; - return next(); - } else { - return next({ - status: 500, - message: Constants.Error.HACKER_CREATE_500_MESSAGE, - data: {} - }); - } + if (!!hacker) { + req.body.hacker = hacker; + return next(); + } else { + return next({ + status: 500, + message: Constants.Error.HACKER_CREATE_500_MESSAGE, + data: {} + }); + } } /** @@ -557,18 +562,18 @@ async function createHacker(req, res, next) { * @param {*} next */ async function checkDuplicateAccountLinks(req, res, next) { - const hacker = await Services.Hacker.findByAccountId(req.body.accountId); - if (!hacker) { - return next(); - } else { - return next({ - status: 409, - message: Constants.Error.HACKER_ID_409_MESSAGE, - data: { - id: req.body.accountId - } - }); - } + const hacker = await Services.Hacker.findByAccountId(req.body.accountId); + if (!hacker) { + return next(); + } else { + return next({ + status: 409, + message: Constants.Error.HACKER_ID_409_MESSAGE, + data: { + id: req.body.accountId + } + }); + } } /** @@ -578,73 +583,78 @@ async function checkDuplicateAccountLinks(req, res, next) { * @param {(err?)=>void} next */ async function findSelf(req, res, next) { - if (req.user.accountType != Constants.General.HACKER || !req.user.confirmed) { - return next({ - status: 409, - message: Constants.Error.ACCOUNT_TYPE_409_MESSAGE, - error: { - id: req.user.id - } - }); - } + if ( + req.user.accountType != Constants.General.HACKER || + !req.user.confirmed + ) { + return next({ + status: 409, + message: Constants.Error.ACCOUNT_TYPE_409_MESSAGE, + error: { + id: req.user.id + } + }); + } - const hacker = await Services.Hacker.findByAccountId(req.user.id); + const hacker = await Services.Hacker.findByAccountId(req.user.id); - if (!!hacker) { - req.body.hacker = hacker; - return next(); - } else { - return next({ - status: 409, - message: Constants.Error.HACKER_404_MESSAGE, - error: { - id: req.user.id - } - }); - } + if (!!hacker) { + req.body.hacker = hacker; + return next(); + } else { + return next({ + status: 409, + message: Constants.Error.HACKER_404_MESSAGE, + error: { + id: req.user.id + } + }); + } } async function getStats(req, res, next) { - const stats = await Services.Hacker.getStats(req.body.results); - req.body.stats = stats; - next(); + const stats = await Services.Hacker.getStats(req.body.results); + req.body.stats = stats; + next(); } module.exports = { - parsePatch: parsePatch, - parseHacker: parseHacker, - addDefaultStatus: addDefaultStatus, - ensureAccountLinkedToHacker: ensureAccountLinkedToHacker, - uploadResume: Middleware.Util.asyncMiddleware(uploadResume), - downloadResume: Middleware.Util.asyncMiddleware(downloadResume), - sendWeekOfEmail: Middleware.Util.asyncMiddleware(sendWeekOfEmail), - sendDayOfEmail: Middleware.Util.asyncMiddleware(sendDayOfEmail), - sendStatusUpdateEmail: Middleware.Util.asyncMiddleware(sendStatusUpdateEmail), - sendAppliedStatusEmail: Middleware.Util.asyncMiddleware( - sendAppliedStatusEmail - ), - updateHacker: Middleware.Util.asyncMiddleware(updateHacker), - validateConfirmedStatusFromAccountId: Middleware.Util.asyncMiddleware( - validateConfirmedStatusFromAccountId - ), - validateConfirmedStatusFromHackerId: Middleware.Util.asyncMiddleware( - validateConfirmedStatusFromHackerId - ), - validateConfirmedStatusFromObject: Middleware.Util.asyncMiddleware( - validateConfirmedStatusFromObject - ), - checkDuplicateAccountLinks: Middleware.Util.asyncMiddleware( - checkDuplicateAccountLinks - ), - updateStatusIfApplicationCompleted: Middleware.Util.asyncMiddleware( - updateStatusIfApplicationCompleted - ), - checkStatus: checkStatus, - parseCheckIn: parseCheckIn, - parseConfirmation: parseConfirmation, - createHacker: Middleware.Util.asyncMiddleware(createHacker), - findSelf: Middleware.Util.asyncMiddleware(findSelf), - getStats: Middleware.Util.asyncMiddleware(getStats), - findById: Middleware.Util.asyncMiddleware(findById), - findByEmail: Middleware.Util.asyncMiddleware(findByEmail) + parsePatch: parsePatch, + parseHacker: parseHacker, + addDefaultStatus: addDefaultStatus, + ensureAccountLinkedToHacker: ensureAccountLinkedToHacker, + uploadResume: Middleware.Util.asyncMiddleware(uploadResume), + downloadResume: Middleware.Util.asyncMiddleware(downloadResume), + sendWeekOfEmail: Middleware.Util.asyncMiddleware(sendWeekOfEmail), + sendDayOfEmail: Middleware.Util.asyncMiddleware(sendDayOfEmail), + sendStatusUpdateEmail: Middleware.Util.asyncMiddleware( + sendStatusUpdateEmail + ), + sendAppliedStatusEmail: Middleware.Util.asyncMiddleware( + sendAppliedStatusEmail + ), + updateHacker: Middleware.Util.asyncMiddleware(updateHacker), + validateConfirmedStatusFromAccountId: Middleware.Util.asyncMiddleware( + validateConfirmedStatusFromAccountId + ), + validateConfirmedStatusFromHackerId: Middleware.Util.asyncMiddleware( + validateConfirmedStatusFromHackerId + ), + validateConfirmedStatusFromObject: Middleware.Util.asyncMiddleware( + validateConfirmedStatusFromObject + ), + checkDuplicateAccountLinks: Middleware.Util.asyncMiddleware( + checkDuplicateAccountLinks + ), + updateStatusIfApplicationCompleted: Middleware.Util.asyncMiddleware( + updateStatusIfApplicationCompleted + ), + checkStatus: checkStatus, + parseCheckIn: parseCheckIn, + parseConfirmation: parseConfirmation, + createHacker: Middleware.Util.asyncMiddleware(createHacker), + findSelf: Middleware.Util.asyncMiddleware(findSelf), + getStats: Middleware.Util.asyncMiddleware(getStats), + findById: Middleware.Util.asyncMiddleware(findById), + findByEmail: Middleware.Util.asyncMiddleware(findByEmail) }; diff --git a/middlewares/parse-body.middleware.js b/middlewares/parse-body.middleware.js index 8f3e4e9d..b24f1155 100644 --- a/middlewares/parse-body.middleware.js +++ b/middlewares/parse-body.middleware.js @@ -2,11 +2,11 @@ const { validationResult } = require("express-validator/check"); const { matchedData } = require("express-validator/filter"); const Constants = { - Error: require("../constants/error.constant") + Error: require("../constants/error.constant") }; module.exports = { - middleware: middleware + middleware: middleware }; /** @@ -16,14 +16,14 @@ module.exports = { * @param {(err?)=>void} next */ function middleware(req, res, next) { - const errors = validationResult(req); - if (!errors.isEmpty()) { - return next({ - status: 422, - message: Constants.Error.VALIDATION_422_MESSAGE, - data: errors.mapped() - }); - } - req.body = matchedData(req); - return next(); + const errors = validationResult(req); + if (!errors.isEmpty()) { + return next({ + status: 422, + message: Constants.Error.VALIDATION_422_MESSAGE, + data: errors.mapped() + }); + } + req.body = matchedData(req); + return next(); } diff --git a/package-lock.json b/package-lock.json index c09d778f..4a882b83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", "dev": true, "requires": { - "@babel/highlight": "7.5.0" + "@babel/highlight": "^7.0.0" } }, "@babel/highlight": { @@ -19,9 +19,9 @@ "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", "dev": true, "requires": { - "chalk": "2.4.2", - "esutils": "2.0.3", - "js-tokens": "4.0.0" + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" }, "dependencies": { "ansi-styles": { @@ -30,7 +30,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -39,9 +39,9 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } } } @@ -51,17 +51,17 @@ "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.31.1.tgz", "integrity": "sha512-MgaF8VmDaoyIqzZUXIbcohTb5sQn+PYlYmcpb0/E8psUpVe+kaBwLq/Z8pcFtACCr6PNT36n+a6s1kG35bAuCA==", "requires": { - "@google-cloud/projectify": "0.3.3", - "@google-cloud/promisify": "0.4.0", - "@types/duplexify": "3.6.0", - "@types/request": "2.47.1", - "arrify": "1.0.1", - "duplexify": "3.6.0", - "ent": "2.2.0", - "extend": "3.0.2", - "google-auth-library": "3.1.0", - "pify": "4.0.1", - "retry-request": "4.0.0" + "@google-cloud/projectify": "^0.3.2", + "@google-cloud/promisify": "^0.4.0", + "@types/duplexify": "^3.5.0", + "@types/request": "^2.47.0", + "arrify": "^1.0.1", + "duplexify": "^3.6.0", + "ent": "^2.2.0", + "extend": "^3.0.1", + "google-auth-library": "^3.0.0", + "pify": "^4.0.0", + "retry-request": "^4.0.0" }, "dependencies": { "pify": { @@ -76,16 +76,16 @@ "resolved": "http://registry.npmjs.org/@google-cloud/common-grpc/-/common-grpc-0.10.1.tgz", "integrity": "sha512-oV5mKRqPAqamPcjj8S61UKyB5cz2ugA0/9MPXUfu0CoYaG6AnhnrbPhHDOW6fQ4eci27ER5iakR00htvM9C+Xg==", "requires": { - "@google-cloud/common": "0.31.1", - "@google-cloud/projectify": "0.3.3", - "@google-cloud/promisify": "0.4.0", - "@grpc/proto-loader": "0.4.0", - "duplexify": "4.0.0", - "extend": "3.0.2", - "grpc": "1.19.0", - "is": "3.3.0", - "retry-request": "4.0.0", - "through2": "3.0.1" + "@google-cloud/common": "^0.31.0", + "@google-cloud/projectify": "^0.3.0", + "@google-cloud/promisify": "^0.4.0", + "@grpc/proto-loader": "^0.4.0", + "duplexify": "^4.0.0", + "extend": "^3.0.2", + "grpc": "^1.15.1", + "is": "^3.2.1", + "retry-request": "^4.0.0", + "through2": "^3.0.0" }, "dependencies": { "duplexify": { @@ -93,10 +93,10 @@ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.0.0.tgz", "integrity": "sha512-yY3mlX6uXXe53lt9TnyIIlPZD9WfBEl+OU/8YLiU+p0xxaNRMjLE+rIEURR5/F1H41z9iMHcmVRxRS89tKCUcQ==", "requires": { - "end-of-stream": "1.4.1", - "inherits": "2.0.3", - "readable-stream": "3.3.0", - "stream-shift": "1.0.0" + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" } }, "readable-stream": { @@ -104,9 +104,9 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", "requires": { - "inherits": "2.0.3", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, "through2": { @@ -114,7 +114,7 @@ "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", "requires": { - "readable-stream": "3.3.0" + "readable-stream": "2 || 3" } } } @@ -124,26 +124,26 @@ "resolved": "http://registry.npmjs.org/@google-cloud/logging/-/logging-4.5.1.tgz", "integrity": "sha512-x6HUOGg78YL1XeFVUwUoVQPI5CU40hXjzu0crjLZZmMsqr4pX285BafNP5TLh45rSa6JJME7EVFWU2+duW3Qgw==", "requires": { - "@google-cloud/common-grpc": "0.10.1", - "@google-cloud/paginator": "0.2.0", - "@google-cloud/projectify": "0.3.3", - "@google-cloud/promisify": "0.4.0", + "@google-cloud/common-grpc": "^0.10.0", + "@google-cloud/paginator": "^0.2.0", + "@google-cloud/projectify": "^0.3.0", + "@google-cloud/promisify": "^0.4.0", "@opencensus/propagation-stackdriver": "0.0.9", - "arrify": "1.0.1", - "eventid": "0.1.2", - "extend": "3.0.2", - "gcp-metadata": "1.0.0", - "google-auth-library": "3.1.0", - "google-gax": "0.25.6", - "is": "3.3.0", - "lodash.merge": "4.6.2", - "on-finished": "2.3.0", - "pify": "4.0.1", - "protobufjs": "6.8.8", - "pumpify": "1.5.1", - "snakecase-keys": "2.1.0", - "stream-events": "1.0.4", - "through2": "3.0.1" + "arrify": "^1.0.1", + "eventid": "^0.1.2", + "extend": "^3.0.2", + "gcp-metadata": "^1.0.0", + "google-auth-library": "^3.0.0", + "google-gax": "^0.25.0", + "is": "^3.2.1", + "lodash.merge": "^4.6.1", + "on-finished": "^2.3.0", + "pify": "^4.0.1", + "protobufjs": "^6.8.8", + "pumpify": "^1.5.1", + "snakecase-keys": "^2.0.0", + "stream-events": "^1.0.4", + "through2": "^3.0.0" }, "dependencies": { "gcp-metadata": { @@ -151,8 +151,8 @@ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-1.0.0.tgz", "integrity": "sha512-Q6HrgfrCQeEircnNP3rCcEgiDv7eF9+1B+1MMgpE190+/+0mjQR8PxeOaRgxZWmdDAF9EIryHB9g1moPiw1SbQ==", "requires": { - "gaxios": "1.4.0", - "json-bigint": "0.3.0" + "gaxios": "^1.0.2", + "json-bigint": "^0.3.0" } }, "pify": { @@ -165,7 +165,7 @@ "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", "requires": { - "readable-stream": "2.3.6" + "readable-stream": "2 || 3" } } } @@ -175,12 +175,12 @@ "resolved": "https://registry.npmjs.org/@google-cloud/logging-winston/-/logging-winston-0.11.1.tgz", "integrity": "sha512-exTIriar6Ixuk65nveMlOoFRyTfgXltW7zc+VBSa7J1cD4X+8SHas/On3YtwvSzaynVHZAZX9ATelT5leitTyQ==", "requires": { - "@google-cloud/logging": "4.5.1", - "google-auth-library": "3.1.0", - "lodash.mapvalues": "4.6.0", - "logform": "2.1.2", - "semver": "6.0.0", - "winston-transport": "4.3.0" + "@google-cloud/logging": "^4.4.0", + "google-auth-library": "^3.1.0", + "lodash.mapvalues": "^4.6.0", + "logform": "^2.0.0", + "semver": "^6.0.0", + "winston-transport": "^4.2.0" }, "dependencies": { "colors": { @@ -193,11 +193,11 @@ "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", "requires": { - "colors": "1.3.3", - "fast-safe-stringify": "2.0.6", - "fecha": "2.3.3", - "ms": "2.1.1", - "triple-beam": "1.3.0" + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^2.3.3", + "ms": "^2.1.1", + "triple-beam": "^1.3.0" } }, "ms": { @@ -217,10 +217,10 @@ "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-0.2.0.tgz", "integrity": "sha512-2ZSARojHDhkLvQ+CS32K+iUhBsWg3AEw+uxtqblA7xoCABDyhpj99FPp35xy6A+XlzMhOSrHHaxFE+t6ZTQq0w==", "requires": { - "arrify": "1.0.1", - "extend": "3.0.2", - "split-array-stream": "2.0.0", - "stream-events": "1.0.4" + "arrify": "^1.0.1", + "extend": "^3.0.1", + "split-array-stream": "^2.0.0", + "stream-events": "^1.0.4" } }, "@google-cloud/projectify": { @@ -238,28 +238,28 @@ "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-3.5.0.tgz", "integrity": "sha512-QxJ/zft4Kxbedpu7MQ5ZsNeS5WbonB7H28T32R4hQO2ply/j6n7bXmd5Vz0kzJu/iub20sK/ibgxYoxrgZD6CQ==", "requires": { - "@google-cloud/common": "2.2.2", - "@google-cloud/paginator": "2.0.1", - "@google-cloud/promisify": "1.0.2", - "arrify": "2.0.1", - "compressible": "2.0.17", - "concat-stream": "2.0.0", - "date-and-time": "0.10.0", - "duplexify": "3.6.0", - "extend": "3.0.2", - "gaxios": "2.0.1", - "gcs-resumable-upload": "2.3.0", - "hash-stream-validation": "0.2.2", - "mime": "2.4.4", - "mime-types": "2.1.18", - "onetime": "5.1.0", - "p-limit": "2.2.1", - "pumpify": "2.0.1", - "readable-stream": "3.4.0", - "snakeize": "0.1.0", - "stream-events": "1.0.4", - "through2": "3.0.1", - "xdg-basedir": "4.0.0" + "@google-cloud/common": "^2.1.1", + "@google-cloud/paginator": "^2.0.0", + "@google-cloud/promisify": "^1.0.0", + "arrify": "^2.0.0", + "compressible": "^2.0.12", + "concat-stream": "^2.0.0", + "date-and-time": "^0.10.0", + "duplexify": "^3.5.0", + "extend": "^3.0.2", + "gaxios": "^2.0.1", + "gcs-resumable-upload": "^2.2.4", + "hash-stream-validation": "^0.2.1", + "mime": "^2.2.0", + "mime-types": "^2.0.8", + "onetime": "^5.1.0", + "p-limit": "^2.2.0", + "pumpify": "^2.0.0", + "readable-stream": "^3.4.0", + "snakeize": "^0.1.0", + "stream-events": "^1.0.1", + "through2": "^3.0.0", + "xdg-basedir": "^4.0.0" }, "dependencies": { "@google-cloud/common": { @@ -267,15 +267,15 @@ "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-2.2.2.tgz", "integrity": "sha512-AgMdDgLeYlEG17tXtMCowE7mplm907pcugtfJYYAp06HNe9RDnunUIY5KMnn9yikYl7NXNofARC+hwG77Zsa4g==", "requires": { - "@google-cloud/projectify": "1.0.1", - "@google-cloud/promisify": "1.0.2", - "arrify": "2.0.1", - "duplexify": "3.6.0", - "ent": "2.2.0", - "extend": "3.0.2", - "google-auth-library": "5.5.0", - "retry-request": "4.0.0", - "teeny-request": "5.3.0" + "@google-cloud/projectify": "^1.0.0", + "@google-cloud/promisify": "^1.0.0", + "arrify": "^2.0.0", + "duplexify": "^3.6.0", + "ent": "^2.2.0", + "extend": "^3.0.2", + "google-auth-library": "^5.0.0", + "retry-request": "^4.0.0", + "teeny-request": "^5.2.1" } }, "@google-cloud/paginator": { @@ -283,8 +283,8 @@ "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-2.0.1.tgz", "integrity": "sha512-HZ6UTGY/gHGNriD7OCikYWL/Eu0sTEur2qqse2w6OVsz+57se3nTkqH14JIPxtf0vlEJ8IJN5w3BdZ22pjCB8g==", "requires": { - "arrify": "2.0.1", - "extend": "3.0.2" + "arrify": "^2.0.0", + "extend": "^3.0.2" } }, "@google-cloud/projectify": { @@ -307,10 +307,10 @@ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", "requires": { - "buffer-from": "1.0.0", - "inherits": "2.0.3", - "readable-stream": "3.4.0", - "typedarray": "0.0.6" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" } }, "gaxios": { @@ -318,10 +318,10 @@ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.0.1.tgz", "integrity": "sha512-c1NXovTxkgRJTIgB2FrFmOFg4YIV6N/bAa4f/FZ4jIw13Ql9ya/82x69CswvotJhbV3DiGnlTZwoq2NVXk2Irg==", "requires": { - "abort-controller": "3.0.0", - "extend": "3.0.2", - "https-proxy-agent": "2.2.1", - "node-fetch": "2.3.0" + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^2.2.1", + "node-fetch": "^2.3.0" } }, "gcp-metadata": { @@ -329,8 +329,8 @@ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.2.0.tgz", "integrity": "sha512-ympv+yQ6k5QuWCuwQqnGEvFGS7MBKdcQdj1i188v3bW9QLFIchTGaBCEZxSQapT0jffdn1vdt8oJhB5VBWQO1Q==", "requires": { - "gaxios": "2.0.1", - "json-bigint": "0.3.0" + "gaxios": "^2.0.1", + "json-bigint": "^0.3.0" } }, "google-auth-library": { @@ -338,14 +338,14 @@ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.5.0.tgz", "integrity": "sha512-TNeraw4miu6/FhO0jDrHiJuRx3SBrAhxHSPj7+rhif43bKE34UItXX9lejKxo3E8FNytuY4LIVIvpcqMQHSYZw==", "requires": { - "arrify": "2.0.1", - "base64-js": "1.3.0", - "fast-text-encoding": "1.0.0", - "gaxios": "2.0.1", - "gcp-metadata": "3.2.0", - "gtoken": "4.1.0", - "jws": "3.1.5", - "lru-cache": "5.1.1" + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "fast-text-encoding": "^1.0.0", + "gaxios": "^2.0.0", + "gcp-metadata": "^3.2.0", + "gtoken": "^4.1.0", + "jws": "^3.1.5", + "lru-cache": "^5.0.0" } }, "google-p12-pem": { @@ -353,7 +353,7 @@ "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.2.tgz", "integrity": "sha512-UfnEARfJKI6pbmC1hfFFm+UAcZxeIwTiEcHfqKe/drMsXD/ilnVjF7zgOGpHXyhuvX6jNJK3S8A0hOQjwtFxEw==", "requires": { - "node-forge": "0.9.1" + "node-forge": "^0.9.0" } }, "gtoken": { @@ -361,10 +361,10 @@ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.0.tgz", "integrity": "sha512-wqyn2gf5buzEZN4QNmmiiW2i2JkEdZnL7Z/9p44RtZqgt4077m4khRgAYNuu8cBwHWCc6MsP6eDUn/KkF6jFIw==", "requires": { - "gaxios": "2.0.1", - "google-p12-pem": "2.0.2", - "jws": "3.1.5", - "mime": "2.4.4" + "gaxios": "^2.0.0", + "google-p12-pem": "^2.0.0", + "jws": "^3.1.5", + "mime": "^2.2.0" } }, "lru-cache": { @@ -372,7 +372,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "requires": { - "yallist": "3.1.1" + "yallist": "^3.0.2" } }, "mime": { @@ -390,7 +390,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "requires": { - "p-try": "2.0.0" + "p-try": "^2.0.0" } }, "pump": { @@ -398,8 +398,8 @@ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, "pumpify": { @@ -407,9 +407,9 @@ "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", "requires": { - "duplexify": "4.1.1", - "inherits": "2.0.3", - "pump": "3.0.0" + "duplexify": "^4.1.1", + "inherits": "^2.0.3", + "pump": "^3.0.0" }, "dependencies": { "duplexify": { @@ -417,10 +417,10 @@ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", "requires": { - "end-of-stream": "1.4.1", - "inherits": "2.0.3", - "readable-stream": "3.4.0", - "stream-shift": "1.0.0" + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" } } } @@ -430,9 +430,9 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", "requires": { - "inherits": "2.0.3", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, "through2": { @@ -440,7 +440,7 @@ "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", "requires": { - "readable-stream": "3.4.0" + "readable-stream": "2 || 3" } }, "xdg-basedir": { @@ -460,7 +460,7 @@ "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-0.3.6.tgz", "integrity": "sha512-SmLNuPGlUur64bNS9aHZguqWDVQ8+Df1CGn+xsh7l6T2wiP5ArOMlywZ3TZo6z/rwKtGQgUJY9ZrPYUmHEXd/Q==", "requires": { - "semver": "5.5.0" + "semver": "^5.5.0" } }, "@grpc/proto-loader": { @@ -468,8 +468,8 @@ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.4.0.tgz", "integrity": "sha512-Jm6o+75uWT7E6+lt8edg4J1F/9+BedOjaMgwE14pxS/AO43/0ZqK+rCLVVrXLoExwSAZvgvOD2B0ivy3Spsspw==", "requires": { - "lodash.camelcase": "4.3.0", - "protobufjs": "6.8.8" + "lodash.camelcase": "^4.3.0", + "protobufjs": "^6.8.6" } }, "@opencensus/propagation-stackdriver": { @@ -477,8 +477,8 @@ "resolved": "https://registry.npmjs.org/@opencensus/propagation-stackdriver/-/propagation-stackdriver-0.0.9.tgz", "integrity": "sha512-yOD0MYTVfvdh9mMJB7tSLxIuOvoL9z28kDojm96pE50DI6PGlL4O3icsiwQbOIJ74h8Vs3ESxspuxwXkegsSEw==", "requires": { - "hex2dec": "1.1.2", - "uuid": "3.2.1" + "hex2dec": "^1.0.1", + "uuid": "^3.2.1" } }, "@protobufjs/aspromise": { @@ -506,8 +506,8 @@ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", "requires": { - "@protobufjs/aspromise": "1.1.2", - "@protobufjs/inquire": "1.1.0" + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" } }, "@protobufjs/float": { @@ -540,9 +540,9 @@ "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-6.4.0.tgz", "integrity": "sha512-GcO+hKXMQiwN0xMGfPITArlj4Nab1vZsrsRLmsJlcXGZV1V1zQC6XuAWJv6MGDd0hr/jKaXmCJ1XMYkxIRQHFw==", "requires": { - "@sendgrid/helpers": "6.4.0", - "@types/request": "2.47.1", - "request": "2.88.0" + "@sendgrid/helpers": "^6.4.0", + "@types/request": "^2.0.3", + "request": "^2.88.0" } }, "@sendgrid/helpers": { @@ -550,8 +550,8 @@ "resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-6.4.0.tgz", "integrity": "sha512-1dDDXauArHyxwTKFFfWvQpsijmwalyLgwoQJ3FRCssFq1RfqYDgFhRg0Xs3v/IXS2jkKWePSWiPORSR4Sysdpw==", "requires": { - "chalk": "2.4.2", - "deepmerge": "2.2.1" + "chalk": "^2.0.1", + "deepmerge": "^2.1.1" }, "dependencies": { "ansi-styles": { @@ -559,7 +559,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -567,9 +567,9 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } } } @@ -579,8 +579,8 @@ "resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-6.4.0.tgz", "integrity": "sha512-pVzbqbxhZ4FUN6iSIksRLtyXRPurrcee1i0noPDStDCLlHVwUR+TofeeKIFWGpIvbbk5UR6S6iV/U5ie8Kdblw==", "requires": { - "@sendgrid/client": "6.4.0", - "@sendgrid/helpers": "6.4.0" + "@sendgrid/client": "^6.4.0", + "@sendgrid/helpers": "^6.4.0" } }, "@types/body-parser": { @@ -589,8 +589,8 @@ "integrity": "sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==", "dev": true, "requires": { - "@types/connect": "3.4.32", - "@types/node": "8.10.10" + "@types/connect": "*", + "@types/node": "*" } }, "@types/bson": { @@ -599,7 +599,7 @@ "integrity": "sha512-pq/rqJwJWkbS10crsG5bgnrisL8pML79KlMKQMoQwLUjlPAkrUHMvHJ3oGwE7WHR61Lv/nadMwXVAD2b+fpD8Q==", "dev": true, "requires": { - "@types/node": "8.10.10" + "@types/node": "*" } }, "@types/caseless": { @@ -619,7 +619,7 @@ "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", "dev": true, "requires": { - "@types/node": "8.10.10" + "@types/node": "*" } }, "@types/cookiejar": { @@ -633,7 +633,7 @@ "resolved": "https://registry.npmjs.org/@types/duplexify/-/duplexify-3.6.0.tgz", "integrity": "sha512-5zOA53RUlzN74bvrSGwjudssD9F3a797sDZQkiYpUOxW+WHaXTCPz4/d5Dgi6FKnOqZ2CpaTo0DhgIfsXAOE/A==", "requires": { - "@types/node": "8.10.10" + "@types/node": "*" } }, "@types/express": { @@ -642,9 +642,9 @@ "integrity": "sha512-VfH/XCP0QbQk5B5puLqTLEeFgR8lfCJHZJKkInZ9mkYd+u8byX0kztXEQxEk4wZXJs8HI+7km2ALXjn4YKcX9w==", "dev": true, "requires": { - "@types/body-parser": "1.17.1", - "@types/express-serve-static-core": "4.16.9", - "@types/serve-static": "1.13.3" + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" } }, "@types/express-serve-static-core": { @@ -653,8 +653,8 @@ "integrity": "sha512-GqpaVWR0DM8FnRUJYKlWgyARoBUAVfRIeVDZQKOttLFp5SmhhF9YFIYeTPwMd/AXfxlP7xVO2dj1fGu0Q+krKQ==", "dev": true, "requires": { - "@types/node": "8.10.10", - "@types/range-parser": "1.2.3" + "@types/node": "*", + "@types/range-parser": "*" } }, "@types/form-data": { @@ -662,7 +662,7 @@ "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", "requires": { - "@types/node": "8.10.10" + "@types/node": "*" } }, "@types/google-cloud__storage": { @@ -671,8 +671,8 @@ "integrity": "sha512-RaQJ7+Ht20MRYJu7mgKBpbVNZIPneztKIl/DUKacRC6A8mXRsJfgDdPA7indHmJGIgm+hzUTj44+A3RyuuYZhg==", "dev": true, "requires": { - "@types/node": "8.10.10", - "@types/request": "2.47.1" + "@types/node": "*", + "@types/request": "*" } }, "@types/long": { @@ -692,8 +692,8 @@ "integrity": "sha512-Va7o1fN3zeabmIJSQ6yuAWkqPvrT38HSTIi4YbVOb2UL7FJ4diXrWt+OUuuEFWAVPtF9VZV5h+7LDYdzgXWgQA==", "dev": true, "requires": { - "@types/bson": "4.0.0", - "@types/node": "8.10.10" + "@types/bson": "*", + "@types/node": "*" } }, "@types/mongoose": { @@ -702,8 +702,8 @@ "integrity": "sha512-vQFa53WOqDmsQzzPGcOQE5F64RLJMBvHJoYdmQ6ksQdbbd5H1qssxeQArSnII45jbIvOPCOCNo+rdp5U/NPtkA==", "dev": true, "requires": { - "@types/mongodb": "3.3.1", - "@types/node": "8.10.10" + "@types/mongodb": "*", + "@types/node": "*" } }, "@types/multer": { @@ -712,7 +712,7 @@ "integrity": "sha512-3hECfz+W0ix/LvPanp87mjO3kOyDnJYTpY9y7gdBxXnYXqEcj21pD0lW7KEUFFr8CHrDF5Mhh7o241KLEXDRoQ==", "dev": true, "requires": { - "@types/express": "4.17.1" + "@types/express": "*" } }, "@types/node": { @@ -731,10 +731,10 @@ "resolved": "https://registry.npmjs.org/@types/request/-/request-2.47.1.tgz", "integrity": "sha512-TV3XLvDjQbIeVxJ1Z3oCTDk/KuYwwcNKVwz2YaT0F5u86Prgc4syDAp6P96rkTQQ4bIdh+VswQIC9zS6NjY7/g==", "requires": { - "@types/caseless": "0.12.1", - "@types/form-data": "2.2.1", - "@types/node": "8.10.10", - "@types/tough-cookie": "2.3.3" + "@types/caseless": "*", + "@types/form-data": "*", + "@types/node": "*", + "@types/tough-cookie": "*" } }, "@types/serve-static": { @@ -743,8 +743,8 @@ "integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==", "dev": true, "requires": { - "@types/express-serve-static-core": "4.16.9", - "@types/mime": "2.0.1" + "@types/express-serve-static-core": "*", + "@types/mime": "*" } }, "@types/superagent": { @@ -753,8 +753,8 @@ "integrity": "sha512-9KhCkyXv268A2nZ1Wvu7rQWM+BmdYUVkycFeNnYrUL5Zwu7o8wPQ3wBfW59dDP+wuoxw0ww8YKgTNv8j/cgscA==", "dev": true, "requires": { - "@types/cookiejar": "2.1.1", - "@types/node": "8.10.10" + "@types/cookiejar": "*", + "@types/node": "*" } }, "@types/tough-cookie": { @@ -772,7 +772,7 @@ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "requires": { - "event-target-shim": "5.0.1" + "event-target-shim": "^5.0.0" } }, "accepts": { @@ -780,7 +780,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "requires": { - "mime-types": "2.1.24", + "mime-types": "~2.1.24", "negotiator": "0.6.2" }, "dependencies": { @@ -816,7 +816,7 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", "requires": { - "es6-promisify": "5.0.0" + "es6-promisify": "^5.0.0" } }, "ajv": { @@ -824,10 +824,10 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", "requires": { - "fast-deep-equal": "2.0.1", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.4.1", - "uri-js": "4.2.2" + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, "ansi-align": { @@ -836,7 +836,7 @@ "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", "dev": true, "requires": { - "string-width": "2.1.1" + "string-width": "^2.0.0" }, "dependencies": { "ansi-regex": { @@ -857,8 +857,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { @@ -867,7 +867,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -884,7 +884,7 @@ "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", "dev": true, "requires": { - "type-fest": "0.8.1" + "type-fest": "^0.8.1" } }, "ansi-regex": { @@ -903,8 +903,8 @@ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { - "micromatch": "3.1.10", - "normalize-path": "2.1.1" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" }, "dependencies": { "normalize-path": { @@ -913,7 +913,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "1.1.0" + "remove-trailing-separator": "^1.0.1" } } } @@ -924,12 +924,12 @@ "integrity": "sha512-9Wf4bRPwCuWOIOxR42dDnsXnFw+rhJg5VrMQK+KmNxJwyIh30UqX6gvjjXSG6YO74MqE87F18bbQXUENK9dPGg==", "dev": true, "requires": { - "apidoc-core": "0.8.3", - "commander": "2.19.0", - "fs-extra": "7.0.1", - "lodash": "4.17.14", - "markdown-it": "8.4.2", - "winston": "3.1.0" + "apidoc-core": "~0.8.2", + "commander": "^2.19.0", + "fs-extra": "^7.0.0", + "lodash": "^4.17.10", + "markdown-it": "^8.3.1", + "winston": "^3.0.0" }, "dependencies": { "commander": { @@ -944,15 +944,15 @@ "integrity": "sha512-FsQfEE+8YIEeuZEYhHDk5cILo1HOcWkGwvoidLrDgPog0r4bser1lEIOco2dN9zpDJ1M88hfDgZvxe5z4xNcwg==", "dev": true, "requires": { - "async": "2.6.1", - "diagnostics": "1.1.1", - "is-stream": "1.1.0", - "logform": "1.10.0", + "async": "^2.6.0", + "diagnostics": "^1.1.1", + "is-stream": "^1.1.0", + "logform": "^1.9.1", "one-time": "0.0.4", - "readable-stream": "2.3.6", - "stack-trace": "0.0.10", - "triple-beam": "1.3.0", - "winston-transport": "4.3.0" + "readable-stream": "^2.3.6", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.2.0" } } } @@ -963,12 +963,12 @@ "integrity": "sha1-2dY1RYKd8lDSzKBJaDqH53U2S5Y=", "dev": true, "requires": { - "fs-extra": "3.0.1", - "glob": "7.1.2", - "iconv-lite": "0.4.19", - "klaw-sync": "2.1.0", - "lodash": "4.17.14", - "semver": "5.3.0" + "fs-extra": "^3.0.1", + "glob": "^7.1.1", + "iconv-lite": "^0.4.17", + "klaw-sync": "^2.1.0", + "lodash": "~4.17.4", + "semver": "~5.3.0" }, "dependencies": { "fs-extra": { @@ -977,9 +977,9 @@ "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "3.0.1", - "universalify": "0.1.2" + "graceful-fs": "^4.1.2", + "jsonfile": "^3.0.0", + "universalify": "^0.1.0" } }, "semver": { @@ -1005,8 +1005,8 @@ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "argparse": { @@ -1015,7 +1015,7 @@ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { - "sprintf-js": "1.0.3" + "sprintf-js": "~1.0.2" } }, "arr-diff": { @@ -1057,8 +1057,8 @@ "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=", "requires": { - "colour": "0.7.1", - "optjs": "3.2.2" + "colour": "~0.7.1", + "optjs": "~3.2.2" } }, "asn1": { @@ -1066,7 +1066,7 @@ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": "~2.1.0" } }, "assert-plus": { @@ -1098,7 +1098,7 @@ "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", "dev": true, "requires": { - "lodash": "4.17.14" + "lodash": "^4.17.10" } }, "async-each": { @@ -1139,13 +1139,13 @@ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "requires": { - "cache-base": "1.0.1", - "class-utils": "0.3.6", - "component-emitter": "1.2.1", - "define-property": "1.0.0", - "isobject": "3.0.1", - "mixin-deep": "1.3.2", - "pascalcase": "0.1.1" + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" }, "dependencies": { "define-property": { @@ -1154,7 +1154,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "is-accessor-descriptor": { @@ -1163,7 +1163,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -1172,7 +1172,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -1181,9 +1181,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } } } @@ -1214,7 +1214,7 @@ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "requires": { - "tweetnacl": "0.14.5" + "tweetnacl": "^0.14.3" } }, "bignumber.js": { @@ -1239,15 +1239,15 @@ "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", "requires": { "bytes": "3.1.0", - "content-type": "1.0.4", + "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "1.1.2", + "depd": "~1.1.2", "http-errors": "1.7.2", "iconv-lite": "0.4.24", - "on-finished": "2.3.0", + "on-finished": "~2.3.0", "qs": "6.7.0", "raw-body": "2.4.0", - "type-is": "1.6.18" + "type-is": "~1.6.17" }, "dependencies": { "debug": { @@ -1263,7 +1263,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": ">= 2.1.2 < 3" } }, "mime-db": { @@ -1290,7 +1290,7 @@ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.24" + "mime-types": "~2.1.24" } } } @@ -1301,13 +1301,13 @@ "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", "dev": true, "requires": { - "ansi-align": "2.0.0", - "camelcase": "4.1.0", - "chalk": "2.4.2", - "cli-boxes": "1.0.0", - "string-width": "2.1.1", - "term-size": "1.2.0", - "widest-line": "2.0.1" + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" }, "dependencies": { "ansi-regex": { @@ -1322,7 +1322,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "camelcase": { @@ -1337,9 +1337,9 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "is-fullwidth-code-point": { @@ -1354,8 +1354,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { @@ -1364,7 +1364,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -1374,7 +1374,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -1384,16 +1384,16 @@ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "repeat-element": "1.1.3", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" }, "dependencies": { "extend-shallow": { @@ -1402,7 +1402,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -1434,7 +1434,7 @@ "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", "requires": { "dicer": "0.2.5", - "readable-stream": "1.1.14" + "readable-stream": "1.1.x" }, "dependencies": { "isarray": { @@ -1447,10 +1447,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "string_decoder": { @@ -1465,7 +1465,7 @@ "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=", "requires": { - "long": "3.2.0" + "long": "~3" }, "dependencies": { "long": { @@ -1486,15 +1486,15 @@ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, "requires": { - "collection-visit": "1.0.0", - "component-emitter": "1.2.1", - "get-value": "2.0.6", - "has-value": "1.0.0", - "isobject": "3.0.1", - "set-value": "2.0.1", - "to-object-path": "0.3.0", - "union-value": "1.0.1", - "unset-value": "1.0.0" + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" } }, "callsites": { @@ -1525,12 +1525,12 @@ "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", "dev": true, "requires": { - "assertion-error": "1.1.0", - "check-error": "1.0.2", - "deep-eql": "3.0.1", - "get-func-name": "2.0.0", - "pathval": "1.1.0", - "type-detect": "4.0.8" + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" } }, "chai-http": { @@ -1539,13 +1539,13 @@ "integrity": "sha512-zFTxlN7HLMv+7+SPXZdkd5wUlK+KxH6Q7bIEMiEx0FK3zuuMqL7cwICAQ0V1+yYRozBburYuxN1qZstgHpFZQg==", "dev": true, "requires": { - "@types/chai": "4.1.7", - "@types/superagent": "3.8.7", - "cookiejar": "2.1.2", - "is-ip": "2.0.0", - "methods": "1.1.2", - "qs": "6.5.1", - "superagent": "3.8.3" + "@types/chai": "4", + "@types/superagent": "^3.8.3", + "cookiejar": "^2.1.1", + "is-ip": "^2.0.0", + "methods": "^1.1.2", + "qs": "^6.5.1", + "superagent": "^3.7.0" } }, "chalk": { @@ -1553,9 +1553,9 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", "requires": { - "ansi-styles": "1.0.0", - "has-color": "0.1.7", - "strip-ansi": "0.1.1" + "ansi-styles": "~1.0.0", + "has-color": "~0.1.0", + "strip-ansi": "~0.1.0" }, "dependencies": { "strip-ansi": { @@ -1583,18 +1583,18 @@ "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "dev": true, "requires": { - "anymatch": "2.0.0", - "async-each": "1.0.3", - "braces": "2.3.2", - "fsevents": "1.2.9", - "glob-parent": "3.1.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "4.0.1", - "normalize-path": "3.0.0", - "path-is-absolute": "1.0.1", - "readdirp": "2.2.1", - "upath": "1.2.0" + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" } }, "chownr": { @@ -1614,10 +1614,10 @@ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { - "arr-union": "3.1.0", - "define-property": "0.2.5", - "isobject": "3.0.1", - "static-extend": "0.1.2" + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" }, "dependencies": { "define-property": { @@ -1626,7 +1626,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } } } @@ -1643,7 +1643,7 @@ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "requires": { - "restore-cursor": "3.1.0" + "restore-cursor": "^3.1.0" } }, "cli-width": { @@ -1657,9 +1657,9 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" } }, "code-point-at": { @@ -1673,8 +1673,8 @@ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "map-visit": "1.0.0", - "object-visit": "1.0.1" + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" } }, "color": { @@ -1683,8 +1683,8 @@ "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", "dev": true, "requires": { - "color-convert": "1.9.1", - "color-string": "1.5.3" + "color-convert": "^1.9.1", + "color-string": "^1.5.2" } }, "color-convert": { @@ -1692,7 +1692,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "requires": { - "color-name": "1.1.3" + "color-name": "^1.1.1" } }, "color-name": { @@ -1706,8 +1706,8 @@ "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", "dev": true, "requires": { - "color-name": "1.1.3", - "simple-swizzle": "0.2.2" + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" } }, "colornames": { @@ -1727,8 +1727,8 @@ "integrity": "sha512-pI3btWyiuz7Ken0BWh9Elzsmv2bM9AhA7psXib4anUXy/orfZ/E0MbQwhSOG/9L8hLlalqrU0UhOuqxW1YjmVw==", "dev": true, "requires": { - "color": "3.0.0", - "text-hex": "1.0.0" + "color": "3.0.x", + "text-hex": "1.0.x" } }, "colour": { @@ -1741,7 +1741,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "requires": { - "delayed-stream": "1.0.0" + "delayed-stream": "~1.0.0" } }, "commander": { @@ -1761,7 +1761,7 @@ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", "requires": { - "mime-db": "1.42.0" + "mime-db": ">= 1.40.0 < 2" }, "dependencies": { "mime-db": { @@ -1781,10 +1781,10 @@ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "requires": { - "buffer-from": "1.0.0", - "inherits": "2.0.3", - "readable-stream": "2.3.6", - "typedarray": "0.0.6" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" } }, "configstore": { @@ -1793,12 +1793,12 @@ "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", "dev": true, "requires": { - "dot-prop": "4.2.0", - "graceful-fs": "4.1.11", - "make-dir": "1.3.0", - "unique-string": "1.0.0", - "write-file-atomic": "2.4.3", - "xdg-basedir": "3.0.0" + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" } }, "console-control-strings": { @@ -1847,7 +1847,7 @@ "requires": { "cookies": "0.7.1", "debug": "3.1.0", - "on-headers": "1.0.1", + "on-headers": "~1.0.1", "safe-buffer": "5.1.1" }, "dependencies": { @@ -1877,8 +1877,8 @@ "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.1.tgz", "integrity": "sha1-fIphX1SBxhq58WyDNzG8uPZjuZs=", "requires": { - "depd": "1.1.2", - "keygrip": "1.0.2" + "depd": "~1.1.1", + "keygrip": "~1.0.2" } }, "copy-descriptor": { @@ -1897,8 +1897,8 @@ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "requires": { - "object-assign": "4.1.1", - "vary": "1.1.2" + "object-assign": "^4", + "vary": "^1" } }, "create-error-class": { @@ -1907,7 +1907,7 @@ "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "dev": true, "requires": { - "capture-stack-trace": "1.0.1" + "capture-stack-trace": "^1.0.0" } }, "cross-spawn": { @@ -1916,9 +1916,9 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "4.1.5", - "shebang-command": "1.2.0", - "which": "1.3.1" + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } }, "cryptiles": { @@ -1926,7 +1926,7 @@ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-4.1.3.tgz", "integrity": "sha512-gT9nyTMSUC1JnziQpPbxKGBbUg8VL7Zn2NB4E1cJYvuXdElHrwxrV9bmltZGDzet45zSDGyYceueke1TjynGzw==", "requires": { - "boom": "7.2.2" + "boom": "7.x.x" }, "dependencies": { "boom": { @@ -1934,7 +1934,7 @@ "resolved": "https://registry.npmjs.org/boom/-/boom-7.2.2.tgz", "integrity": "sha512-IFUbOa8PS7xqmhIjpeStwT3d09hGkNYQ6aj2iELSTxcVs2u0aKn1NzhkdUQSzsRg1FVkj3uit3I6mXQCBixw+A==", "requires": { - "hoek": "6.0.3" + "hoek": "6.x.x" } }, "hoek": { @@ -1965,7 +1965,7 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" } }, "date-and-time": { @@ -1978,7 +1978,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "requires": { - "ms": "2.1.1" + "ms": "^2.1.1" }, "dependencies": { "ms": { @@ -2005,7 +2005,7 @@ "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", "dev": true, "requires": { - "type-detect": "4.0.8" + "type-detect": "^4.0.0" } }, "deep-extend": { @@ -2030,7 +2030,7 @@ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { - "object-keys": "1.1.1" + "object-keys": "^1.0.12" } }, "define-property": { @@ -2039,8 +2039,8 @@ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "is-descriptor": "1.0.2", - "isobject": "3.0.1" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "dependencies": { "is-accessor-descriptor": { @@ -2049,7 +2049,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -2058,7 +2058,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -2067,9 +2067,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } } } @@ -2105,9 +2105,9 @@ "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", "dev": true, "requires": { - "colorspace": "1.1.1", - "enabled": "1.0.2", - "kuler": "1.0.1" + "colorspace": "1.1.x", + "enabled": "1.0.x", + "kuler": "1.0.x" } }, "dicer": { @@ -2115,7 +2115,7 @@ "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", "requires": { - "readable-stream": "1.1.14", + "readable-stream": "1.1.x", "streamsearch": "0.1.2" }, "dependencies": { @@ -2129,10 +2129,10 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "string_decoder": { @@ -2159,7 +2159,7 @@ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "requires": { - "esutils": "2.0.3" + "esutils": "^2.0.2" } }, "dot-prop": { @@ -2168,7 +2168,7 @@ "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", "dev": true, "requires": { - "is-obj": "1.0.1" + "is-obj": "^1.0.0" } }, "dotenv": { @@ -2187,10 +2187,10 @@ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", "requires": { - "end-of-stream": "1.4.1", - "inherits": "2.0.3", - "readable-stream": "2.3.6", - "stream-shift": "1.0.0" + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" } }, "ecc-jsbn": { @@ -2198,8 +2198,8 @@ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "requires": { - "jsbn": "0.1.1", - "safer-buffer": "2.1.2" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, "ecdsa-sig-formatter": { @@ -2207,7 +2207,7 @@ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "^5.0.1" } }, "ee-first": { @@ -2226,7 +2226,7 @@ "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", "dev": true, "requires": { - "env-variable": "0.0.5" + "env-variable": "0.0.x" } }, "encodeurl": { @@ -2239,7 +2239,7 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "requires": { - "once": "1.4.0" + "once": "^1.4.0" } }, "ent": { @@ -2259,16 +2259,16 @@ "integrity": "sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==", "dev": true, "requires": { - "es-to-primitive": "1.2.0", - "function-bind": "1.1.1", - "has": "1.0.3", - "has-symbols": "1.0.0", - "is-callable": "1.1.4", - "is-regex": "1.0.4", - "object-inspect": "1.6.0", - "object-keys": "1.1.1", - "string.prototype.trimleft": "2.1.0", - "string.prototype.trimright": "2.1.0" + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.0", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.0.0", + "string.prototype.trimright": "^2.0.0" } }, "es-to-primitive": { @@ -2277,9 +2277,9 @@ "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", "dev": true, "requires": { - "is-callable": "1.1.4", - "is-date-object": "1.0.1", - "is-symbol": "1.0.2" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" } }, "es6-promise": { @@ -2292,7 +2292,7 @@ "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "requires": { - "es6-promise": "4.2.5" + "es6-promise": "^4.0.3" } }, "escape-html": { @@ -2311,43 +2311,43 @@ "integrity": "sha512-qMlSWJaCSxDFr8fBPvJM9kJwbazrhNcBU3+DszDW1OlEwKBBRWsJc7NJFelvwQpanHCR14cOLD41x8Eqvo3Nng==", "dev": true, "requires": { - "@babel/code-frame": "7.5.5", - "ajv": "6.10.2", - "chalk": "2.4.2", - "cross-spawn": "6.0.5", - "debug": "4.1.1", - "doctrine": "3.0.0", - "eslint-scope": "5.0.0", - "eslint-utils": "1.4.3", - "eslint-visitor-keys": "1.1.0", - "espree": "6.1.2", - "esquery": "1.0.1", - "esutils": "2.0.3", - "file-entry-cache": "5.0.1", - "functional-red-black-tree": "1.0.1", - "glob-parent": "5.1.0", - "globals": "12.3.0", - "ignore": "4.0.6", - "import-fresh": "3.2.1", - "imurmurhash": "0.1.4", - "inquirer": "7.0.0", - "is-glob": "4.0.1", - "js-yaml": "3.13.1", - "json-stable-stringify-without-jsonify": "1.0.1", - "levn": "0.3.0", - "lodash": "4.17.14", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "natural-compare": "1.4.0", - "optionator": "0.8.3", - "progress": "2.0.3", - "regexpp": "2.0.1", - "semver": "6.3.0", - "strip-ansi": "5.2.0", - "strip-json-comments": "3.0.1", - "table": "5.4.6", - "text-table": "0.2.0", - "v8-compile-cache": "2.1.0" + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" }, "dependencies": { "ansi-regex": { @@ -2362,7 +2362,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -2371,9 +2371,9 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "cross-spawn": { @@ -2382,11 +2382,11 @@ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "nice-try": "1.0.5", - "path-key": "2.0.1", - "semver": "5.7.1", - "shebang-command": "1.2.0", - "which": "1.3.1" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" }, "dependencies": { "semver": { @@ -2403,7 +2403,7 @@ "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", "dev": true, "requires": { - "is-glob": "4.0.1" + "is-glob": "^4.0.1" } }, "semver": { @@ -2418,7 +2418,7 @@ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "4.1.0" + "ansi-regex": "^4.1.0" } }, "strip-json-comments": { @@ -2435,7 +2435,7 @@ "integrity": "sha512-FamQVKM3jjUVwhG4hEMnbtsq7xOIDm+SY5iBPfR8gKsJoAB2IQnNF+bk1+8Fy44Nq7PPJaLvkRxILYdJWoguKQ==", "dev": true, "requires": { - "get-stdin": "6.0.0" + "get-stdin": "^6.0.0" } }, "eslint-plugin-prettier": { @@ -2444,7 +2444,7 @@ "integrity": "sha512-A+TZuHZ0KU0cnn56/9mfR7/KjUJ9QNVXUhwvRFSR7PGPe0zQR6PTkmyqg1AtUUEOzTqeRsUwyKFh0oVZKVCrtA==", "dev": true, "requires": { - "prettier-linter-helpers": "1.0.0" + "prettier-linter-helpers": "^1.0.0" } }, "eslint-scope": { @@ -2453,8 +2453,8 @@ "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", "dev": true, "requires": { - "esrecurse": "4.2.1", - "estraverse": "4.3.0" + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" } }, "eslint-utils": { @@ -2463,7 +2463,7 @@ "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", "dev": true, "requires": { - "eslint-visitor-keys": "1.1.0" + "eslint-visitor-keys": "^1.1.0" } }, "eslint-visitor-keys": { @@ -2478,9 +2478,9 @@ "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==", "dev": true, "requires": { - "acorn": "7.1.0", - "acorn-jsx": "5.1.0", - "eslint-visitor-keys": "1.1.0" + "acorn": "^7.1.0", + "acorn-jsx": "^5.1.0", + "eslint-visitor-keys": "^1.1.0" } }, "esprima": { @@ -2495,7 +2495,7 @@ "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", "dev": true, "requires": { - "estraverse": "4.3.0" + "estraverse": "^4.0.0" } }, "esrecurse": { @@ -2504,7 +2504,7 @@ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", "dev": true, "requires": { - "estraverse": "4.3.0" + "estraverse": "^4.1.0" } }, "estraverse": { @@ -2534,8 +2534,8 @@ "resolved": "https://registry.npmjs.org/eventid/-/eventid-0.1.2.tgz", "integrity": "sha1-CyMtPiROpbHVKJhBQOpprH7IkhU=", "requires": { - "d64": "1.0.0", - "uuid": "3.2.1" + "d64": "^1.0.0", + "uuid": "^3.0.1" } }, "execa": { @@ -2544,13 +2544,13 @@ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" } }, "expand-brackets": { @@ -2559,13 +2559,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "debug": { @@ -2583,7 +2583,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -2592,7 +2592,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -2602,36 +2602,36 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", "requires": { - "accepts": "1.3.7", + "accepts": "~1.3.7", "array-flatten": "1.1.1", "body-parser": "1.19.0", "content-disposition": "0.5.3", - "content-type": "1.0.4", + "content-type": "~1.0.4", "cookie": "0.4.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "1.1.2", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", - "finalhandler": "1.1.2", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", "fresh": "0.5.2", "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.3", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.5", + "proxy-addr": "~2.0.5", "qs": "6.7.0", - "range-parser": "1.2.1", + "range-parser": "~1.2.1", "safe-buffer": "5.1.2", "send": "0.17.1", "serve-static": "1.14.1", "setprototypeof": "1.1.1", - "statuses": "1.5.0", - "type-is": "1.6.18", + "statuses": "~1.5.0", + "type-is": "~1.6.18", "utils-merge": "1.0.1", - "vary": "1.1.2" + "vary": "~1.1.2" }, "dependencies": { "cookie": { @@ -2676,7 +2676,7 @@ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.24" + "mime-types": "~2.1.24" } } } @@ -2686,8 +2686,8 @@ "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.2.0.tgz", "integrity": "sha512-892cPistoSPzMuoG2p1W+2ZxBi0bAvPaaYgXK1E1C8/QncLo2d1HbiDDWkXUtTthjGEzEmwiELLJHu1Ez2hOEg==", "requires": { - "lodash": "4.17.15", - "validator": "11.1.0" + "lodash": "^4.17.15", + "validator": "^11.1.0" }, "dependencies": { "lodash": { @@ -2702,8 +2702,8 @@ "resolved": "https://registry.npmjs.org/express-winston/-/express-winston-2.6.0.tgz", "integrity": "sha512-m4qvQrrIErAZFMQman8CKnQB8sgVG0dSp/wRFv1ZyoWPpP/6waDZywteAdjMF57uJ5+9O7tkwZb5k9w80ZyvAA==", "requires": { - "chalk": "0.4.0", - "lodash": "4.17.14" + "chalk": "~0.4.0", + "lodash": "~4.17.5" } }, "extend": { @@ -2717,8 +2717,8 @@ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { @@ -2727,7 +2727,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -2738,9 +2738,9 @@ "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, "requires": { - "chardet": "0.7.0", - "iconv-lite": "0.4.24", - "tmp": "0.0.33" + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" }, "dependencies": { "iconv-lite": { @@ -2749,7 +2749,7 @@ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": ">= 2.1.2 < 3" } } } @@ -2760,14 +2760,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -2776,7 +2776,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "extend-shallow": { @@ -2785,7 +2785,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "is-accessor-descriptor": { @@ -2794,7 +2794,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -2803,7 +2803,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -2812,9 +2812,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } } } @@ -2872,7 +2872,7 @@ "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==", "dev": true, "requires": { - "escape-string-regexp": "1.0.5" + "escape-string-regexp": "^1.0.5" } }, "file-entry-cache": { @@ -2881,7 +2881,7 @@ "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", "dev": true, "requires": { - "flat-cache": "2.0.1" + "flat-cache": "^2.0.1" } }, "fill-range": { @@ -2890,10 +2890,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "dependencies": { "extend-shallow": { @@ -2902,7 +2902,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -2913,12 +2913,12 @@ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "requires": { "debug": "2.6.9", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.3", - "statuses": "1.5.0", - "unpipe": "1.0.0" + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" }, "dependencies": { "debug": { @@ -2936,7 +2936,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "requires": { - "locate-path": "3.0.0" + "locate-path": "^3.0.0" } }, "flat": { @@ -2945,7 +2945,7 @@ "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", "dev": true, "requires": { - "is-buffer": "2.0.3" + "is-buffer": "~2.0.3" }, "dependencies": { "is-buffer": { @@ -2962,7 +2962,7 @@ "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", "dev": true, "requires": { - "flatted": "2.0.1", + "flatted": "^2.0.0", "rimraf": "2.6.3", "write": "1.0.3" } @@ -2989,9 +2989,9 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "requires": { - "asynckit": "0.4.0", + "asynckit": "^0.4.0", "combined-stream": "1.0.6", - "mime-types": "2.1.18" + "mime-types": "^2.1.12" } }, "formidable": { @@ -3011,7 +3011,7 @@ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "map-cache": "0.2.2" + "map-cache": "^0.2.2" } }, "fresh": { @@ -3025,9 +3025,9 @@ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "4.0.0", - "universalify": "0.1.2" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "dependencies": { "jsonfile": { @@ -3036,7 +3036,7 @@ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, "requires": { - "graceful-fs": "4.1.11" + "graceful-fs": "^4.1.6" } } } @@ -3046,7 +3046,7 @@ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "requires": { - "minipass": "2.3.5" + "minipass": "^2.2.1" } }, "fs.realpath": { @@ -3061,8 +3061,8 @@ "dev": true, "optional": true, "requires": { - "nan": "2.14.0", - "node-pre-gyp": "0.12.0" + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" }, "dependencies": { "abbrev": { @@ -3074,7 +3074,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -3088,21 +3089,23 @@ "dev": true, "optional": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -3115,17 +3118,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3139,7 +3145,7 @@ "dev": true, "optional": true, "requires": { - "ms": "2.1.1" + "ms": "^2.1.1" } }, "deep-extend": { @@ -3166,7 +3172,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.3.5" + "minipass": "^2.2.1" } }, "fs.realpath": { @@ -3181,14 +3187,14 @@ "dev": true, "optional": true, "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.3" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, "glob": { @@ -3197,12 +3203,12 @@ "dev": true, "optional": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "has-unicode": { @@ -3217,7 +3223,7 @@ "dev": true, "optional": true, "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": ">= 2.1.2 < 3" } }, "ignore-walk": { @@ -3226,7 +3232,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.4" } }, "inflight": { @@ -3235,14 +3241,15 @@ "dev": true, "optional": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -3254,8 +3261,9 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "isarray": { @@ -3268,22 +3276,25 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { - "safe-buffer": "5.1.2", - "yallist": "3.0.3" + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" } }, "minizlib": { @@ -3292,13 +3303,14 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.3.5" + "minipass": "^2.2.1" } }, "mkdirp": { "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3322,9 +3334,9 @@ "dev": true, "optional": true, "requires": { - "debug": "4.1.1", - "iconv-lite": "0.4.24", - "sax": "1.2.4" + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" } }, "node-pre-gyp": { @@ -3333,16 +3345,16 @@ "dev": true, "optional": true, "requires": { - "detect-libc": "1.0.3", - "mkdirp": "0.5.1", - "needle": "2.3.0", - "nopt": "4.0.1", - "npm-packlist": "1.4.1", - "npmlog": "4.1.2", - "rc": "1.2.8", - "rimraf": "2.6.3", - "semver": "5.7.0", - "tar": "4.4.8" + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" } }, "nopt": { @@ -3351,8 +3363,8 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" + "abbrev": "1", + "osenv": "^0.1.4" } }, "npm-bundled": { @@ -3367,8 +3379,8 @@ "dev": true, "optional": true, "requires": { - "ignore-walk": "3.0.1", - "npm-bundled": "1.0.6" + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" } }, "npmlog": { @@ -3377,16 +3389,17 @@ "dev": true, "optional": true, "requires": { - "are-we-there-yet": "1.1.5", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3398,8 +3411,9 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "os-homedir": { @@ -3420,8 +3434,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "path-is-absolute": { @@ -3442,10 +3456,10 @@ "dev": true, "optional": true, "requires": { - "deep-extend": "0.6.0", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -3462,13 +3476,13 @@ "dev": true, "optional": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.2", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "rimraf": { @@ -3477,13 +3491,14 @@ "dev": true, "optional": true, "requires": { - "glob": "7.1.3" + "glob": "^7.1.3" } }, "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3519,10 +3534,11 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { @@ -3531,15 +3547,16 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-json-comments": { @@ -3554,13 +3571,13 @@ "dev": true, "optional": true, "requires": { - "chownr": "1.1.1", - "fs-minipass": "1.2.5", - "minipass": "2.3.5", - "minizlib": "1.2.1", - "mkdirp": "0.5.1", - "safe-buffer": "5.1.2", - "yallist": "3.0.3" + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" } }, "util-deprecate": { @@ -3575,18 +3592,20 @@ "dev": true, "optional": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2 || 2" } }, "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -3607,14 +3626,14 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.3" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, "gaxios": { @@ -3622,9 +3641,9 @@ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.4.0.tgz", "integrity": "sha512-qW0q08OcvFwaSmwUiELnif+q5NvAAoQfUN6iq8lx/HnmgMcJ9U+jiB+c+5C1muSBGsQ3D3PiLFpJ9jjO8BRCDg==", "requires": { - "extend": "3.0.2", - "https-proxy-agent": "2.2.1", - "node-fetch": "2.3.0" + "extend": "^3.0.2", + "https-proxy-agent": "^2.2.1", + "node-fetch": "^2.2.0" } }, "gcp-metadata": { @@ -3632,8 +3651,8 @@ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.9.3.tgz", "integrity": "sha512-caV4S84xAjENtpezLCT/GILEAF5h/bC4cNqZFmt/tjTn8t+JBtTkQrgBrJu3857YdsnlM8rxX/PMcKGtE8hUlw==", "requires": { - "gaxios": "1.4.0", - "json-bigint": "0.3.0" + "gaxios": "^1.0.2", + "json-bigint": "^0.3.0" } }, "gcs-resumable-upload": { @@ -3641,12 +3660,12 @@ "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-2.3.0.tgz", "integrity": "sha512-PclXJiEngrVx0c4K0LfE1XOxhmOkBEy39Rrhspdn6jAbbwe4OQMZfjo7Z1LHBrh57+bNZeIN4M+BooYppCoHSg==", "requires": { - "abort-controller": "3.0.0", - "configstore": "5.0.0", - "gaxios": "2.0.1", - "google-auth-library": "5.5.0", - "pumpify": "2.0.1", - "stream-events": "1.0.4" + "abort-controller": "^3.0.0", + "configstore": "^5.0.0", + "gaxios": "^2.0.0", + "google-auth-library": "^5.0.0", + "pumpify": "^2.0.0", + "stream-events": "^1.0.4" }, "dependencies": { "arrify": { @@ -3659,12 +3678,12 @@ "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.0.tgz", "integrity": "sha512-eE/hvMs7qw7DlcB5JPRnthmrITuHMmACUJAp89v6PT6iOqzoLS7HRWhBtuHMlhNHo2AhUSA/3Dh1bKNJHcublQ==", "requires": { - "dot-prop": "5.1.0", - "graceful-fs": "4.1.11", - "make-dir": "3.0.0", - "unique-string": "2.0.0", - "write-file-atomic": "3.0.0", - "xdg-basedir": "4.0.0" + "dot-prop": "^5.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" } }, "crypto-random-string": { @@ -3677,7 +3696,7 @@ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.1.0.tgz", "integrity": "sha512-n1oC6NBF+KM9oVXtjmen4Yo7HyAVWV2UUl50dCYJdw2924K6dX9bf9TTTWaKtYlRn0FEtxG27KS80ayVLixxJA==", "requires": { - "is-obj": "2.0.0" + "is-obj": "^2.0.0" } }, "duplexify": { @@ -3685,10 +3704,10 @@ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", "requires": { - "end-of-stream": "1.4.1", - "inherits": "2.0.3", - "readable-stream": "3.4.0", - "stream-shift": "1.0.0" + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" } }, "gaxios": { @@ -3696,10 +3715,10 @@ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.0.1.tgz", "integrity": "sha512-c1NXovTxkgRJTIgB2FrFmOFg4YIV6N/bAa4f/FZ4jIw13Ql9ya/82x69CswvotJhbV3DiGnlTZwoq2NVXk2Irg==", "requires": { - "abort-controller": "3.0.0", - "extend": "3.0.2", - "https-proxy-agent": "2.2.1", - "node-fetch": "2.3.0" + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^2.2.1", + "node-fetch": "^2.3.0" } }, "gcp-metadata": { @@ -3707,8 +3726,8 @@ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.2.0.tgz", "integrity": "sha512-ympv+yQ6k5QuWCuwQqnGEvFGS7MBKdcQdj1i188v3bW9QLFIchTGaBCEZxSQapT0jffdn1vdt8oJhB5VBWQO1Q==", "requires": { - "gaxios": "2.0.1", - "json-bigint": "0.3.0" + "gaxios": "^2.0.1", + "json-bigint": "^0.3.0" } }, "google-auth-library": { @@ -3716,14 +3735,14 @@ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.5.0.tgz", "integrity": "sha512-TNeraw4miu6/FhO0jDrHiJuRx3SBrAhxHSPj7+rhif43bKE34UItXX9lejKxo3E8FNytuY4LIVIvpcqMQHSYZw==", "requires": { - "arrify": "2.0.1", - "base64-js": "1.3.0", - "fast-text-encoding": "1.0.0", - "gaxios": "2.0.1", - "gcp-metadata": "3.2.0", - "gtoken": "4.1.0", - "jws": "3.1.5", - "lru-cache": "5.1.1" + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "fast-text-encoding": "^1.0.0", + "gaxios": "^2.0.0", + "gcp-metadata": "^3.2.0", + "gtoken": "^4.1.0", + "jws": "^3.1.5", + "lru-cache": "^5.0.0" } }, "google-p12-pem": { @@ -3731,7 +3750,7 @@ "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.2.tgz", "integrity": "sha512-UfnEARfJKI6pbmC1hfFFm+UAcZxeIwTiEcHfqKe/drMsXD/ilnVjF7zgOGpHXyhuvX6jNJK3S8A0hOQjwtFxEw==", "requires": { - "node-forge": "0.9.1" + "node-forge": "^0.9.0" } }, "gtoken": { @@ -3739,10 +3758,10 @@ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.0.tgz", "integrity": "sha512-wqyn2gf5buzEZN4QNmmiiW2i2JkEdZnL7Z/9p44RtZqgt4077m4khRgAYNuu8cBwHWCc6MsP6eDUn/KkF6jFIw==", "requires": { - "gaxios": "2.0.1", - "google-p12-pem": "2.0.2", - "jws": "3.1.5", - "mime": "2.4.4" + "gaxios": "^2.0.0", + "google-p12-pem": "^2.0.0", + "jws": "^3.1.5", + "mime": "^2.2.0" } }, "is-obj": { @@ -3755,7 +3774,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "requires": { - "yallist": "3.1.1" + "yallist": "^3.0.2" } }, "make-dir": { @@ -3763,7 +3782,7 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", "requires": { - "semver": "6.3.0" + "semver": "^6.0.0" } }, "mime": { @@ -3781,8 +3800,8 @@ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, "pumpify": { @@ -3790,9 +3809,9 @@ "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", "requires": { - "duplexify": "4.1.1", - "inherits": "2.0.3", - "pump": "3.0.0" + "duplexify": "^4.1.1", + "inherits": "^2.0.3", + "pump": "^3.0.0" } }, "readable-stream": { @@ -3800,9 +3819,9 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", "requires": { - "inherits": "2.0.3", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, "semver": { @@ -3815,7 +3834,7 @@ "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", "requires": { - "crypto-random-string": "2.0.0" + "crypto-random-string": "^2.0.0" } }, "write-file-atomic": { @@ -3823,10 +3842,10 @@ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.0.tgz", "integrity": "sha512-EIgkf60l2oWsffja2Sf2AL384dx328c0B+cIYPTQq5q2rOYuDV00/iPFBOUiDKKwKMOhkymH8AidPaRvzfxY+Q==", "requires": { - "imurmurhash": "0.1.4", - "is-typedarray": "1.0.0", - "signal-exit": "3.0.2", - "typedarray-to-buffer": "3.1.5" + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, "xdg-basedir": { @@ -3875,7 +3894,7 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" } }, "glob": { @@ -3884,12 +3903,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "glob-parent": { @@ -3898,8 +3917,8 @@ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "3.1.0", - "path-dirname": "1.0.2" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" }, "dependencies": { "is-glob": { @@ -3908,7 +3927,7 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "^2.1.0" } } } @@ -3919,7 +3938,7 @@ "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", "dev": true, "requires": { - "ini": "1.3.5" + "ini": "^1.3.4" } }, "globals": { @@ -3928,7 +3947,7 @@ "integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==", "dev": true, "requires": { - "type-fest": "0.8.1" + "type-fest": "^0.8.1" } }, "google-auth-library": { @@ -3936,15 +3955,15 @@ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-3.1.0.tgz", "integrity": "sha512-EntjrOgSffw5EhZGoV8+ROPwEK/aQpoMZaULw3bKailEGdjaUI25PmmFc4AN6vG/Q24YEUiuLxtTXa1Usar5Eg==", "requires": { - "base64-js": "1.3.0", - "fast-text-encoding": "1.0.0", - "gaxios": "1.4.0", - "gcp-metadata": "0.9.3", - "gtoken": "2.3.2", - "https-proxy-agent": "2.2.1", - "jws": "3.1.5", - "lru-cache": "5.1.1", - "semver": "5.5.0" + "base64-js": "^1.3.0", + "fast-text-encoding": "^1.0.0", + "gaxios": "^1.2.1", + "gcp-metadata": "^0.9.3", + "gtoken": "^2.3.2", + "https-proxy-agent": "^2.2.1", + "jws": "^3.1.5", + "lru-cache": "^5.0.0", + "semver": "^5.5.0" }, "dependencies": { "lru-cache": { @@ -3952,7 +3971,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "requires": { - "yallist": "3.0.3" + "yallist": "^3.0.2" } }, "yallist": { @@ -3967,20 +3986,20 @@ "resolved": "http://registry.npmjs.org/google-gax/-/google-gax-0.25.6.tgz", "integrity": "sha512-+CVtOSLQt42mwVvJJirhBiAvWsp8zKeb9zW5Wy3wyvb3VG9OugHzZpwvYO9D4yNPPspe7L9CpIs80I5nUJlS8w==", "requires": { - "@grpc/grpc-js": "0.3.6", - "@grpc/proto-loader": "0.4.0", - "duplexify": "3.6.0", - "google-auth-library": "3.1.0", - "google-proto-files": "0.20.0", - "grpc": "1.19.0", - "grpc-gcp": "0.1.1", - "is-stream-ended": "0.1.4", - "lodash.at": "4.6.0", - "lodash.has": "4.5.2", - "protobufjs": "6.8.8", - "retry-request": "4.0.0", - "semver": "6.0.0", - "walkdir": "0.3.2" + "@grpc/grpc-js": "^0.3.0", + "@grpc/proto-loader": "^0.4.0", + "duplexify": "^3.6.0", + "google-auth-library": "^3.0.0", + "google-proto-files": "^0.20.0", + "grpc": "^1.16.0", + "grpc-gcp": "^0.1.1", + "is-stream-ended": "^0.1.4", + "lodash.at": "^4.6.0", + "lodash.has": "^4.5.2", + "protobufjs": "^6.8.8", + "retry-request": "^4.0.0", + "semver": "^6.0.0", + "walkdir": "^0.3.2" }, "dependencies": { "semver": { @@ -3995,8 +4014,8 @@ "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.2.tgz", "integrity": "sha512-+EuKr4CLlGsnXx4XIJIVkcKYrsa2xkAmCvxRhX2HsazJzUBAJ35wARGeApHUn4nNfPD03Vl057FskNr20VaCyg==", "requires": { - "node-forge": "0.7.5", - "pify": "3.0.0" + "node-forge": "^0.7.4", + "pify": "^3.0.0" } }, "google-proto-files": { @@ -4004,9 +4023,9 @@ "resolved": "http://registry.npmjs.org/google-proto-files/-/google-proto-files-0.20.0.tgz", "integrity": "sha512-ORU+XhOeDv/UPtnCYLkO1ItmfhRCRPR3ZoeVQ7GfVzEs7PVitPIhsYlY5ZzG8XXnsdmtK27ENurfQ1jhAWpZHg==", "requires": { - "@google-cloud/promisify": "0.4.0", - "protobufjs": "6.8.8", - "walkdir": "0.3.2" + "@google-cloud/promisify": "^0.4.0", + "protobufjs": "^6.8.0", + "walkdir": "^0.3.0" } }, "got": { @@ -4015,17 +4034,17 @@ "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { - "create-error-class": "3.0.2", - "duplexer3": "0.1.4", - "get-stream": "3.0.0", - "is-redirect": "1.0.0", - "is-retry-allowed": "1.2.0", - "is-stream": "1.1.0", - "lowercase-keys": "1.0.1", - "safe-buffer": "5.1.1", - "timed-out": "4.0.1", - "unzip-response": "2.0.1", - "url-parse-lax": "1.0.0" + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" } }, "graceful-fs": { @@ -4044,11 +4063,11 @@ "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.19.0.tgz", "integrity": "sha512-xX+jZ1M3YXjngsRj/gTxB4EwM0WoWUr54DmyNq9xTeg1oSuVaTPD/PK9wnZKOJWTt1pkeFspXqwJPhddZNxHOA==", "requires": { - "lodash.camelcase": "4.3.0", - "lodash.clone": "4.5.0", - "nan": "2.10.0", - "node-pre-gyp": "0.12.0", - "protobufjs": "5.0.3" + "lodash.camelcase": "^4.3.0", + "lodash.clone": "^4.5.0", + "nan": "^2.0.0", + "node-pre-gyp": "^0.12.0", + "protobufjs": "^5.0.3" }, "dependencies": { "abbrev": { @@ -4067,8 +4086,8 @@ "version": "1.1.5", "bundled": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "balanced-match": { @@ -4079,7 +4098,7 @@ "version": "1.1.11", "bundled": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -4126,7 +4145,7 @@ "version": "1.2.5", "bundled": true, "requires": { - "minipass": "2.3.5" + "minipass": "^2.2.1" } }, "fs.realpath": { @@ -4137,26 +4156,26 @@ "version": "2.7.4", "bundled": true, "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.3" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, "glob": { "version": "7.1.2", "bundled": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "has-unicode": { @@ -4167,22 +4186,22 @@ "version": "0.4.23", "bundled": true, "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": ">= 2.1.2 < 3" } }, "ignore-walk": { "version": "3.0.1", "bundled": true, "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.4" } }, "inflight": { "version": "1.0.6", "bundled": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -4197,7 +4216,7 @@ "version": "1.0.0", "bundled": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "isarray": { @@ -4208,7 +4227,7 @@ "version": "3.0.4", "bundled": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -4219,15 +4238,15 @@ "version": "2.3.5", "bundled": true, "requires": { - "safe-buffer": "5.1.2", - "yallist": "3.0.3" + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" } }, "minizlib": { "version": "1.1.1", "bundled": true, "requires": { - "minipass": "2.3.5" + "minipass": "^2.2.1" } }, "mkdirp": { @@ -4251,33 +4270,33 @@ "version": "2.2.4", "bundled": true, "requires": { - "debug": "2.6.9", - "iconv-lite": "0.4.23", - "sax": "1.2.4" + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" } }, "node-pre-gyp": { "version": "0.12.0", "bundled": true, "requires": { - "detect-libc": "1.0.3", - "mkdirp": "0.5.1", - "needle": "2.2.4", - "nopt": "4.0.1", - "npm-packlist": "1.1.12", - "npmlog": "4.1.2", - "rc": "1.2.8", - "rimraf": "2.6.2", - "semver": "5.6.0", - "tar": "4.4.8" + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" } }, "nopt": { "version": "4.0.1", "bundled": true, "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" + "abbrev": "1", + "osenv": "^0.1.4" } }, "npm-bundled": { @@ -4288,18 +4307,18 @@ "version": "1.1.12", "bundled": true, "requires": { - "ignore-walk": "3.0.1", - "npm-bundled": "1.0.5" + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" } }, "npmlog": { "version": "4.1.2", "bundled": true, "requires": { - "are-we-there-yet": "1.1.5", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { @@ -4314,7 +4333,7 @@ "version": "1.4.0", "bundled": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "os-homedir": { @@ -4329,8 +4348,8 @@ "version": "0.1.5", "bundled": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "path-is-absolute": { @@ -4346,40 +4365,40 @@ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz", "integrity": "sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==", "requires": { - "ascli": "1.0.1", - "bytebuffer": "5.0.1", - "glob": "7.1.2", - "yargs": "3.32.0" + "ascli": "~1", + "bytebuffer": "~5", + "glob": "^7.0.5", + "yargs": "^3.10.0" } }, "rc": { "version": "1.2.8", "bundled": true, "requires": { - "deep-extend": "0.6.0", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" } }, "readable-stream": { "version": "2.3.6", "bundled": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.2", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "rimraf": { "version": "2.6.2", "bundled": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } }, "safe-buffer": { @@ -4410,23 +4429,23 @@ "version": "1.0.2", "bundled": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { "version": "1.1.1", "bundled": true, "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { "version": "3.0.1", "bundled": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-json-comments": { @@ -4437,13 +4456,13 @@ "version": "4.4.8", "bundled": true, "requires": { - "chownr": "1.1.1", - "fs-minipass": "1.2.5", - "minipass": "2.3.5", - "minizlib": "1.1.1", - "mkdirp": "0.5.1", - "safe-buffer": "5.1.2", - "yallist": "3.0.3" + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" } }, "util-deprecate": { @@ -4454,7 +4473,7 @@ "version": "1.1.3", "bundled": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2 || 2" } }, "wrappy": { @@ -4472,8 +4491,8 @@ "resolved": "https://registry.npmjs.org/grpc-gcp/-/grpc-gcp-0.1.1.tgz", "integrity": "sha512-MAt0Ae9QuL2Lbbt2d+kDta5AxqRD1JVXtBcJuQKp9GeFL5TxPw/hxIyDNyivPjKEXjbG3cBGwSE3CXq6a3KHEQ==", "requires": { - "grpc": "1.19.0", - "protobufjs": "6.8.8" + "grpc": "^1.16.0", + "protobufjs": "^6.8.8" } }, "gtoken": { @@ -4481,11 +4500,11 @@ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.2.tgz", "integrity": "sha512-F8EObUGyC8Qd3WXTloNULZBwfUsOABoHElihB1F6zGhT/cy38iPL09wGLRY712I+hQnOyA+sYlgPFX2cOKz0qg==", "requires": { - "gaxios": "1.4.0", - "google-p12-pem": "1.0.2", - "jws": "3.1.5", - "mime": "2.4.0", - "pify": "4.0.1" + "gaxios": "^1.0.4", + "google-p12-pem": "^1.0.0", + "jws": "^3.1.5", + "mime": "^2.2.0", + "pify": "^4.0.0" }, "dependencies": { "mime": { @@ -4505,10 +4524,10 @@ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", "requires": { - "neo-async": "2.6.1", - "optimist": "0.6.1", - "source-map": "0.6.1", - "uglify-js": "3.6.1" + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" }, "dependencies": { "source-map": { @@ -4528,8 +4547,8 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "requires": { - "ajv": "6.10.2", - "har-schema": "2.0.0" + "ajv": "^6.5.5", + "har-schema": "^2.0.0" } }, "has": { @@ -4538,7 +4557,7 @@ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "function-bind": "1.1.1" + "function-bind": "^1.1.1" } }, "has-color": { @@ -4568,9 +4587,9 @@ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "1.0.0", - "isobject": "3.0.1" + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" } }, "has-values": { @@ -4579,8 +4598,8 @@ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "dependencies": { "kind-of": { @@ -4589,7 +4608,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -4599,7 +4618,7 @@ "resolved": "https://registry.npmjs.org/hash-stream-validation/-/hash-stream-validation-0.2.2.tgz", "integrity": "sha512-cMlva5CxWZOrlS/cY0C+9qAzesn5srhFA8IT1VPiHc9bWWBLkJfEUIZr7MWoi89oOOGmpg8ymchaOjiArsGu5A==", "requires": { - "through2": "2.0.3" + "through2": "^2.0.0" } }, "he": { @@ -4618,10 +4637,10 @@ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", "requires": { - "depd": "1.1.2", + "depd": "~1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.1", - "statuses": "1.5.0", + "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.0" } }, @@ -4630,7 +4649,7 @@ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", "requires": { - "agent-base": "4.2.1", + "agent-base": "4", "debug": "3.1.0" }, "dependencies": { @@ -4649,9 +4668,9 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.16.1" + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, "https-proxy-agent": { @@ -4676,7 +4695,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "requires": { - "ms": "2.1.1" + "ms": "^2.1.1" } }, "ms": { @@ -4708,7 +4727,7 @@ "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.4" } }, "import-fresh": { @@ -4717,8 +4736,8 @@ "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", "dev": true, "requires": { - "parent-module": "1.0.1", - "resolve-from": "4.0.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "dependencies": { "resolve-from": { @@ -4745,8 +4764,8 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -4765,19 +4784,19 @@ "integrity": "sha512-rSdC7zelHdRQFkWnhsMu2+2SO41mpv2oF2zy4tMhmiLWkcKbOAs87fWAJhVXttKVwhdZvymvnuM95EyEXg2/tQ==", "dev": true, "requires": { - "ansi-escapes": "4.3.0", - "chalk": "2.4.2", - "cli-cursor": "3.1.0", - "cli-width": "2.2.0", - "external-editor": "3.1.0", - "figures": "3.1.0", - "lodash": "4.17.15", + "ansi-escapes": "^4.2.1", + "chalk": "^2.4.2", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", "mute-stream": "0.0.8", - "run-async": "2.3.0", - "rxjs": "6.5.3", - "string-width": "4.2.0", - "strip-ansi": "5.2.0", - "through": "2.3.8" + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^4.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" }, "dependencies": { "ansi-regex": { @@ -4792,7 +4811,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -4801,9 +4820,9 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "emoji-regex": { @@ -4830,9 +4849,9 @@ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { - "emoji-regex": "8.0.0", - "is-fullwidth-code-point": "3.0.0", - "strip-ansi": "6.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" }, "dependencies": { "strip-ansi": { @@ -4841,7 +4860,7 @@ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "5.0.0" + "ansi-regex": "^5.0.0" } } } @@ -4852,7 +4871,7 @@ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "4.1.0" + "ansi-regex": "^4.1.0" }, "dependencies": { "ansi-regex": { @@ -4892,7 +4911,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -4901,7 +4920,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -4912,7 +4931,7 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "1.13.1" + "binary-extensions": "^1.0.0" } }, "is-buffer": { @@ -4933,7 +4952,7 @@ "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", "dev": true, "requires": { - "ci-info": "1.6.0" + "ci-info": "^1.5.0" } }, "is-data-descriptor": { @@ -4942,7 +4961,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -4951,7 +4970,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -4968,9 +4987,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "dependencies": { "kind-of": { @@ -4998,7 +5017,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "is-glob": { @@ -5007,7 +5026,7 @@ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "^2.1.1" } }, "is-installed-globally": { @@ -5016,8 +5035,8 @@ "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", "dev": true, "requires": { - "global-dirs": "0.1.1", - "is-path-inside": "1.0.1" + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" } }, "is-ip": { @@ -5026,7 +5045,7 @@ "integrity": "sha1-aO6gfooKCpTC0IDdZ0xzGrKkYas=", "dev": true, "requires": { - "ip-regex": "2.1.0" + "ip-regex": "^2.0.0" } }, "is-npm": { @@ -5041,7 +5060,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -5050,7 +5069,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -5067,7 +5086,7 @@ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "dev": true, "requires": { - "path-is-inside": "1.0.2" + "path-is-inside": "^1.0.1" } }, "is-plain-object": { @@ -5076,7 +5095,7 @@ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" } }, "is-promise": { @@ -5097,7 +5116,7 @@ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "dev": true, "requires": { - "has": "1.0.3" + "has": "^1.0.1" } }, "is-retry-allowed": { @@ -5123,7 +5142,7 @@ "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", "dev": true, "requires": { - "has-symbols": "1.0.0" + "has-symbols": "^1.0.0" } }, "is-typedarray": { @@ -5171,8 +5190,8 @@ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { - "argparse": "1.0.10", - "esprima": "4.0.1" + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, "jsbn": { @@ -5185,7 +5204,7 @@ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=", "requires": { - "bignumber.js": "7.2.1" + "bignumber.js": "^7.0.0" } }, "json-schema": { @@ -5215,7 +5234,7 @@ "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", "dev": true, "requires": { - "graceful-fs": "4.1.11" + "graceful-fs": "^4.1.6" } }, "jsonwebtoken": { @@ -5223,16 +5242,16 @@ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", "requires": { - "jws": "3.2.2", - "lodash.includes": "4.3.0", - "lodash.isboolean": "3.0.3", - "lodash.isinteger": "4.0.4", - "lodash.isnumber": "3.0.3", - "lodash.isplainobject": "4.0.6", - "lodash.isstring": "4.0.1", - "lodash.once": "4.1.1", - "ms": "2.1.1", - "semver": "5.6.0" + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" }, "dependencies": { "jwa": { @@ -5242,7 +5261,7 @@ "requires": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "5.1.1" + "safe-buffer": "^5.0.1" } }, "jws": { @@ -5250,8 +5269,8 @@ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", "requires": { - "jwa": "1.4.1", - "safe-buffer": "5.1.1" + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" } }, "ms": { @@ -5284,7 +5303,7 @@ "requires": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.10", - "safe-buffer": "5.1.1" + "safe-buffer": "^5.0.1" }, "dependencies": { "ecdsa-sig-formatter": { @@ -5292,7 +5311,7 @@ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz", "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "^5.0.1" } } } @@ -5302,8 +5321,8 @@ "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz", "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==", "requires": { - "jwa": "1.1.6", - "safe-buffer": "5.1.1" + "jwa": "^1.1.5", + "safe-buffer": "^5.0.1" } }, "kareem": { @@ -5328,7 +5347,7 @@ "integrity": "sha1-PTvNhgDnv971MjHHOf8FOu1WDkQ=", "dev": true, "requires": { - "graceful-fs": "4.1.11" + "graceful-fs": "^4.1.11" } }, "kuler": { @@ -5337,7 +5356,7 @@ "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", "dev": true, "requires": { - "colornames": "1.1.1" + "colornames": "^1.1.1" } }, "latest-version": { @@ -5346,7 +5365,7 @@ "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", "dev": true, "requires": { - "package-json": "4.0.1" + "package-json": "^4.0.0" } }, "lcid": { @@ -5354,7 +5373,7 @@ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "requires": { - "invert-kv": "1.0.0" + "invert-kv": "^1.0.0" } }, "levn": { @@ -5363,8 +5382,8 @@ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "prelude-ls": "1.1.2", - "type-check": "0.3.2" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" } }, "linkify-it": { @@ -5373,7 +5392,7 @@ "integrity": "sha512-4REs8/062kV2DSHxNfq5183zrqXMl7WP0WzABH9IeJI+NLm429FgE1PDecltYfnOoFDFlZGh2T8PfZn0r+GTRg==", "dev": true, "requires": { - "uc.micro": "1.0.5" + "uc.micro": "^1.0.1" } }, "locate-path": { @@ -5381,8 +5400,8 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "requires": { - "p-locate": "3.0.0", - "path-exists": "3.0.0" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" }, "dependencies": { "path-exists": { @@ -5468,7 +5487,7 @@ "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", "dev": true, "requires": { - "chalk": "2.4.2" + "chalk": "^2.0.1" }, "dependencies": { "ansi-styles": { @@ -5477,7 +5496,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -5486,9 +5505,9 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } } } @@ -5499,11 +5518,11 @@ "integrity": "sha512-em5ojIhU18fIMOw/333mD+ZLE2fis0EzXl1ZwHx4iQzmpQi6odNiY/t+ITNr33JZhT9/KEaH+UPIipr6a9EjWg==", "dev": true, "requires": { - "colors": "1.3.3", - "fast-safe-stringify": "2.0.6", - "fecha": "2.3.3", - "ms": "2.1.1", - "triple-beam": "1.3.0" + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^2.3.3", + "ms": "^2.1.1", + "triple-beam": "^1.2.0" }, "dependencies": { "colors": { @@ -5537,8 +5556,8 @@ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } }, "make-dir": { @@ -5547,7 +5566,7 @@ "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, "requires": { - "pify": "3.0.0" + "pify": "^3.0.0" } }, "map-cache": { @@ -5567,7 +5586,7 @@ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "object-visit": "1.0.1" + "object-visit": "^1.0.0" } }, "markdown-it": { @@ -5576,11 +5595,11 @@ "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", "dev": true, "requires": { - "argparse": "1.0.10", - "entities": "1.1.2", - "linkify-it": "2.1.0", - "mdurl": "1.0.1", - "uc.micro": "1.0.5" + "argparse": "^1.0.7", + "entities": "~1.1.1", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" }, "dependencies": { "entities": { @@ -5623,19 +5642,19 @@ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.13", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" } }, "mime": { @@ -5654,7 +5673,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "requires": { - "mime-db": "1.33.0" + "mime-db": "~1.33.0" } }, "mimic-fn": { @@ -5667,7 +5686,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -5680,8 +5699,8 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "requires": { - "safe-buffer": "5.1.2", - "yallist": "3.0.3" + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" }, "dependencies": { "safe-buffer": { @@ -5701,7 +5720,7 @@ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "requires": { - "minipass": "2.3.5" + "minipass": "^2.2.1" } }, "mixin-deep": { @@ -5710,8 +5729,8 @@ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { - "for-in": "1.0.2", - "is-extendable": "1.0.1" + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { @@ -5720,7 +5739,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -5776,7 +5795,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "cliui": { @@ -5785,9 +5804,9 @@ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { - "string-width": "3.1.0", - "strip-ansi": "5.2.0", - "wrap-ansi": "5.1.0" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" } }, "debug": { @@ -5796,7 +5815,7 @@ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.1.1" + "ms": "^2.1.1" } }, "glob": { @@ -5805,12 +5824,12 @@ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "is-fullwidth-code-point": { @@ -5831,9 +5850,9 @@ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "emoji-regex": "7.0.3", - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "5.2.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" } }, "strip-ansi": { @@ -5842,7 +5861,7 @@ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "4.1.0" + "ansi-regex": "^4.1.0" } }, "supports-color": { @@ -5851,7 +5870,7 @@ "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } }, "which": { @@ -5860,7 +5879,7 @@ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { - "isexe": "2.0.0" + "isexe": "^2.0.0" } }, "wrap-ansi": { @@ -5869,9 +5888,9 @@ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "string-width": "3.1.0", - "strip-ansi": "5.2.0" + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" } }, "y18n": { @@ -5886,16 +5905,16 @@ "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", "dev": true, "requires": { - "cliui": "5.0.0", - "find-up": "3.0.0", - "get-caller-file": "2.0.5", - "require-directory": "2.1.1", - "require-main-filename": "2.0.0", - "set-blocking": "2.0.0", - "string-width": "3.1.0", - "which-module": "2.0.0", - "y18n": "4.0.0", - "yargs-parser": "13.1.1" + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" } } } @@ -5905,9 +5924,9 @@ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.3.2.tgz", "integrity": "sha512-fqJt3iywelk4yKu/lfwQg163Bjpo5zDKhXiohycvon4iQHbrfflSAz9AIlRE6496Pm/dQKQK5bMigdVo2s6gBg==", "requires": { - "bson": "1.1.1", - "require_optional": "1.0.1", - "safe-buffer": "5.2.0" + "bson": "^1.1.1", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2" }, "dependencies": { "safe-buffer": { @@ -5922,7 +5941,7 @@ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.7.5.tgz", "integrity": "sha512-BZ4FxtnbTurc/wcm/hLltLdI4IDxo4nsE0D9q58YymTdZwreNzwO62CcjVtaHhmr8HmJtOInp2W/T12FZaMf8g==", "requires": { - "bson": "1.1.1", + "bson": "~1.1.1", "kareem": "2.3.1", "mongodb": "3.3.2", "mongoose-legacy-pluralize": "1.0.2", @@ -5964,7 +5983,7 @@ "requires": { "bluebird": "3.5.1", "debug": "3.1.0", - "regexp-clone": "1.0.0", + "regexp-clone": "^1.0.0", "safe-buffer": "5.1.2", "sliced": "1.0.1" }, @@ -5994,14 +6013,14 @@ "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", "requires": { - "append-field": "1.0.0", - "busboy": "0.2.14", - "concat-stream": "1.6.2", - "mkdirp": "0.5.1", - "object-assign": "4.1.1", - "on-finished": "2.3.0", - "type-is": "1.6.18", - "xtend": "4.0.1" + "append-field": "^1.0.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.1", + "object-assign": "^4.1.1", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" } }, "mute-stream": { @@ -6021,17 +6040,17 @@ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "fragment-cache": "0.2.1", - "is-windows": "1.0.2", - "kind-of": "6.0.2", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" } }, "natural-compare": { @@ -6045,9 +6064,9 @@ "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.0.tgz", "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", "requires": { - "debug": "4.1.1", - "iconv-lite": "0.4.19", - "sax": "1.2.4" + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" } }, "negotiator": { @@ -6072,8 +6091,8 @@ "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", "dev": true, "requires": { - "object.getownpropertydescriptors": "2.0.3", - "semver": "5.7.1" + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" }, "dependencies": { "semver": { @@ -6099,16 +6118,16 @@ "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz", "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", "requires": { - "detect-libc": "1.0.3", - "mkdirp": "0.5.1", - "needle": "2.3.0", - "nopt": "4.0.1", - "npm-packlist": "1.4.1", - "npmlog": "4.1.2", - "rc": "1.2.8", - "rimraf": "2.6.3", - "semver": "5.5.0", - "tar": "4.4.8" + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" }, "dependencies": { "nopt": { @@ -6116,8 +6135,8 @@ "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" + "abbrev": "1", + "osenv": "^0.1.4" } } } @@ -6128,16 +6147,16 @@ "integrity": "sha512-VGPaqQBNk193lrJFotBU8nvWZPqEZY2eIzymy2jjY0fJ9qIsxA0sxQ8ATPl0gZC645gijYEc1jtZvpS8QWzJGQ==", "dev": true, "requires": { - "chokidar": "2.1.8", - "debug": "3.2.6", - "ignore-by-default": "1.0.1", - "minimatch": "3.0.4", - "pstree.remy": "1.1.7", - "semver": "5.7.1", - "supports-color": "5.5.0", - "touch": "3.1.0", - "undefsafe": "2.0.2", - "update-notifier": "2.5.0" + "chokidar": "^2.1.8", + "debug": "^3.2.6", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.7", + "semver": "^5.7.1", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.2", + "update-notifier": "^2.5.0" }, "dependencies": { "debug": { @@ -6146,7 +6165,7 @@ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.1" } }, "ms": { @@ -6167,7 +6186,7 @@ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } } } @@ -6178,7 +6197,7 @@ "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", "dev": true, "requires": { - "abbrev": "1.1.1" + "abbrev": "1" } }, "normalize-path": { @@ -6197,8 +6216,8 @@ "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", "requires": { - "ignore-walk": "3.0.1", - "npm-bundled": "1.0.6" + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" } }, "npm-run-path": { @@ -6207,7 +6226,7 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "2.0.1" + "path-key": "^2.0.0" } }, "npmlog": { @@ -6215,10 +6234,10 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "requires": { - "are-we-there-yet": "1.1.5", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { @@ -6242,9 +6261,9 @@ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "copy-descriptor": "0.1.1", - "define-property": "0.2.5", - "kind-of": "3.2.2" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" }, "dependencies": { "define-property": { @@ -6253,7 +6272,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "kind-of": { @@ -6262,7 +6281,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -6285,7 +6304,7 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.0" } }, "object.assign": { @@ -6294,10 +6313,10 @@ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "dev": true, "requires": { - "define-properties": "1.1.3", - "function-bind": "1.1.1", - "has-symbols": "1.0.0", - "object-keys": "1.1.1" + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" } }, "object.getownpropertydescriptors": { @@ -6306,8 +6325,8 @@ "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", "dev": true, "requires": { - "define-properties": "1.1.3", - "es-abstract": "1.14.2" + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" } }, "object.pick": { @@ -6316,7 +6335,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" } }, "on-finished": { @@ -6337,7 +6356,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "one-time": { @@ -6351,7 +6370,7 @@ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", "requires": { - "mimic-fn": "2.1.0" + "mimic-fn": "^2.1.0" } }, "optimist": { @@ -6359,8 +6378,8 @@ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.3" + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" } }, "optionator": { @@ -6369,12 +6388,12 @@ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", "dev": true, "requires": { - "deep-is": "0.1.3", - "fast-levenshtein": "2.0.6", - "levn": "0.3.0", - "prelude-ls": "1.1.2", - "type-check": "0.3.2", - "word-wrap": "1.2.3" + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" } }, "optjs": { @@ -6392,7 +6411,7 @@ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "requires": { - "lcid": "1.0.0" + "lcid": "^1.0.0" } }, "os-tmpdir": { @@ -6405,8 +6424,8 @@ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "p-finally": { @@ -6420,7 +6439,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", "requires": { - "p-try": "2.0.0" + "p-try": "^2.0.0" } }, "p-locate": { @@ -6428,7 +6447,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "requires": { - "p-limit": "2.1.0" + "p-limit": "^2.0.0" } }, "p-try": { @@ -6442,10 +6461,10 @@ "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", "dev": true, "requires": { - "got": "6.7.1", - "registry-auth-token": "3.4.0", - "registry-url": "3.1.0", - "semver": "5.5.0" + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" } }, "parent-module": { @@ -6454,7 +6473,7 @@ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "requires": { - "callsites": "3.1.0" + "callsites": "^3.0.0" } }, "parseurl": { @@ -6473,7 +6492,7 @@ "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz", "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=", "requires": { - "passport-strategy": "1.0.0", + "passport-strategy": "1.x.x", "pause": "0.0.1" } }, @@ -6482,7 +6501,7 @@ "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", "requires": { - "passport-strategy": "1.0.0" + "passport-strategy": "1.x.x" } }, "passport-strategy": { @@ -6574,7 +6593,7 @@ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, "requires": { - "fast-diff": "1.2.0" + "fast-diff": "^1.1.2" } }, "process-nextick-args": { @@ -6593,19 +6612,19 @@ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", "requires": { - "@protobufjs/aspromise": "1.1.2", - "@protobufjs/base64": "1.1.2", - "@protobufjs/codegen": "2.0.4", - "@protobufjs/eventemitter": "1.1.0", - "@protobufjs/fetch": "1.1.0", - "@protobufjs/float": "1.0.2", - "@protobufjs/inquire": "1.1.0", - "@protobufjs/path": "1.1.2", - "@protobufjs/pool": "1.1.0", - "@protobufjs/utf8": "1.1.0", - "@types/long": "4.0.0", - "@types/node": "10.14.4", - "long": "4.0.0" + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.0", + "@types/node": "^10.1.0", + "long": "^4.0.0" }, "dependencies": { "@types/node": { @@ -6620,7 +6639,7 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", "requires": { - "forwarded": "0.1.2", + "forwarded": "~0.1.2", "ipaddr.js": "1.9.0" } }, @@ -6646,8 +6665,8 @@ "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, "pumpify": { @@ -6655,9 +6674,9 @@ "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "requires": { - "duplexify": "3.6.0", - "inherits": "2.0.3", - "pump": "2.0.1" + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" } }, "punycode": { @@ -6675,10 +6694,10 @@ "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.2.tgz", "integrity": "sha512-eR6RgxFYPDFH+zFLTJKtoNP/RlsHANQb52AUmQ2bGDPMuUw7jJb0F+DNEgx7qQGIElrbFxWYMc0/B91zLZPF9Q==", "requires": { - "dijkstrajs": "1.0.1", - "isarray": "2.0.5", - "pngjs": "3.4.0", - "yargs": "13.3.0" + "dijkstrajs": "^1.0.1", + "isarray": "^2.0.1", + "pngjs": "^3.3.0", + "yargs": "^13.2.4" }, "dependencies": { "ansi-regex": { @@ -6691,7 +6710,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "camelcase": { @@ -6704,9 +6723,9 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "requires": { - "string-width": "3.1.0", - "strip-ansi": "5.2.0", - "wrap-ansi": "5.1.0" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" } }, "is-fullwidth-code-point": { @@ -6724,9 +6743,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "requires": { - "emoji-regex": "7.0.3", - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "5.2.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" } }, "strip-ansi": { @@ -6734,7 +6753,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "requires": { - "ansi-regex": "4.1.0" + "ansi-regex": "^4.1.0" } }, "wrap-ansi": { @@ -6742,9 +6761,9 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "requires": { - "ansi-styles": "3.2.1", - "string-width": "3.1.0", - "strip-ansi": "5.2.0" + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" } }, "y18n": { @@ -6757,16 +6776,16 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", "requires": { - "cliui": "5.0.0", - "find-up": "3.0.0", - "get-caller-file": "2.0.5", - "require-directory": "2.1.1", - "require-main-filename": "2.0.0", - "set-blocking": "2.0.0", - "string-width": "3.1.0", - "which-module": "2.0.0", - "y18n": "4.0.0", - "yargs-parser": "13.1.1" + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" } }, "yargs-parser": { @@ -6774,8 +6793,8 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", "requires": { - "camelcase": "5.3.1", - "decamelize": "1.2.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } @@ -6807,7 +6826,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": ">= 2.1.2 < 3" } } } @@ -6817,10 +6836,10 @@ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "requires": { - "deep-extend": "0.6.0", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -6835,13 +6854,13 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "readdirp": { @@ -6850,9 +6869,9 @@ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "micromatch": "3.1.10", - "readable-stream": "2.3.6" + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" } }, "regex-not": { @@ -6861,8 +6880,8 @@ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { - "extend-shallow": "3.0.2", - "safe-regex": "1.1.0" + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" } }, "regexp-clone": { @@ -6882,8 +6901,8 @@ "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", "dev": true, "requires": { - "rc": "1.2.8", - "safe-buffer": "5.1.1" + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" } }, "registry-url": { @@ -6892,7 +6911,7 @@ "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "dev": true, "requires": { - "rc": "1.2.8" + "rc": "^1.0.1" } }, "remove-trailing-separator": { @@ -6918,26 +6937,26 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.8.0", - "caseless": "0.12.0", - "combined-stream": "1.0.6", - "extend": "3.0.2", - "forever-agent": "0.6.1", - "form-data": "2.3.2", - "har-validator": "5.1.3", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.24", - "oauth-sign": "0.9.0", - "performance-now": "2.1.0", - "qs": "6.5.2", - "safe-buffer": "5.2.0", - "tough-cookie": "2.4.3", - "tunnel-agent": "0.6.0", - "uuid": "3.3.2" + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" }, "dependencies": { "mime-db": { @@ -6985,8 +7004,8 @@ "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", "requires": { - "resolve-from": "2.0.0", - "semver": "5.5.0" + "resolve-from": "^2.0.0", + "semver": "^5.1.0" } }, "resolve-from": { @@ -7006,8 +7025,8 @@ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "requires": { - "onetime": "5.1.0", - "signal-exit": "3.0.2" + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" } }, "ret": { @@ -7021,7 +7040,7 @@ "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-4.0.0.tgz", "integrity": "sha512-S4HNLaWcMP6r8E4TMH52Y7/pM8uNayOcTDDQNBwsCccL1uI+Ol2TljxRDPzaNfbhOB30+XWP5NnZkB3LiJxi1w==", "requires": { - "through2": "2.0.3" + "through2": "^2.0.0" } }, "rimraf": { @@ -7029,7 +7048,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "requires": { - "glob": "7.1.3" + "glob": "^7.1.3" }, "dependencies": { "glob": { @@ -7037,12 +7056,12 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } } } @@ -7053,7 +7072,7 @@ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", "dev": true, "requires": { - "is-promise": "2.1.0" + "is-promise": "^2.1.0" } }, "rxjs": { @@ -7062,7 +7081,7 @@ "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", "dev": true, "requires": { - "tslib": "1.10.0" + "tslib": "^1.9.0" } }, "safe-buffer": { @@ -7076,7 +7095,7 @@ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "ret": "0.1.15" + "ret": "~0.1.10" } }, "safer-buffer": { @@ -7100,7 +7119,7 @@ "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "dev": true, "requires": { - "semver": "5.5.0" + "semver": "^5.0.3" } }, "send": { @@ -7109,18 +7128,18 @@ "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", "requires": { "debug": "2.6.9", - "depd": "1.1.2", - "destroy": "1.0.4", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "1.7.2", + "http-errors": "~1.7.2", "mime": "1.6.0", "ms": "2.1.1", - "on-finished": "2.3.0", - "range-parser": "1.2.1", - "statuses": "1.5.0" + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" }, "dependencies": { "debug": { @@ -7155,9 +7174,9 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", "requires": { - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "parseurl": "1.3.3", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", "send": "0.17.1" } }, @@ -7172,10 +7191,10 @@ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "split-string": "3.1.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" }, "dependencies": { "extend-shallow": { @@ -7184,7 +7203,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -7200,7 +7219,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "1.0.0" + "shebang-regex": "^1.0.0" } }, "shebang-regex": { @@ -7225,7 +7244,7 @@ "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", "dev": true, "requires": { - "is-arrayish": "0.3.2" + "is-arrayish": "^0.3.1" }, "dependencies": { "is-arrayish": { @@ -7242,9 +7261,9 @@ "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "astral-regex": "1.0.0", - "is-fullwidth-code-point": "2.0.0" + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" }, "dependencies": { "ansi-styles": { @@ -7253,7 +7272,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "is-fullwidth-code-point": { @@ -7274,8 +7293,8 @@ "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-2.1.0.tgz", "integrity": "sha512-oQSiCIgNCwixBf8Kxgv0SPo67zQSutIEymAk/dkgcdZEOMPvGMGPua/WwYGPG4LLHArGGews3CB3zEEfqlMk2g==", "requires": { - "map-obj": "3.0.0", - "to-snake-case": "1.0.0" + "map-obj": "~3.0.0", + "to-snake-case": "~1.0.0" } }, "snakeize": { @@ -7289,14 +7308,14 @@ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "requires": { - "base": "0.11.2", - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "map-cache": "0.2.2", - "source-map": "0.5.7", - "source-map-resolve": "0.5.2", - "use": "3.1.1" + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" }, "dependencies": { "debug": { @@ -7314,7 +7333,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -7323,7 +7342,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -7334,9 +7353,9 @@ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { - "define-property": "1.0.0", - "isobject": "3.0.1", - "snapdragon-util": "3.0.1" + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" }, "dependencies": { "define-property": { @@ -7345,7 +7364,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "is-accessor-descriptor": { @@ -7354,7 +7373,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -7363,7 +7382,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -7372,9 +7391,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } } } @@ -7385,7 +7404,7 @@ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.2.0" }, "dependencies": { "kind-of": { @@ -7394,7 +7413,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -7411,11 +7430,11 @@ "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "dev": true, "requires": { - "atob": "2.1.2", - "decode-uri-component": "0.2.0", - "resolve-url": "0.2.1", - "source-map-url": "0.4.0", - "urix": "0.1.0" + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" } }, "source-map-url": { @@ -7429,7 +7448,7 @@ "resolved": "https://registry.npmjs.org/split-array-stream/-/split-array-stream-2.0.0.tgz", "integrity": "sha512-hmMswlVY91WvGMxs0k8MRgq8zb2mSen4FmDNc5AFiTWtrBpdZN6nwD6kROVe4vNL+ywrvbCKsWVCnEd4riELIg==", "requires": { - "is-stream-ended": "0.1.4" + "is-stream-ended": "^0.1.4" } }, "split-string": { @@ -7438,7 +7457,7 @@ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { - "extend-shallow": "3.0.2" + "extend-shallow": "^3.0.0" } }, "sprintf-js": { @@ -7452,15 +7471,15 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", "requires": { - "asn1": "0.2.4", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.2", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.2", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "safer-buffer": "2.1.2", - "tweetnacl": "0.14.5" + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" } }, "stack-trace": { @@ -7474,8 +7493,8 @@ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "define-property": "0.2.5", - "object-copy": "0.1.0" + "define-property": "^0.2.5", + "object-copy": "^0.1.0" }, "dependencies": { "define-property": { @@ -7484,7 +7503,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } } } @@ -7499,7 +7518,7 @@ "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.4.tgz", "integrity": "sha512-D243NJaYs/xBN2QnoiMDY7IesJFIK7gEhnvAYqJa5JvDdnh2dC4qDBwlCf0ohPpX2QRlA/4gnbnPd3rs3KxVcA==", "requires": { - "stubs": "3.0.0" + "stubs": "^3.0.0" } }, "stream-shift": { @@ -7517,9 +7536,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string.prototype.trimleft": { @@ -7528,8 +7547,8 @@ "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", "dev": true, "requires": { - "define-properties": "1.1.3", - "function-bind": "1.1.1" + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" } }, "string.prototype.trimright": { @@ -7538,8 +7557,8 @@ "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", "dev": true, "requires": { - "define-properties": "1.1.3", - "function-bind": "1.1.1" + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" } }, "string_decoder": { @@ -7547,7 +7566,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { @@ -7555,7 +7574,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-eof": { @@ -7580,16 +7599,16 @@ "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", "dev": true, "requires": { - "component-emitter": "1.2.1", - "cookiejar": "2.1.2", - "debug": "3.2.6", - "extend": "3.0.2", - "form-data": "2.3.2", - "formidable": "1.2.1", - "methods": "1.1.2", - "mime": "1.4.1", - "qs": "6.5.1", - "readable-stream": "2.3.6" + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.2.0", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.3.5" }, "dependencies": { "debug": { @@ -7598,7 +7617,7 @@ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.1" } }, "ms": { @@ -7614,7 +7633,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } }, "table": { @@ -7623,10 +7642,10 @@ "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", "dev": true, "requires": { - "ajv": "6.10.2", - "lodash": "4.17.14", - "slice-ansi": "2.1.0", - "string-width": "3.1.0" + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" }, "dependencies": { "ansi-regex": { @@ -7647,9 +7666,9 @@ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "emoji-regex": "7.0.3", - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "5.2.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" } }, "strip-ansi": { @@ -7658,7 +7677,7 @@ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "4.1.0" + "ansi-regex": "^4.1.0" } } } @@ -7668,13 +7687,13 @@ "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "requires": { - "chownr": "1.1.1", - "fs-minipass": "1.2.5", - "minipass": "2.3.5", - "minizlib": "1.2.1", - "mkdirp": "0.5.1", - "safe-buffer": "5.1.2", - "yallist": "3.0.3" + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" }, "dependencies": { "safe-buffer": { @@ -7694,11 +7713,11 @@ "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-5.3.0.tgz", "integrity": "sha512-sN9E3JvEBe2CFqB/jpJpw1erWD1C7MxyYCxogHFCQSyZfkHYcdf4wzVQSw7FZxbwcfnS+FP0W9BS0mp6SEOKjg==", "requires": { - "http-proxy-agent": "2.1.0", - "https-proxy-agent": "3.0.0", - "node-fetch": "2.3.0", - "stream-events": "1.0.5", - "uuid": "3.3.3" + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^3.0.0", + "node-fetch": "^2.2.0", + "stream-events": "^1.0.5", + "uuid": "^3.3.2" }, "dependencies": { "agent-base": { @@ -7706,7 +7725,7 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", "requires": { - "es6-promisify": "5.0.0" + "es6-promisify": "^5.0.0" } }, "debug": { @@ -7714,7 +7733,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "requires": { - "ms": "2.1.2" + "ms": "^2.1.1" } }, "https-proxy-agent": { @@ -7722,8 +7741,8 @@ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.0.tgz", "integrity": "sha512-y4jAxNEihqvBI5F3SaO2rtsjIOnnNA8sEbuiP+UhJZJHeM2NRm6c09ax2tgqme+SgUUvjao2fJXF4h3D6Cb2HQ==", "requires": { - "agent-base": "4.3.0", - "debug": "3.2.6" + "agent-base": "^4.3.0", + "debug": "^3.1.0" } }, "ms": { @@ -7736,7 +7755,7 @@ "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", "requires": { - "stubs": "3.0.0" + "stubs": "^3.0.0" } }, "uuid": { @@ -7752,7 +7771,7 @@ "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", "dev": true, "requires": { - "execa": "0.7.0" + "execa": "^0.7.0" } }, "text-hex": { @@ -7778,8 +7797,8 @@ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "requires": { - "readable-stream": "2.3.6", - "xtend": "4.0.1" + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" } }, "timed-out": { @@ -7794,7 +7813,7 @@ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { - "os-tmpdir": "1.0.2" + "os-tmpdir": "~1.0.2" } }, "to-no-case": { @@ -7808,7 +7827,7 @@ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -7817,7 +7836,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -7828,10 +7847,10 @@ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, "requires": { - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "regex-not": "1.0.2", - "safe-regex": "1.1.0" + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" } }, "to-regex-range": { @@ -7840,8 +7859,8 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "3.0.0", - "repeat-string": "1.6.1" + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" } }, "to-snake-case": { @@ -7849,7 +7868,7 @@ "resolved": "https://registry.npmjs.org/to-snake-case/-/to-snake-case-1.0.0.tgz", "integrity": "sha1-znRpE4l5RgGah+Yu366upMYIq4w=", "requires": { - "to-space-case": "1.0.0" + "to-space-case": "^1.0.0" } }, "to-space-case": { @@ -7857,7 +7876,7 @@ "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", "integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=", "requires": { - "to-no-case": "1.0.2" + "to-no-case": "^1.0.0" } }, "toidentifier": { @@ -7871,7 +7890,7 @@ "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", "dev": true, "requires": { - "nopt": "1.0.10" + "nopt": "~1.0.10" } }, "tough-cookie": { @@ -7879,8 +7898,8 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "requires": { - "psl": "1.2.0", - "punycode": "1.4.1" + "psl": "^1.1.24", + "punycode": "^1.4.1" }, "dependencies": { "punycode": { @@ -7906,7 +7925,7 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "^5.0.1" } }, "tweetnacl": { @@ -7920,7 +7939,7 @@ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { - "prelude-ls": "1.1.2" + "prelude-ls": "~1.1.2" } }, "type-detect": { @@ -7941,7 +7960,7 @@ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.24" + "mime-types": "~2.1.24" }, "dependencies": { "mime-db": { @@ -7969,7 +7988,7 @@ "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "requires": { - "is-typedarray": "1.0.0" + "is-typedarray": "^1.0.0" } }, "uc.micro": { @@ -8002,7 +8021,7 @@ "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", "dev": true, "requires": { - "debug": "2.6.9" + "debug": "^2.2.0" }, "dependencies": { "debug": { @@ -8022,10 +8041,10 @@ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { - "arr-union": "3.1.0", - "get-value": "2.0.6", - "is-extendable": "0.1.1", - "set-value": "2.0.1" + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" } }, "unique-string": { @@ -8034,7 +8053,7 @@ "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", "dev": true, "requires": { - "crypto-random-string": "1.0.0" + "crypto-random-string": "^1.0.0" } }, "universalify": { @@ -8054,8 +8073,8 @@ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "has-value": "0.3.1", - "isobject": "3.0.1" + "has-value": "^0.3.1", + "isobject": "^3.0.0" }, "dependencies": { "has-value": { @@ -8064,9 +8083,9 @@ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "0.1.4", - "isobject": "2.1.0" + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" }, "dependencies": { "isobject": { @@ -8106,16 +8125,16 @@ "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", "dev": true, "requires": { - "boxen": "1.3.0", - "chalk": "2.4.2", - "configstore": "3.1.2", - "import-lazy": "2.1.0", - "is-ci": "1.2.1", - "is-installed-globally": "0.1.0", - "is-npm": "1.0.0", - "latest-version": "3.1.0", - "semver-diff": "2.1.0", - "xdg-basedir": "3.0.0" + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" }, "dependencies": { "ansi-styles": { @@ -8124,7 +8143,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "chalk": { @@ -8133,9 +8152,9 @@ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } } } @@ -8145,7 +8164,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "requires": { - "punycode": "2.1.1" + "punycode": "^2.1.0" } }, "urix": { @@ -8160,7 +8179,7 @@ "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "dev": true, "requires": { - "prepend-http": "1.0.4" + "prepend-http": "^1.0.1" } }, "use": { @@ -8205,9 +8224,9 @@ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { - "assert-plus": "1.0.0", + "assert-plus": "^1.0.0", "core-util-is": "1.0.2", - "extsprintf": "1.3.0" + "extsprintf": "^1.2.0" } }, "walkdir": { @@ -8221,7 +8240,7 @@ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { - "isexe": "2.0.0" + "isexe": "^2.0.0" } }, "which-module": { @@ -8234,7 +8253,7 @@ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2 || 2" } }, "widest-line": { @@ -8243,7 +8262,7 @@ "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", "dev": true, "requires": { - "string-width": "2.1.1" + "string-width": "^2.1.1" }, "dependencies": { "ansi-regex": { @@ -8264,8 +8283,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { @@ -8274,7 +8293,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -8289,12 +8308,12 @@ "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.4.tgz", "integrity": "sha512-NBo2Pepn4hK4V01UfcWcDlmiVTs7VTB1h7bgnB0rgP146bYhMxX0ypCz3lBOfNxCO4Zuek7yeT+y/zM1OfMw4Q==", "requires": { - "async": "1.0.0", - "colors": "1.0.3", - "cycle": "1.0.3", - "eyes": "0.1.8", - "isstream": "0.1.2", - "stack-trace": "0.0.10" + "async": "~1.0.0", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "stack-trace": "0.0.x" }, "dependencies": { "async": { @@ -8309,8 +8328,8 @@ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", "requires": { - "readable-stream": "2.3.6", - "triple-beam": "1.3.0" + "readable-stream": "^2.3.6", + "triple-beam": "^1.2.0" } }, "word-wrap": { @@ -8329,8 +8348,8 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" } }, "wrappy": { @@ -8344,7 +8363,7 @@ "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", "dev": true, "requires": { - "mkdirp": "0.5.1" + "mkdirp": "^0.5.1" } }, "write-file-atomic": { @@ -8353,9 +8372,9 @@ "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "imurmurhash": "0.1.4", - "signal-exit": "3.0.2" + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" } }, "xdg-basedir": { @@ -8385,13 +8404,13 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", "requires": { - "camelcase": "2.1.1", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "os-locale": "1.4.0", - "string-width": "1.0.2", - "window-size": "0.1.4", - "y18n": "3.2.1" + "camelcase": "^2.0.1", + "cliui": "^3.0.3", + "decamelize": "^1.1.1", + "os-locale": "^1.4.0", + "string-width": "^1.0.1", + "window-size": "^0.1.4", + "y18n": "^3.2.0" } }, "yargs-parser": { @@ -8400,8 +8419,8 @@ "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", "dev": true, "requires": { - "camelcase": "5.3.1", - "decamelize": "1.2.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" }, "dependencies": { "camelcase": { @@ -8418,9 +8437,9 @@ "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", "dev": true, "requires": { - "flat": "4.1.0", - "lodash": "4.17.15", - "yargs": "13.3.0" + "flat": "^4.1.0", + "lodash": "^4.17.15", + "yargs": "^13.3.0" }, "dependencies": { "ansi-regex": { @@ -8435,7 +8454,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "cliui": { @@ -8444,9 +8463,9 @@ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { - "string-width": "3.1.0", - "strip-ansi": "5.2.0", - "wrap-ansi": "5.1.0" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" } }, "is-fullwidth-code-point": { @@ -8467,9 +8486,9 @@ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "emoji-regex": "7.0.3", - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "5.2.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" } }, "strip-ansi": { @@ -8478,7 +8497,7 @@ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "4.1.0" + "ansi-regex": "^4.1.0" } }, "wrap-ansi": { @@ -8487,9 +8506,9 @@ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "string-width": "3.1.0", - "strip-ansi": "5.2.0" + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" } }, "y18n": { @@ -8504,16 +8523,16 @@ "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", "dev": true, "requires": { - "cliui": "5.0.0", - "find-up": "3.0.0", - "get-caller-file": "2.0.5", - "require-directory": "2.1.1", - "require-main-filename": "2.0.0", - "set-blocking": "2.0.0", - "string-width": "3.1.0", - "which-module": "2.0.0", - "y18n": "4.0.0", - "yargs-parser": "13.1.1" + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" } } } diff --git a/routes/api/hacker.js b/routes/api/hacker.js index cd7fbe5c..8954c038 100644 --- a/routes/api/hacker.js +++ b/routes/api/hacker.js @@ -1,32 +1,32 @@ "use strict"; const express = require("express"); const Controllers = { - Hacker: require("../../controllers/hacker.controller") + Hacker: require("../../controllers/hacker.controller") }; const Middleware = { - Validator: { - /* Insert the require statement to the validator file here */ - Hacker: require("../../middlewares/validators/hacker.validator"), - RouteParam: require("../../middlewares/validators/routeParam.validator") - }, - /* Insert all of ther middleware require statements here */ - parseBody: require("../../middlewares/parse-body.middleware"), - Util: require("../../middlewares/util.middleware"), - Hacker: require("../../middlewares/hacker.middleware"), - Auth: require("../../middlewares/auth.middleware"), - Search: require("../../middlewares/search.middleware") + Validator: { + /* Insert the require statement to the validator file here */ + Hacker: require("../../middlewares/validators/hacker.validator"), + RouteParam: require("../../middlewares/validators/routeParam.validator") + }, + /* Insert all of ther middleware require statements here */ + parseBody: require("../../middlewares/parse-body.middleware"), + Util: require("../../middlewares/util.middleware"), + Hacker: require("../../middlewares/hacker.middleware"), + Auth: require("../../middlewares/auth.middleware"), + Search: require("../../middlewares/search.middleware") }; const Services = { - Hacker: require("../../services/hacker.service"), - Account: require("../../services/account.service") + Hacker: require("../../services/hacker.service"), + Account: require("../../services/account.service") }; const CONSTANTS = require("../../constants/general.constant"); module.exports = { - activate: function(apiRouter) { - const hackerRouter = express.Router(); + activate: function(apiRouter) { + const hackerRouter = express.Router(); - /** + /** * @api {get} /hacker/self get information about own hacker * @apiName self * @apiGroup Hacker @@ -70,15 +70,15 @@ module.exports = { * @apiErrorExample {object} Error-Response: * {"message": "Hacker not found", "data": {}} */ - hackerRouter.route("/self").get( - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized(), + hackerRouter.route("/self").get( + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized(), - Middleware.Hacker.findSelf, - Controllers.Hacker.showHacker - ); + Middleware.Hacker.findSelf, + Controllers.Hacker.showHacker + ); - /** + /** * @api {post} /hacker/ create a new hacker * @apiName createHacker * @apiGroup Hacker @@ -176,28 +176,28 @@ module.exports = { * @apiErrorExample {object} Error-Response: * {"message": "Error while creating hacker", "data": {}} */ - hackerRouter.route("/").post( - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized(), - Middleware.Validator.Hacker.newHackerValidator, + hackerRouter.route("/").post( + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized(), + Middleware.Validator.Hacker.newHackerValidator, - Middleware.parseBody.middleware, - // validate type - Middleware.Hacker.validateConfirmedStatusFromAccountId, - // validate that the accountId is not being used for any other thing - Middleware.Hacker.checkDuplicateAccountLinks, + Middleware.parseBody.middleware, + // validate type + Middleware.Hacker.validateConfirmedStatusFromAccountId, + // validate that the accountId is not being used for any other thing + Middleware.Hacker.checkDuplicateAccountLinks, - Middleware.Hacker.parseHacker, + Middleware.Hacker.parseHacker, - Middleware.Hacker.addDefaultStatus, - Middleware.Auth.createRoleBindings(CONSTANTS.HACKER), - Middleware.Hacker.createHacker, - Middleware.Hacker.sendAppliedStatusEmail, + Middleware.Hacker.addDefaultStatus, + Middleware.Auth.createRoleBindings(CONSTANTS.HACKER), + Middleware.Hacker.createHacker, + Middleware.Hacker.sendAppliedStatusEmail, - Controllers.Hacker.createdHacker - ); + Controllers.Hacker.createdHacker + ); - /** + /** * @api {get} /hacker/stats * Gets the stats of all of the hackers who have applied. * @apiName getHackerStats @@ -232,90 +232,90 @@ module.exports = { * } * */ - hackerRouter - .route("/stats") - .get( - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized(), - Middleware.Validator.Hacker.statsValidator, - Middleware.parseBody.middleware, - Middleware.Search.setExpandTrue, - Middleware.Search.parseQuery, - Middleware.Search.executeQuery, - Middleware.Hacker.getStats, - Controllers.Hacker.gotStats - ); - - /** - * @api {patch} /hacker/status/:id update a hacker's status - * @apiName patchHackerStatus - * @apiGroup Hacker - * @apiVersion 0.0.9 - * - * @apiParam (body) {string} [status] Status of the hacker's application ("None"|"Applied"|"Accepted"|"Declined"|"Waitlisted"|"Confirmed"|"Withdrawn"|"Checked-in") - * @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("/status/:id").patch( - Middleware.Validator.RouteParam.idValidator, - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - Middleware.Validator.Hacker.updateStatusValidator, - Middleware.parseBody.middleware, - Middleware.Hacker.parsePatch, - Middleware.Hacker.validateConfirmedStatusFromHackerId, - - Middleware.Hacker.updateHacker, - Middleware.Hacker.sendStatusUpdateEmail, - 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. - * @apiName checkinHacker - * @apiGroup Hacker - * @apiVersion 0.0.9 - * @apiParam (body) {string} [status] Check-in status. "Checked-in" - * @apiSuccess {string} message Success message - * @apiSuccess {object} data Hacker object - * @apiSuccessExample {object} Success-Response: - * { - * "message": "Changed hacker information", - * "data": { - * "status": "Checked-in" - * } - * } - * @apiPermission Administrator - * @apiPermission Volunteer - */ - hackerRouter.route("/checkin/:id").patch( - Middleware.Validator.RouteParam.idValidator, - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - - Middleware.parseBody.middleware, - Middleware.Hacker.parsePatch, - Middleware.Hacker.validateConfirmedStatusFromHackerId, - Middleware.Hacker.checkStatus([ - CONSTANTS.HACKER_STATUS_ACCEPTED, - CONSTANTS.HACKER_STATUS_CONFIRMED - ]), - Middleware.Hacker.parseCheckIn, - Middleware.Hacker.updateHacker, - - Middleware.Hacker.sendStatusUpdateEmail, - Controllers.Hacker.updatedHacker - ); - - /** + hackerRouter + .route("/stats") + .get( + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized(), + Middleware.Validator.Hacker.statsValidator, + Middleware.parseBody.middleware, + Middleware.Search.setExpandTrue, + Middleware.Search.parseQuery, + Middleware.Search.executeQuery, + Middleware.Hacker.getStats, + Controllers.Hacker.gotStats + ); + + /** + * @api {patch} /hacker/status/:id update a hacker's status + * @apiName patchHackerStatus + * @apiGroup Hacker + * @apiVersion 0.0.9 + * + * @apiParam (body) {string} [status] Status of the hacker's application ("None"|"Applied"|"Accepted"|"Declined"|"Waitlisted"|"Confirmed"|"Withdrawn"|"Checked-in") + * @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("/status/:id").patch( + Middleware.Validator.RouteParam.idValidator, + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + Middleware.Validator.Hacker.updateStatusValidator, + Middleware.parseBody.middleware, + Middleware.Hacker.parsePatch, + Middleware.Hacker.validateConfirmedStatusFromHackerId, + + Middleware.Hacker.updateHacker, + Middleware.Hacker.sendStatusUpdateEmail, + 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. + * @apiName checkinHacker + * @apiGroup Hacker + * @apiVersion 0.0.9 + * @apiParam (body) {string} [status] Check-in status. "Checked-in" + * @apiSuccess {string} message Success message + * @apiSuccess {object} data Hacker object + * @apiSuccessExample {object} Success-Response: + * { + * "message": "Changed hacker information", + * "data": { + * "status": "Checked-in" + * } + * } + * @apiPermission Administrator + * @apiPermission Volunteer + */ + hackerRouter.route("/checkin/:id").patch( + Middleware.Validator.RouteParam.idValidator, + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + + Middleware.parseBody.middleware, + Middleware.Hacker.parsePatch, + Middleware.Hacker.validateConfirmedStatusFromHackerId, + Middleware.Hacker.checkStatus([ + CONSTANTS.HACKER_STATUS_ACCEPTED, + CONSTANTS.HACKER_STATUS_CONFIRMED + ]), + Middleware.Hacker.parseCheckIn, + Middleware.Hacker.updateHacker, + + Middleware.Hacker.sendStatusUpdateEmail, + Controllers.Hacker.updatedHacker + ); + + /** * @api {patch} /hacker/:id update a hacker's information. * @apiDescription This route only contains the ability to update a subset of a hacker's information. If you want to update a status, you must have Admin priviledges and use PATCH /hacker/status/:id. * @apiName patchHacker @@ -412,23 +412,23 @@ module.exports = { * @apiErrorExample {object} Error-Response: * {"message": "Error while updating hacker", "data": {}} */ - hackerRouter.route("/:id").patch( - Middleware.Validator.RouteParam.idValidator, - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + hackerRouter.route("/:id").patch( + Middleware.Validator.RouteParam.idValidator, + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - Middleware.Validator.Hacker.updateHackerValidator, + Middleware.Validator.Hacker.updateHackerValidator, - Middleware.parseBody.middleware, - Middleware.Hacker.parsePatch, - Middleware.Hacker.validateConfirmedStatusFromHackerId, + Middleware.parseBody.middleware, + Middleware.Hacker.parsePatch, + Middleware.Hacker.validateConfirmedStatusFromHackerId, - Middleware.Hacker.updateHacker, - Middleware.Hacker.updateStatusIfApplicationCompleted, - Controllers.Hacker.updatedHacker - ); + Middleware.Hacker.updateHacker, + Middleware.Hacker.updateStatusIfApplicationCompleted, + Controllers.Hacker.updatedHacker + ); - /** + /** * @api {get} /hacker/:id get a hacker's information * @apiName getHacker * @apiGroup Hacker @@ -484,18 +484,18 @@ module.exports = { * @apiErrorExample {object} Error-Response: * {"message": "Hacker not found", "data": {}} */ - hackerRouter.route("/:id").get( - Middleware.Validator.RouteParam.idValidator, - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + hackerRouter.route("/:id").get( + Middleware.Validator.RouteParam.idValidator, + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - Middleware.parseBody.middleware, + Middleware.parseBody.middleware, - Middleware.Hacker.findById, - Controllers.Hacker.showHacker - ); + Middleware.Hacker.findById, + Controllers.Hacker.showHacker + ); - /** + /** * @api {get} /hacker/email/:email get a hacker's information * @apiName getHacker * @apiGroup Hacker @@ -551,227 +551,227 @@ module.exports = { * @apiErrorExample {object} Error-Response: * {"message": "Hacker not found", "data": {}} */ - hackerRouter.route("/email/:email").get( - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Account.findByEmail]), - - Middleware.Validator.RouteParam.emailValidator, - Middleware.parseBody.middleware, - - Middleware.Hacker.findByEmail, - Controllers.Hacker.showHacker - ); - - hackerRouter - .route("/resume/:id") - /** - * @api {post} /hacker/resume/:id upload or update resume for a hacker. - * @apiName postHackerResume - * @apiGroup Hacker - * @apiVersion 0.0.8 - * @apiDescription NOTE: This must be sent via multipart/form-data POST request - * - * @apiParam (param) {ObjectId} id Hacker id - * @apiParam (body) {File} resume The uploaded file. - * - * @apiSuccess {String} message Success message - * @apiSuccess {Object} data Location in the bucket that the file was stored. - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * message: "Uploaded resume", - * data: { - * filename: "resumes/1535032624768-507f191e810c19729de860ea" - * } - * } - * - * @apiPermission Must be logged in, and the account id must be linked to the hacker. - */ - .post( - //TODO: authenticate middleware - Middleware.Validator.Hacker.uploadResumeValidator, - Middleware.parseBody.middleware, - //verify that the hacker entity contains the account id - Middleware.Hacker.ensureAccountLinkedToHacker, - //load resume into memory - Middleware.Util.Multer.single("resume"), - //upload resume to storage and update hacker profile - Middleware.Hacker.uploadResume, - //controller response - Controllers.Hacker.uploadedResume - ) - /** - * @api {get} /hacker/resume:id get the resume for a hacker. - * @apiName getHackerResume - * @apiGroup Hacker - * @apiVersion 0.0.8 - * - * @apiParam (param) {ObjectId} id Hacker id - * - * @apiSuccess {String} message Success message - * @apiSuccessExample {json} Success-Response: - * HTTP/1.1 200 OK - * { - * message: "Downloaded resume", - * data: { - * id: "507f191e810c19729de860ea", - * resume: [Buffer] - * } - * } - * @apiError {String} message "Resume does not exist" - * @apiErrorExample {json} Error-Response: - * HTTP/1.1 404 - * { - * message: "Resume not found", - * data: {} - * } - * @apiSampleRequest off - * @apiPermission Must be logged in, and the account id must be linked to the hacker. - */ - .get( - //TODO: authenticate middleware - Middleware.Validator.Hacker.downloadResumeValidator, - Middleware.parseBody.middleware, - Middleware.Hacker.downloadResume, - Controllers.Hacker.downloadedResume - ); - - /** - * @api {patch} /hacker/confirmation/:id - * Allows confirmation of hacker attendence if they are accepted. Also allows change from 'confirmed' to 'withdrawn'. - * @apiName patchHackerConfirmed - * @apiGroup Hacker - * @apiVersion 0.0.9 - * - * @apiParam (body) {string} [status] The new status of the hacker. "Accepted", "Confirmed", or "Withdrawn" - * @apiSuccess {string} message Success message - * @apiSuccess {object} data Hacker object - * @apiSuccessExample {object} Success-Response: - * { - * "message": "Changed hacker information", - * "data": { - * "status": "Confirmed" - * } - * } - * @apiPermission Administrator - * @apiPermission Hacker - */ - hackerRouter.route("/confirmation/:id").patch( - Middleware.Validator.RouteParam.idValidator, - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - - Middleware.Validator.Hacker.updateConfirmationValidator, - Middleware.parseBody.middleware, - Middleware.Hacker.parsePatch, - Middleware.Hacker.validateConfirmedStatusFromHackerId, - Middleware.Hacker.checkStatus([ - CONSTANTS.HACKER_STATUS_ACCEPTED, - CONSTANTS.HACKER_STATUS_CONFIRMED, - CONSTANTS.HACKER_STATUS_WITHDRAWN - ]), - - Middleware.Hacker.parseConfirmation, - Middleware.Hacker.updateHacker, - - Middleware.Hacker.sendStatusUpdateEmail, - Controllers.Hacker.updatedHacker - ); - - /** - * @api {post} /hacker/email/weekOf/:id - * @apiDescription Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be either confirmed, or checked in. - * @apiName postHackerSendWeekOfEmail - * @apiGroup Hacker - * @apiVersion 0.0.9 - * - * @apiParam (param) {string} [status] The hacker ID - * @apiSuccess {string} message Success message - * @apiSuccess {object} data empty - * @apiSuccessExample {object} Success-Response: - * { - * "message": "Hacker week-of email sent.", - * "data": {} - * } - * @apiPermission Administrator - */ - hackerRouter.route("/email/weekOf/:id").post( - Middleware.Validator.RouteParam.idValidator, - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - - Middleware.parseBody.middleware, - Middleware.Hacker.findById, - - Middleware.Hacker.validateConfirmedStatusFromHackerId, - Middleware.Hacker.checkStatus([ - CONSTANTS.HACKER_STATUS_CONFIRMED, - CONSTANTS.HACKER_STATUS_CHECKED_IN - ]), - - Middleware.Hacker.sendWeekOfEmail, - Controllers.Hacker.sentWeekOfEmail - ); - - /** - * @api {post} /hacker/email/dayOf/:id - * @apiDescription Sends a hacker the day-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be either confirmed, or checked in. - * @apiName postHackerSendDayOfEmail - * @apiGroup Hacker - * @apiVersion 0.0.9 - * - * @apiParam (param) {string} [status] The hacker ID - * @apiSuccess {string} message Success message - * @apiSuccess {object} data empty - * @apiSuccessExample {object} Success-Response: - * { - * "message": "Hacker day-of email sent.", - * "data": {} - * } - * @apiPermission Administrator - */ - hackerRouter.route("/email/dayOf/:id").post( - Middleware.Validator.RouteParam.idValidator, - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - - Middleware.parseBody.middleware, - Middleware.Hacker.findById, - Middleware.Hacker.validateConfirmedStatusFromHackerId, - Middleware.Hacker.checkStatus([CONSTANTS.HACKER_STATUS_CHECKED_IN]), - Middleware.Hacker.sendDayOfEmail, - Controllers.Hacker.sentDayOfEmail - ); - - /** - * @api {post} /hacker/email/weekOf/:id - * @apiDescription Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in. - * @apiName postHackerSendWeekOfEmail - * @apiGroup Hacker - * @apiVersion 0.0.9 - * - * @apiParam (param) {string} [status] The hacker ID - * @apiSuccess {string} message Success message - * @apiSuccess {object} data empty - * @apiSuccessExample {object} Success-Response: - * { - * "message": "Hacker week-of email sent.", - * "data": {} - * } - * @apiPermission Administrator - */ - hackerRouter.route("/email/dayOf/:id").post( - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - - Middleware.Validator.RouteParam.idValidator, - Middleware.parseBody.middleware, - Middleware.Hacker.findById, - Middleware.Hacker.checkStatus([CONSTANTS.HACKER_STATUS_CHECKED_IN]), - Middleware.Hacker.sendDayOfEmail, - Controllers.Hacker.sentDayOfEmail - ); - - apiRouter.use("/hacker", hackerRouter); - } + hackerRouter.route("/email/:email").get( + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Account.findByEmail]), + + Middleware.Validator.RouteParam.emailValidator, + Middleware.parseBody.middleware, + + Middleware.Hacker.findByEmail, + Controllers.Hacker.showHacker + ); + + hackerRouter + .route("/resume/:id") + /** + * @api {post} /hacker/resume/:id upload or update resume for a hacker. + * @apiName postHackerResume + * @apiGroup Hacker + * @apiVersion 0.0.8 + * @apiDescription NOTE: This must be sent via multipart/form-data POST request + * + * @apiParam (param) {ObjectId} id Hacker id + * @apiParam (body) {File} resume The uploaded file. + * + * @apiSuccess {String} message Success message + * @apiSuccess {Object} data Location in the bucket that the file was stored. + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * { + * message: "Uploaded resume", + * data: { + * filename: "resumes/1535032624768-507f191e810c19729de860ea" + * } + * } + * + * @apiPermission Must be logged in, and the account id must be linked to the hacker. + */ + .post( + //TODO: authenticate middleware + Middleware.Validator.Hacker.uploadResumeValidator, + Middleware.parseBody.middleware, + //verify that the hacker entity contains the account id + Middleware.Hacker.ensureAccountLinkedToHacker, + //load resume into memory + Middleware.Util.Multer.single("resume"), + //upload resume to storage and update hacker profile + Middleware.Hacker.uploadResume, + //controller response + Controllers.Hacker.uploadedResume + ) + /** + * @api {get} /hacker/resume:id get the resume for a hacker. + * @apiName getHackerResume + * @apiGroup Hacker + * @apiVersion 0.0.8 + * + * @apiParam (param) {ObjectId} id Hacker id + * + * @apiSuccess {String} message Success message + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * { + * message: "Downloaded resume", + * data: { + * id: "507f191e810c19729de860ea", + * resume: [Buffer] + * } + * } + * @apiError {String} message "Resume does not exist" + * @apiErrorExample {json} Error-Response: + * HTTP/1.1 404 + * { + * message: "Resume not found", + * data: {} + * } + * @apiSampleRequest off + * @apiPermission Must be logged in, and the account id must be linked to the hacker. + */ + .get( + //TODO: authenticate middleware + Middleware.Validator.Hacker.downloadResumeValidator, + Middleware.parseBody.middleware, + Middleware.Hacker.downloadResume, + Controllers.Hacker.downloadedResume + ); + + /** + * @api {patch} /hacker/confirmation/:id + * Allows confirmation of hacker attendence if they are accepted. Also allows change from 'confirmed' to 'withdrawn'. + * @apiName patchHackerConfirmed + * @apiGroup Hacker + * @apiVersion 0.0.9 + * + * @apiParam (body) {string} [status] The new status of the hacker. "Accepted", "Confirmed", or "Withdrawn" + * @apiSuccess {string} message Success message + * @apiSuccess {object} data Hacker object + * @apiSuccessExample {object} Success-Response: + * { + * "message": "Changed hacker information", + * "data": { + * "status": "Confirmed" + * } + * } + * @apiPermission Administrator + * @apiPermission Hacker + */ + hackerRouter.route("/confirmation/:id").patch( + Middleware.Validator.RouteParam.idValidator, + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + + Middleware.Validator.Hacker.updateConfirmationValidator, + Middleware.parseBody.middleware, + Middleware.Hacker.parsePatch, + Middleware.Hacker.validateConfirmedStatusFromHackerId, + Middleware.Hacker.checkStatus([ + CONSTANTS.HACKER_STATUS_ACCEPTED, + CONSTANTS.HACKER_STATUS_CONFIRMED, + CONSTANTS.HACKER_STATUS_WITHDRAWN + ]), + + Middleware.Hacker.parseConfirmation, + Middleware.Hacker.updateHacker, + + Middleware.Hacker.sendStatusUpdateEmail, + Controllers.Hacker.updatedHacker + ); + + /** + * @api {post} /hacker/email/weekOf/:id + * @apiDescription Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be either confirmed, or checked in. + * @apiName postHackerSendWeekOfEmail + * @apiGroup Hacker + * @apiVersion 0.0.9 + * + * @apiParam (param) {string} [status] The hacker ID + * @apiSuccess {string} message Success message + * @apiSuccess {object} data empty + * @apiSuccessExample {object} Success-Response: + * { + * "message": "Hacker week-of email sent.", + * "data": {} + * } + * @apiPermission Administrator + */ + hackerRouter.route("/email/weekOf/:id").post( + Middleware.Validator.RouteParam.idValidator, + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + + Middleware.parseBody.middleware, + Middleware.Hacker.findById, + + Middleware.Hacker.validateConfirmedStatusFromHackerId, + Middleware.Hacker.checkStatus([ + CONSTANTS.HACKER_STATUS_CONFIRMED, + CONSTANTS.HACKER_STATUS_CHECKED_IN + ]), + + Middleware.Hacker.sendWeekOfEmail, + Controllers.Hacker.sentWeekOfEmail + ); + + /** + * @api {post} /hacker/email/dayOf/:id + * @apiDescription Sends a hacker the day-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be either confirmed, or checked in. + * @apiName postHackerSendDayOfEmail + * @apiGroup Hacker + * @apiVersion 0.0.9 + * + * @apiParam (param) {string} [status] The hacker ID + * @apiSuccess {string} message Success message + * @apiSuccess {object} data empty + * @apiSuccessExample {object} Success-Response: + * { + * "message": "Hacker day-of email sent.", + * "data": {} + * } + * @apiPermission Administrator + */ + hackerRouter.route("/email/dayOf/:id").post( + Middleware.Validator.RouteParam.idValidator, + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + + Middleware.parseBody.middleware, + Middleware.Hacker.findById, + Middleware.Hacker.validateConfirmedStatusFromHackerId, + Middleware.Hacker.checkStatus([CONSTANTS.HACKER_STATUS_CHECKED_IN]), + Middleware.Hacker.sendDayOfEmail, + Controllers.Hacker.sentDayOfEmail + ); + + /** + * @api {post} /hacker/email/weekOf/:id + * @apiDescription Sends a hacker the week-of email, along with the HackPass QR code to view their hacker profile (for checkin purposes). Hackers must be eitherconfirmed, or checked in. + * @apiName postHackerSendWeekOfEmail + * @apiGroup Hacker + * @apiVersion 0.0.9 + * + * @apiParam (param) {string} [status] The hacker ID + * @apiSuccess {string} message Success message + * @apiSuccess {object} data empty + * @apiSuccessExample {object} Success-Response: + * { + * "message": "Hacker week-of email sent.", + * "data": {} + * } + * @apiPermission Administrator + */ + hackerRouter.route("/email/dayOf/:id").post( + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + + Middleware.Validator.RouteParam.idValidator, + Middleware.parseBody.middleware, + Middleware.Hacker.findById, + Middleware.Hacker.checkStatus([CONSTANTS.HACKER_STATUS_CHECKED_IN]), + Middleware.Hacker.sendDayOfEmail, + Controllers.Hacker.sentDayOfEmail + ); + + apiRouter.use("/hacker", hackerRouter); + } }; diff --git a/services/account.service.js b/services/account.service.js index 599696d3..0d633232 100644 --- a/services/account.service.js +++ b/services/account.service.js @@ -10,15 +10,15 @@ const bcrypt = require("bcrypt"); * @description Finds an account by mongoID. */ function findById(id) { - const TAG = `[Account Service # findById]:`; - const query = { - _id: id - }; - - return Account.findById( - query, - logger.queryCallbackFactory(TAG, "account", query) - ); + const TAG = `[Account Service # findById]:`; + const query = { + _id: id + }; + + return Account.findById( + query, + logger.queryCallbackFactory(TAG, "account", query) + ); } /** @@ -28,11 +28,11 @@ function findById(id) { * @description Find an account by email. */ function findByEmail(email) { - const query = { - email: email - }; + const query = { + email: email + }; - return findOne(query); + return findOne(query); } /** @@ -41,12 +41,12 @@ function findByEmail(email) { * @return {Account | null} either account or null */ async function getAccountIfValid(email, password) { - const account = await findByEmail(email); + const account = await findByEmail(email); - if (!!account && account.comparePassword(password)) { - return account; - } - return null; + if (!!account && account.comparePassword(password)) { + return account; + } + return null; } /** @@ -56,7 +56,7 @@ async function getAccountIfValid(email, password) { * @description Hashes password with bcrypt. */ function hashPassword(password) { - return bcrypt.hashSync(password, 10); + return bcrypt.hashSync(password, 10); } /** @@ -66,12 +66,12 @@ function hashPassword(password) { * @description Finds an account by some query. */ function findOne(query) { - const TAG = `[Account Service # findOne ]:`; + const TAG = `[Account Service # findOne ]:`; - return Account.findOne( - query, - logger.queryCallbackFactory(TAG, "account", query) - ); + return Account.findOne( + query, + logger.queryCallbackFactory(TAG, "account", query) + ); } /** @@ -81,11 +81,11 @@ function findOne(query) { * @description Adds a new account to database. */ function addOneAccount(accountDetails) { - const TAG = `[Account Service # addOneAccount ]:`; + const TAG = `[Account Service # addOneAccount ]:`; - const account = new Account(accountDetails); + const account = new Account(accountDetails); - return account.save(); + return account.save(); } /** @@ -96,17 +96,17 @@ function addOneAccount(accountDetails) { * @description Changes account information to the specified information in accountDetails. */ function updateOne(id, accountDetails) { - const TAG = `[Account Service # updateOne ]:`; + const TAG = `[Account Service # updateOne ]:`; - const query = { - _id: id - }; + const query = { + _id: id + }; - return Account.findOneAndUpdate( - query, - accountDetails, - logger.updateCallbackFactory(TAG, "account") - ); + return Account.findOneAndUpdate( + query, + accountDetails, + logger.updateCallbackFactory(TAG, "account") + ); } /** @@ -115,19 +115,19 @@ function updateOne(id, accountDetails) { * @param {string} newPassword the new password for the account (in plain-text). */ function updatePassword(id, newPassword) { - const hashed = hashPassword(newPassword); - return updateOne(id, { - password: hashed - }); + const hashed = hashPassword(newPassword); + return updateOne(id, { + password: hashed + }); } module.exports = { - findOne: findOne, - findById: findById, - findByEmail: findByEmail, - addOneAccount: addOneAccount, - getAccountIfValid: getAccountIfValid, - hashPassword: hashPassword, - updateOne: updateOne, - updatePassword: updatePassword + findOne: findOne, + findById: findById, + findByEmail: findByEmail, + addOneAccount: addOneAccount, + getAccountIfValid: getAccountIfValid, + hashPassword: hashPassword, + updateOne: updateOne, + updatePassword: updatePassword }; diff --git a/services/hacker.service.js b/services/hacker.service.js index 54f35d46..1126d916 100644 --- a/services/hacker.service.js +++ b/services/hacker.service.js @@ -15,11 +15,11 @@ const QRCode = require("qrcode"); * @description Adds a new hacker to database. */ function createHacker(hackerDetails) { - const TAG = `[Hacker Service # createHacker]:`; + const TAG = `[Hacker Service # createHacker]:`; - const hacker = new Hacker(hackerDetails); + const hacker = new Hacker(hackerDetails); - return hacker.save(); + return hacker.save(); } /** @@ -30,17 +30,17 @@ function createHacker(hackerDetails) { * @description Update an account specified by its mongoId with information specified by hackerDetails. */ function updateOne(id, hackerDetails) { - const TAG = `[Hacker Service # update ]:`; + const TAG = `[Hacker Service # update ]:`; - const query = { - _id: id - }; + const query = { + _id: id + }; - return Hacker.findOneAndUpdate( - query, - hackerDetails, - logger.updateCallbackFactory(TAG, "hacker") - ); + return Hacker.findOneAndUpdate( + query, + hackerDetails, + logger.updateCallbackFactory(TAG, "hacker") + ); } /** @@ -50,9 +50,9 @@ function updateOne(id, hackerDetails) { * @description Finds an hacker by the id, which is the mongoId. */ function findById(id) { - const TAG = `[Hacker Service # findById ]:`; + const TAG = `[Hacker Service # findById ]:`; - return Hacker.findById(id, logger.queryCallbackFactory(TAG, "hacker", id)); + return Hacker.findById(id, logger.queryCallbackFactory(TAG, "hacker", id)); } /** @@ -63,18 +63,18 @@ function findById(id) { * @description Finds an hacker by some query. */ async function findIds(queries) { - const TAG = `[Hacker Service # findIds ]:`; - let ids = []; - - for (const query of queries) { - let currId = await Hacker.findOne( - query, - "_id", - logger.queryCallbackFactory(TAG, "hacker", query) - ); - ids.push(currId); - } - return ids; + const TAG = `[Hacker Service # findIds ]:`; + let ids = []; + + for (const query of queries) { + let currId = await Hacker.findOne( + query, + "_id", + logger.queryCallbackFactory(TAG, "hacker", query) + ); + ids.push(currId); + } + return ids; } /** @@ -83,28 +83,28 @@ async function findIds(queries) { * @return {DocumentQuery} A hacker document queried by accountId */ function findByAccountId(accountId) { - const TAG = `[ Hacker Service # findByAccountId ]:`; - const query = { - accountId: accountId - }; + const TAG = `[ Hacker Service # findByAccountId ]:`; + const query = { + accountId: accountId + }; - return Hacker.findOne(query, logger.updateCallbackFactory(TAG, "hacker")); + return Hacker.findOne(query, logger.updateCallbackFactory(TAG, "hacker")); } async function getStatsAllHackersCached() { - const TAG = `[ hacker Service # getStatsAll ]`; - if (cache.get(Constants.CACHE_KEY_STATS) !== null) { - logger.info(`${TAG} Getting cached stats`); - return cache.get(Constants.CACHE_KEY_STATS); - } - const allHackers = await Hacker.find( - {}, - logger.updateCallbackFactory(TAG, "hacker") - ).populate({ - path: "accountId" - }); - cache.put(Constants.CACHE_KEY_STATS, stats, Constants.CACHE_TIMEOUT_STATS); //set a time-out of 5 minutes - return getStats(allHackers); + const TAG = `[ hacker Service # getStatsAll ]`; + if (cache.get(Constants.CACHE_KEY_STATS) !== null) { + logger.info(`${TAG} Getting cached stats`); + return cache.get(Constants.CACHE_KEY_STATS); + } + const allHackers = await Hacker.find( + {}, + logger.updateCallbackFactory(TAG, "hacker") + ).populate({ + path: "accountId" + }); + cache.put(Constants.CACHE_KEY_STATS, stats, Constants.CACHE_TIMEOUT_STATS); //set a time-out of 5 minutes + return getStats(allHackers); } /** @@ -112,10 +112,10 @@ async function getStatsAllHackersCached() { * @param {string} str The string to be encoded in the QR code. */ async function generateQRCode(str) { - const response = await QRCode.toDataURL(str, { - scale: 4 - }); - return response; + const response = await QRCode.toDataURL(str, { + scale: 4 + }); + return response; } /** @@ -125,102 +125,103 @@ async function generateQRCode(str) { * @param {string} id The ID of the hacker to view */ function generateHackerViewLink(httpOrHttps, domain, id) { - const link = `${httpOrHttps}://${domain}/application/view/${id}`; - return link; + const link = `${httpOrHttps}://${domain}/application/view/${id}`; + return link; } function getStats(hackers) { - const TAG = `[ hacker Service # getStats ]`; - const stats = { - total: 0, - status: {}, - school: {}, - degree: {}, - gender: {}, - needsBus: {}, - ethnicity: {}, - jobInterest: {}, - fieldOfStudy: {}, - graduationYear: {}, - dietaryRestrictions: {}, - shirtSize: {}, - age: {} - }; - - hackers.forEach(hacker => { - if (!hacker.accountId) { - // user is no longer with us for some reason :( - return; - } - stats.total += 1; - stats.status[hacker.status] = stats.status[hacker.status] - ? stats.status[hacker.status] + 1 - : 1; - stats.school[hacker.application.general.school] = stats.school[ - hacker.application.general.school - ] - ? stats.school[hacker.application.general.school] + 1 - : 1; - stats.degree[hacker.application.general.degree] = stats.degree[ - hacker.application.general.degree - ] - ? stats.degree[hacker.application.general.degree] + 1 - : 1; - stats.gender[hacker.application.other.gender] = stats.gender[ - hacker.application.other.gender - ] - ? stats.gender[hacker.application.other.gender] + 1 - : 1; - stats.needsBus[hacker.application.accomodation.needsBus] = stats.needsBus[ - hacker.application.accomodation.needsBus - ] - ? stats.needsBus[hacker.application.accomodation.needsBus] + 1 - : 1; - - for (const ethnicity of hacker.application.other.ethnicity) { - stats.ethnicity[ethnicity] = stats.ethnicity[ethnicity] - ? stats.ethnicity[ethnicity] + 1 - : 1; - } - - stats.jobInterest[hacker.application.general.jobInterest] = stats - .jobInterest[hacker.application.general.jobInterest] - ? stats.jobInterest[hacker.application.general.jobInterest] + 1 - : 1; - stats.fieldOfStudy[hacker.application.general.fieldOfStudy] = stats - .fieldOfStudy[hacker.application.general.fieldOfStudy] - ? stats.fieldOfStudy[hacker.application.general.fieldOfStudy] + 1 - : 1; - stats.graduationYear[hacker.application.general.graduationYear] = stats - .graduationYear[hacker.application.general.graduationYear] - ? stats.graduationYear[hacker.application.general.graduationYear] + 1 - : 1; - - for (const dietaryRestrictions of hacker.accountId.dietaryRestrictions) { - stats.dietaryRestrictions[dietaryRestrictions] = stats - .dietaryRestrictions[dietaryRestrictions] - ? stats.dietaryRestrictions[dietaryRestrictions] + 1 - : 1; - } - stats.shirtSize[hacker.accountId.shirtSize] = stats.shirtSize[ - hacker.accountId.shirtSize - ] - ? stats.shirtSize[hacker.accountId.shirtSize] + 1 - : 1; - const age = hacker.accountId.getAge(); - stats.age[age] = stats.age[age] ? stats.age[age] + 1 : 1; - }); - return stats; + const TAG = `[ hacker Service # getStats ]`; + const stats = { + total: 0, + status: {}, + school: {}, + degree: {}, + gender: {}, + needsBus: {}, + ethnicity: {}, + jobInterest: {}, + fieldOfStudy: {}, + graduationYear: {}, + dietaryRestrictions: {}, + shirtSize: {}, + age: {} + }; + + hackers.forEach((hacker) => { + if (!hacker.accountId) { + // user is no longer with us for some reason :( + return; + } + stats.total += 1; + stats.status[hacker.status] = stats.status[hacker.status] + ? stats.status[hacker.status] + 1 + : 1; + stats.school[hacker.application.general.school] = stats.school[ + hacker.application.general.school + ] + ? stats.school[hacker.application.general.school] + 1 + : 1; + stats.degree[hacker.application.general.degree] = stats.degree[ + hacker.application.general.degree + ] + ? stats.degree[hacker.application.general.degree] + 1 + : 1; + stats.gender[hacker.application.other.gender] = stats.gender[ + hacker.application.other.gender + ] + ? stats.gender[hacker.application.other.gender] + 1 + : 1; + stats.needsBus[hacker.application.accomodation.needsBus] = stats + .needsBus[hacker.application.accomodation.needsBus] + ? stats.needsBus[hacker.application.accomodation.needsBus] + 1 + : 1; + + for (const ethnicity of hacker.application.other.ethnicity) { + stats.ethnicity[ethnicity] = stats.ethnicity[ethnicity] + ? stats.ethnicity[ethnicity] + 1 + : 1; + } + + stats.jobInterest[hacker.application.general.jobInterest] = stats + .jobInterest[hacker.application.general.jobInterest] + ? stats.jobInterest[hacker.application.general.jobInterest] + 1 + : 1; + stats.fieldOfStudy[hacker.application.general.fieldOfStudy] = stats + .fieldOfStudy[hacker.application.general.fieldOfStudy] + ? stats.fieldOfStudy[hacker.application.general.fieldOfStudy] + 1 + : 1; + stats.graduationYear[hacker.application.general.graduationYear] = stats + .graduationYear[hacker.application.general.graduationYear] + ? stats.graduationYear[hacker.application.general.graduationYear] + + 1 + : 1; + + for (const dietaryRestrictions of hacker.accountId + .dietaryRestrictions) { + stats.dietaryRestrictions[dietaryRestrictions] = stats + .dietaryRestrictions[dietaryRestrictions] + ? stats.dietaryRestrictions[dietaryRestrictions] + 1 + : 1; + } + stats.shirtSize[hacker.accountId.shirtSize] = stats.shirtSize[ + hacker.accountId.shirtSize + ] + ? stats.shirtSize[hacker.accountId.shirtSize] + 1 + : 1; + const age = hacker.accountId.getAge(); + stats.age[age] = stats.age[age] ? stats.age[age] + 1 : 1; + }); + return stats; } module.exports = { - createHacker: createHacker, - findById: findById, - updateOne: updateOne, - findIds: findIds, - findByAccountId: findByAccountId, - getStats: getStats, - getStatsAllHackersCached: getStatsAllHackersCached, - generateQRCode: generateQRCode, - generateHackerViewLink: generateHackerViewLink + createHacker: createHacker, + findById: findById, + updateOne: updateOne, + findIds: findIds, + findByAccountId: findByAccountId, + getStats: getStats, + getStatsAllHackersCached: getStatsAllHackersCached, + generateQRCode: generateQRCode, + generateHackerViewLink: generateHackerViewLink }; diff --git a/services/storage.service.js b/services/storage.service.js index c7614239..85cb9ce7 100644 --- a/services/storage.service.js +++ b/services/storage.service.js @@ -3,86 +3,85 @@ const GStorage = require("@google-cloud/storage"); const Logger = require("./logger.service"); class StorageService { - constructor() { - this.bucketName = process.env.BUCKET_NAME; - try { - this.storage = new GStorage.Storage(); - } catch (error) { - Logger.error(error); + constructor() { + this.bucketName = process.env.BUCKET_NAME; + try { + this.storage = new GStorage.Storage(); + } catch (error) { + Logger.error(error); + } + this.bucket = this.storage.bucket(this.bucketName); } - this.bucket = this.storage.bucket(this.bucketName); - } - /** - * Upload a file to storage. - * @param {{mimetype:string,buffer:Buffer}} file Multer file object - * @param {string} gcfilename the location in the bucket that you want the file stored. - * @returns {Promise} the address of the file that was uploaded - */ - upload(file, gcfilename) { - const blob = this.bucket.file(gcfilename); - const blobStream = blob.createWriteStream({ - metadata: { - contentType: file.mimetype - }, - resumable: false - }); - const _this = this; - return new Promise(function(resolve, reject) { - blobStream.on("finish", () => { - resolve(_this.getPublicUrl(gcfilename)); - }); - blobStream.on("error", reject); - //write the file data into the stream, and end it. - blobStream.end(file.buffer); - }); - } - /** - * Download file from storage. - * @param {string} filename path to file in bucket - * @returns {Promise<[Buffer]>} the file data that was returned - */ - download(filename) { - const file = this.bucket.file(filename); - return new Promise((resolve, reject) => { - file.exists().then(doesExist => { - if (doesExist) { - file - .download() - .then(resolve) - .catch(reject); - } else { - reject("file does not exist"); - } - }); - }); - } - /** - * Delete a file - * @param {*} filename the file that you want to delete - * @returns {Promise<[ApiResponse]>} - */ - delete(filename) { - const file = this.bucket.file(filename); - return file.delete(); - } + /** + * Upload a file to storage. + * @param {{mimetype:string,buffer:Buffer}} file Multer file object + * @param {string} gcfilename the location in the bucket that you want the file stored. + * @returns {Promise} the address of the file that was uploaded + */ + upload(file, gcfilename) { + const blob = this.bucket.file(gcfilename); + const blobStream = blob.createWriteStream({ + metadata: { + contentType: file.mimetype + }, + resumable: false + }); + const _this = this; + return new Promise(function(resolve, reject) { + blobStream.on("finish", () => { + resolve(_this.getPublicUrl(gcfilename)); + }); + blobStream.on("error", reject); + //write the file data into the stream, and end it. + blobStream.end(file.buffer); + }); + } + /** + * Download file from storage. + * @param {string} filename path to file in bucket + * @returns {Promise<[Buffer]>} the file data that was returned + */ + download(filename) { + const file = this.bucket.file(filename); + return new Promise((resolve, reject) => { + file.exists().then((doesExist) => { + if (doesExist) { + file.download() + .then(resolve) + .catch(reject); + } else { + reject("file does not exist"); + } + }); + }); + } + /** + * Delete a file + * @param {*} filename the file that you want to delete + * @returns {Promise<[ApiResponse]>} + */ + delete(filename) { + const file = this.bucket.file(filename); + return file.delete(); + } - /** - * - * @param {*} filename the file that you want to check exists - * @returns {Promise<[Boolean]>} - */ - exists(filename) { - const file = this.bucket.file(filename); - return file.exists(); - } - /** - * Get the public URL of the file - * @param {string} filename the path of the file - */ - getPublicUrl(filename) { - return `https://storage.googleapis.com/${this.bucket.name}/${filename}`; - } + /** + * + * @param {*} filename the file that you want to check exists + * @returns {Promise<[Boolean]>} + */ + exists(filename) { + const file = this.bucket.file(filename); + return file.exists(); + } + /** + * Get the public URL of the file + * @param {string} filename the path of the file + */ + getPublicUrl(filename) { + return `https://storage.googleapis.com/${this.bucket.name}/${filename}`; + } } module.exports = new StorageService(); diff --git a/tests/account.test.js b/tests/account.test.js index 58b063e4..947f4ea9 100644 --- a/tests/account.test.js +++ b/tests/account.test.js @@ -7,16 +7,16 @@ const logger = require("../services/logger.service"); const Account = require("../models/account.model"); const should = chai.should(); const Constants = { - Error: require("../constants/error.constant"), - General: require("../constants/general.constant"), - Success: require("../constants/success.constant") + Error: require("../constants/error.constant"), + General: require("../constants/general.constant"), + Success: require("../constants/success.constant") }; const util = { - account: require("./util/account.test.util"), - auth: require("./util/auth.test.util"), - accountConfirmation: require("./util/accountConfirmation.test.util"), - reset: require("./util/resetPassword.test.util") + account: require("./util/account.test.util"), + auth: require("./util/auth.test.util"), + accountConfirmation: require("./util/accountConfirmation.test.util"), + reset: require("./util/resetPassword.test.util") }; const agent = chai.request.agent(server.app); // tokens @@ -39,606 +39,626 @@ const storedAccount3 = util.account.NonConfirmedAccount3; const newAccount0 = util.account.unlinkedAccounts.new[0]; describe("GET user account", function() { - // fail on authentication - it("should FAIL to list the user's account on /api/account/self GET due to authentication", function(done) { - chai - .request(server.app) - .get("/api/account/self") - .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(); - }); - }); - - // fail due to invalid login - it("should FAIL due to invalid password", function(done) { - agent - .post("/api/auth/login") - .type("application/json") - .send({ - email: Admin0.email, - password: "FakePassword" - }) - .end((err, res) => { - res.should.have.status(401); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); - done(); - }); - }); + // fail on authentication + it("should FAIL to list the user's account on /api/account/self GET due to authentication", function(done) { + chai.request(server.app) + .get("/api/account/self") + .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(); + }); + }); - // success case - it("should list the user's account on /api/account/self GET", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return ( + // fail due to invalid login + it("should FAIL due to invalid password", function(done) { agent - .get("/api/account/self") - // does not have password because of to stripped json - .end(function(err, res) { - if (err) { - return done(err); + .post("/api/auth/login") + .type("application/json") + .send({ + email: Admin0.email, + password: "FakePassword" + }) + .end((err, res) => { + res.should.have.status(401); + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); + done(); + }); + }); + + // success case + it("should list the user's account on /api/account/self GET", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.ACCOUNT_READ); - res.body.should.have.property("data"); - res.body.data.should.be.a("object"); - res.body.data.should.have.property("firstName"); - res.body.data.should.have.property("lastName"); - res.body.data.should.have.property("email"); - res.body.data.should.have.property("dietaryRestrictions"); - res.body.data.should.have.property("shirtSize"); - done(); - }) - ); + return ( + agent + .get("/api/account/self") + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Success.ACCOUNT_READ + ); + res.body.should.have.property("data"); + res.body.data.should.be.a("object"); + res.body.data.should.have.property("firstName"); + res.body.data.should.have.property("lastName"); + res.body.data.should.have.property("email"); + res.body.data.should.have.property( + "dietaryRestrictions" + ); + res.body.data.should.have.property("shirtSize"); + done(); + }) + ); + }); }); - }); - // success case - admin case - it("should list another account specified by id using admin priviledge on /api/account/:id/ GET", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return ( - agent - .get(`/api/account/${teamHackerAccount0._id}`) - // does not have password because of to stripped json - .end(function(err, res) { - if (err) { - return done(err); + // success case - admin case + it("should list another account specified by id using admin priviledge on /api/account/:id/ GET", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.ACCOUNT_READ); - res.body.should.have.property("data"); + return ( + agent + .get(`/api/account/${teamHackerAccount0._id}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Success.ACCOUNT_READ + ); + res.body.should.have.property("data"); - // use acc.toStrippedJSON to deal with hidden passwords and convert _id to id - const acc = new Account(teamHackerAccount0); - chai.assert.equal( - JSON.stringify(res.body.data), - JSON.stringify(acc.toStrippedJSON()) + // use acc.toStrippedJSON to deal with hidden passwords and convert _id to id + const acc = new Account(teamHackerAccount0); + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(acc.toStrippedJSON()) + ); + done(); + }) ); - done(); - }) - ); + }); }); - }); - // success case - user case - it("should list an account specified by id on /api/account/:id/ GET", function(done) { - util.auth.login(agent, teamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return ( - agent - .get(`/api/account/${teamHackerAccount0._id}`) - // does not have password because of to stripped json - .end(function(err, res) { - if (err) { - return done(err); + // success case - user case + it("should list an account specified by id on /api/account/:id/ GET", function(done) { + util.auth.login(agent, teamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.ACCOUNT_READ); - res.body.should.have.property("data"); + return ( + agent + .get(`/api/account/${teamHackerAccount0._id}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Success.ACCOUNT_READ + ); + res.body.should.have.property("data"); - // use acc.toStrippedJSON to deal with hidden passwords and convert _id to id - const acc = new Account(teamHackerAccount0); - chai.assert.equal( - JSON.stringify(res.body.data), - JSON.stringify(acc.toStrippedJSON()) + // use acc.toStrippedJSON to deal with hidden passwords and convert _id to id + const acc = new Account(teamHackerAccount0); + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(acc.toStrippedJSON()) + ); + done(); + }) ); - done(); - }) - ); + }); }); - }); - // // fail case on authorization - it("should FAIL to list an account specified by id on /api/account/:id/ GET due to lack of authorization", function(done) { - util.auth.login(agent, teamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return ( - agent - .get(`/api/account/${Admin0._id}`) - // does not have password because of to stripped json - .end(function(err, res) { - if (err) { - return done(err); + // // fail case on authorization + it("should FAIL to list an account specified by id on /api/account/:id/ GET due to lack of authorization", function(done) { + util.auth.login(agent, teamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); } - 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"); + return ( + agent + .get(`/api/account/${Admin0._id}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); + } + 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(); - }) - ); + done(); + }) + ); + }); }); - }); }); describe("POST create account", function() { - it("should SUCCEED and create a new account", function(done) { - chai - .request(server.app) - .post(`/api/account/`) - .type("application/json") - .send(newAccount0) - .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.ACCOUNT_CREATE); + it("should SUCCEED and create a new account", function(done) { + chai.request(server.app) + .post(`/api/account/`) + .type("application/json") + .send(newAccount0) + .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.ACCOUNT_CREATE); - // use acc.toStrippedJSON to deal with hidden passwords and convert _id to id - const acc = new Account(newAccount0).toStrippedJSON(); - // delete id as those are generated - delete acc.id; - delete res.body.data.id; - chai.assert.equal(JSON.stringify(res.body.data), JSON.stringify(acc)); - done(); - }); - }); + // use acc.toStrippedJSON to deal with hidden passwords and convert _id to id + const acc = new Account(newAccount0).toStrippedJSON(); + // delete id as those are generated + delete acc.id; + delete res.body.data.id; + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(acc) + ); + done(); + }); + }); - it("should FAIL to create an account because the email is already in use", function(done) { - chai - .request(server.app) - .post(`/api/account/`) - .type("application/json") - .send(teamHackerAccount0) - .end(function(err, res) { - res.should.have.status(422); - done(); - }); - }); + it("should FAIL to create an account because the email is already in use", function(done) { + chai.request(server.app) + .post(`/api/account/`) + .type("application/json") + .send(teamHackerAccount0) + .end(function(err, res) { + res.should.have.status(422); + done(); + }); + }); }); describe("POST confirm account", function() { - it("should SUCCEED and confirm the account", function(done) { - chai - .request(server.app) - .post(`/api/auth/confirm/${confirmationToken}`) - .type("application/json") - .end(function(err, res) { - res.should.have.status(200); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.AUTH_CONFIRM_ACCOUNT); - done(); - }); - }); - it("should FAIL confirming the account", function(done) { - chai - .request(server.app) - .post(`/api/auth/confirm/${fakeToken}`) - .type("application/json") - .end(function(err, res) { - res.should.have.status(401); - res.body.should.have.property("message"); - res.body.message.should.equal( - Constants.Error.ACCOUNT_TOKEN_401_MESSAGE - ); - done(); - }); - }); - it("should FAIL to confirm account that has token with email but no account", function(done) { - chai - .request(server.app) - .post(`/api/auth/confirm/${fakeToken}`) - .type("application/json") - .end(function(err, res) { - res.should.have.status(401); - res.body.should.have.property("message"); - res.body.message.should.equal( - Constants.Error.ACCOUNT_TOKEN_401_MESSAGE - ); - done(); - }); - }); + it("should SUCCEED and confirm the account", function(done) { + chai.request(server.app) + .post(`/api/auth/confirm/${confirmationToken}`) + .type("application/json") + .end(function(err, res) { + res.should.have.status(200); + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Success.AUTH_CONFIRM_ACCOUNT + ); + done(); + }); + }); + it("should FAIL confirming the account", function(done) { + chai.request(server.app) + .post(`/api/auth/confirm/${fakeToken}`) + .type("application/json") + .end(function(err, res) { + res.should.have.status(401); + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Error.ACCOUNT_TOKEN_401_MESSAGE + ); + done(); + }); + }); + it("should FAIL to confirm account that has token with email but no account", function(done) { + chai.request(server.app) + .post(`/api/auth/confirm/${fakeToken}`) + .type("application/json") + .end(function(err, res) { + res.should.have.status(401); + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Error.ACCOUNT_TOKEN_401_MESSAGE + ); + done(); + }); + }); }); describe("PATCH update account", function() { - const updatedInfo = { - _id: teamHackerAccount0._id, - firstName: "new", - lastName: "name" - }; + const updatedInfo = { + _id: teamHackerAccount0._id, + firstName: "new", + lastName: "name" + }; - const failUpdatedInfo = { - _id: Admin0._id, - firstName: "fail", - lastName: "fail" - }; + const failUpdatedInfo = { + _id: Admin0._id, + firstName: "fail", + lastName: "fail" + }; - // fail on authentication - it("should FAIL to update an account due to authentication", function(done) { - chai - .request(server.app) - .patch(`/api/account/${updatedInfo._id}`) - .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(); - }); - }); + // fail on authentication + it("should FAIL to update an account due to authentication", function(done) { + chai.request(server.app) + .patch(`/api/account/${updatedInfo._id}`) + .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(); + }); + }); - // succeed on :all case - it("should SUCCEED and use admin to update another account", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - agent - .patch(`/api/account/${updatedInfo._id}`) - .type("application/json") - .send(updatedInfo) - .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.ACCOUNT_UPDATE); - res.body.should.have.property("data"); - // Is this correct matching of data? - res.body.data.firstName.should.equal(updatedInfo.firstName); - res.body.data.lastName.should.equal(updatedInfo.lastName); - done(); + // succeed on :all case + it("should SUCCEED and use admin to update another account", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + agent + .patch(`/api/account/${updatedInfo._id}`) + .type("application/json") + .send(updatedInfo) + .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.ACCOUNT_UPDATE + ); + res.body.should.have.property("data"); + // Is this correct matching of data? + res.body.data.firstName.should.equal(updatedInfo.firstName); + res.body.data.lastName.should.equal(updatedInfo.lastName); + done(); + }); }); }); - }); - // succeed on :self case - it("should SUCCEED and update the user's own account", function(done) { - util.auth.login(agent, teamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - agent - .patch(`/api/account/${updatedInfo._id}`) - .type("application/json") - .send(updatedInfo) - .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.ACCOUNT_UPDATE); - res.body.should.have.property("data"); - // Is this correct matching of data? - res.body.data.firstName.should.equal(updatedInfo.firstName); - res.body.data.lastName.should.equal(updatedInfo.lastName); - done(); + // succeed on :self case + it("should SUCCEED and update the user's own account", function(done) { + util.auth.login(agent, teamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + agent + .patch(`/api/account/${updatedInfo._id}`) + .type("application/json") + .send(updatedInfo) + .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.ACCOUNT_UPDATE + ); + res.body.should.have.property("data"); + // Is this correct matching of data? + res.body.data.firstName.should.equal(updatedInfo.firstName); + res.body.data.lastName.should.equal(updatedInfo.lastName); + done(); + }); }); }); - }); - // fail due to lack of authorization - it("should FAIL to update an account due to lack of authorization", function(done) { - util.auth.login(agent, teamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - agent - .patch(`/api/account/${failUpdatedInfo._id}`) - .type("application/json") - .send(updatedInfo) - .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"); + // fail due to lack of authorization + it("should FAIL to update an account due to lack of authorization", function(done) { + util.auth.login(agent, teamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + agent + .patch(`/api/account/${failUpdatedInfo._id}`) + .type("application/json") + .send(updatedInfo) + .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(); + done(); + }); }); }); - }); }); describe("POST reset password", function() { - const password = { - password: "NewPassword" - }; - it("should SUCCEED and change the password", function(done) { - chai - .request(server.app) - .post("/api/auth/password/reset") - .type("application/json") - .set("X-Reset-Token", resetToken) - .send(password) - .end(function(err, res) { - res.should.have.status(200); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.AUTH_RESET_PASSWORD); - done(); - }); - }); + const password = { + password: "NewPassword" + }; + it("should SUCCEED and change the password", function(done) { + chai.request(server.app) + .post("/api/auth/password/reset") + .type("application/json") + .set("X-Reset-Token", resetToken) + .send(password) + .end(function(err, res) { + res.should.have.status(200); + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Success.AUTH_RESET_PASSWORD + ); + done(); + }); + }); }); describe("PATCH change password for logged in user", function() { - const successChangePassword = { - oldPassword: Admin0.password, - newPassword: "password12345" - }; - const failChangePassword = { - oldPassword: "WrongPassword", - newPassword: "password12345" - }; - // fail on authentication - it("should FAIL to change the user's password because they are not logged in", function(done) { - chai - .request(server.app) - .patch("/api/auth/password/change") - .type("application/json") - .send(failChangePassword) - .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(); - }); - }); - // success case - it("should change the logged in user's password to a new password", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - agent - .patch("/api/auth/password/change") - .type("application/json") - .send(successChangePassword) - .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.AUTH_RESET_PASSWORD); - done(); + const successChangePassword = { + oldPassword: Admin0.password, + newPassword: "password12345" + }; + const failChangePassword = { + oldPassword: "WrongPassword", + newPassword: "password12345" + }; + // fail on authentication + it("should FAIL to change the user's password because they are not logged in", function(done) { + chai.request(server.app) + .patch("/api/auth/password/change") + .type("application/json") + .send(failChangePassword) + .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(); + }); + }); + // success case + it("should change the logged in user's password to a new password", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + agent + .patch("/api/auth/password/change") + .type("application/json") + .send(successChangePassword) + .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.AUTH_RESET_PASSWORD + ); + done(); + }); }); }); - }); - // fail case because old password in incorrect - it("should FAIL to change the logged in user's password to a new password because old password is incorrect", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - agent - .patch("/api/auth/password/change") - .type("application/json") - .send(failChangePassword) - .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(); + // fail case because old password in incorrect + it("should FAIL to change the logged in user's password to a new password because old password is incorrect", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + agent + .patch("/api/auth/password/change") + .type("application/json") + .send(failChangePassword) + .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(); + }); }); }); - }); }); describe("GET retrieve permissions", function() { - it("should SUCCEED and retrieve the rolebindings for the user", function(done) { - util.auth.login(agent, teamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - agent - .get("/api/auth/rolebindings/" + teamHackerAccount0._id) - .type("application/json") - .end(function(err, res) { - res.should.have.status(200); - res.body.should.have.property("message"); - res.body.message.should.equal( - Constants.Success.AUTH_GET_ROLE_BINDINGS - ); - res.body.should.have.property("data"); - res.body.data.should.be.a("object"); - res.body.data.should.have.property("roles"); - res.body.data.should.have.property("accountId"); - res.body.data.accountId.should.equal( - teamHackerAccount0._id.toHexString() - ); - done(); + it("should SUCCEED and retrieve the rolebindings for the user", function(done) { + util.auth.login(agent, teamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + agent + .get("/api/auth/rolebindings/" + teamHackerAccount0._id) + .type("application/json") + .end(function(err, res) { + res.should.have.status(200); + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Success.AUTH_GET_ROLE_BINDINGS + ); + res.body.should.have.property("data"); + res.body.data.should.be.a("object"); + res.body.data.should.have.property("roles"); + res.body.data.should.have.property("accountId"); + res.body.data.accountId.should.equal( + teamHackerAccount0._id.toHexString() + ); + done(); + }); }); }); - }); - it("should FAIL to retrieve the rolebindings as the account is not authenticated", function(done) { - chai - .request(server.app) - .get("/api/auth/rolebindings/" + teamHackerAccount0._id) - .type("application/json") - .end(function(err, res) { - res.should.have.status(401); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); - done(); - }); - }); + it("should FAIL to retrieve the rolebindings as the account is not authenticated", function(done) { + chai.request(server.app) + .get("/api/auth/rolebindings/" + teamHackerAccount0._id) + .type("application/json") + .end(function(err, res) { + res.should.have.status(401); + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); + done(); + }); + }); }); describe("GET resend confirmation email", function() { - it("should SUCCEED and resend the confirmation email", function(done) { - util.auth.login(agent, storedAccount1, error => { - if (error) { - agent.close(); - return done(error); - } - agent - .get("/api/auth/confirm/resend") - .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.AUTH_SEND_CONFIRMATION_EMAIL - ); - done(); + it("should SUCCEED and resend the confirmation email", function(done) { + util.auth.login(agent, storedAccount1, (error) => { + if (error) { + agent.close(); + return done(error); + } + agent + .get("/api/auth/confirm/resend") + .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.AUTH_SEND_CONFIRMATION_EMAIL + ); + done(); + }); }); }); - }); - it("should FAIL as the account is already confirmed", function(done) { - util.auth.login(agent, teamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - agent - .get("/api/auth/confirm/resend") - .type("application/json") - .end(function(err, res) { - res.should.have.status(422); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal("Account already confirmed"); - done(); + it("should FAIL as the account is already confirmed", function(done) { + util.auth.login(agent, teamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + agent + .get("/api/auth/confirm/resend") + .type("application/json") + .end(function(err, res) { + res.should.have.status(422); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal("Account already confirmed"); + done(); + }); }); }); - }); - it("should FAIL as account confirmation token does not exist", function(done) { - util.auth.login(agent, storedAccount3, error => { - if (error) { - agent.close(); - return done(error); - } - agent - .get("/api/auth/confirm/resend") - .type("application/json") - .end(function(err, res) { - res.should.have.status(428); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal( - "Account confirmation token does not exist" - ); - done(); + it("should FAIL as account confirmation token does not exist", function(done) { + util.auth.login(agent, storedAccount3, (error) => { + if (error) { + agent.close(); + return done(error); + } + agent + .get("/api/auth/confirm/resend") + .type("application/json") + .end(function(err, res) { + res.should.have.status(428); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + "Account confirmation token does not exist" + ); + done(); + }); }); }); - }); }); describe("POST invite account", function() { - it("Should succeed to invite a user to create an account", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return ( - agent - .post("/api/account/invite") - .type("application/json") - .send({ - email: newAccount0.email, - accountType: Constants.General.VOLUNTEER - }) - // does not have password because of to stripped json - .end(function(err, res) { - if (err) { - return done(err); + it("Should succeed to invite a user to create an account", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); } - res.should.have.status(200); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.ACCOUNT_INVITE); - done(); - }) - ); + return ( + agent + .post("/api/account/invite") + .type("application/json") + .send({ + email: newAccount0.email, + accountType: Constants.General.VOLUNTEER + }) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(200); + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Success.ACCOUNT_INVITE + ); + done(); + }) + ); + }); }); - }); }); describe("GET invites", function() { - it("Should FAIL to get all invites due to Authentication", function(done) { - chai - .request(server.app) - .get("/api/account/invite") - .end(function(err, res) { - res.should.have.status(401); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); - done(); - }); - }); - it("Should FAIL to get all invites due to Authorization", function(done) { - util.auth.login(agent, teamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent.get("/api/account/invite").end(function(err, res) { - res.should.have.status(403); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.AUTH_403_MESSAGE); - done(); - }); + it("Should FAIL to get all invites due to Authentication", function(done) { + chai.request(server.app) + .get("/api/account/invite") + .end(function(err, res) { + res.should.have.status(401); + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); + done(); + }); }); - }); - it("Should SUCCEED to get all invites", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent.get("/api/account/invite").end(function(err, res) { - if (err) { - return done(err); - } - res.should.have.status(200); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.ACCOUNT_GET_INVITES); - res.body.should.have.property("data"); - res.body.data.should.have.property("invites"); - res.body.data.invites.length.should.equal( - util.accountConfirmation.AccountConfirmationTokens.length - ); - done(); - }); + it("Should FAIL to get all invites due to Authorization", function(done) { + util.auth.login(agent, teamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent.get("/api/account/invite").end(function(err, res) { + res.should.have.status(403); + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Error.AUTH_403_MESSAGE); + done(); + }); + }); + }); + it("Should SUCCEED to get all invites", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent.get("/api/account/invite").end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(200); + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Success.ACCOUNT_GET_INVITES + ); + res.body.should.have.property("data"); + res.body.data.should.have.property("invites"); + res.body.data.invites.length.should.equal( + util.accountConfirmation.AccountConfirmationTokens.length + ); + done(); + }); + }); }); - }); }); diff --git a/tests/hacker.test.js b/tests/hacker.test.js index 146eef24..fed0ffcf 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -9,16 +9,16 @@ const Hacker = require("../models/hacker.model"); const fs = require("fs"); const path = require("path"); const Constants = { - Success: require("../constants/success.constant"), - General: require("../constants/general.constant"), - Error: require("../constants/error.constant") + Success: require("../constants/success.constant"), + General: require("../constants/general.constant"), + Error: require("../constants/error.constant") }; const util = { - auth: require("./util/auth.test.util"), - hacker: require("./util/hacker.test.util"), - account: require("./util/account.test.util"), - accountConfirmation: require("./util/accountConfirmation.test.util") + auth: require("./util/auth.test.util"), + hacker: require("./util/hacker.test.util"), + account: require("./util/account.test.util"), + accountConfirmation: require("./util/accountConfirmation.test.util") }; const StorageService = require("../services/storage.service"); @@ -42,7 +42,7 @@ const TeamHacker1 = util.hacker.TeamHacker1; const duplicateAccountLinkHacker0 = util.hacker.duplicateAccountLinkHacker0; const unconfirmedHackerAccount1 = - util.account.hackerAccounts.stored.unconfirmed[0]; + util.account.hackerAccounts.stored.unconfirmed[0]; const unconfirmedHackerAccount0 = util.hacker.unconfirmedAccountHacker0; const unconfirmedHacker1 = util.hacker.unconfirmedAccountHacker1; @@ -50,1087 +50,1150 @@ const unconfirmedHacker1 = util.hacker.unconfirmedAccountHacker1; const invalidHacker1 = util.hacker.invalidHacker1; describe("GET hacker", function() { - // fail on authentication - it("should FAIL to list a hacker's information on /api/hacker/:id GET due to authentication", function(done) { - chai - .request(server.app) - .get(`/api/hacker/` + TeamHacker0._id) - .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(); - }); - }); - - // success case - it("should list the user's hacker info on /api/hacker/self GET", function(done) { - util.auth.login(agent, teamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent.get("/api/hacker/self").end(function(err, res) { - if (err) { - return done(err); - } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.HACKER_READ); - res.body.should.have.property("data"); - - let hacker = new Hacker(TeamHacker0); - chai.assert.equal( - JSON.stringify(res.body.data), - JSON.stringify(hacker.toJSON()) - ); - done(); - }); - }); - }); - - // fail case due to wrong account type - it("should FAIL to list the hacker info of an admin due to wrong account type /api/account/self GET", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent.get("/api/hacker/self").end(function(err, res) { - res.should.have.status(409); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.ACCOUNT_TYPE_409_MESSAGE); - done(); - }); + // fail on authentication + it("should FAIL to list a hacker's information on /api/hacker/:id GET due to authentication", function(done) { + chai.request(server.app) + .get(`/api/hacker/` + TeamHacker0._id) + .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(); + }); }); - }); - - // succeed on admin case - it("should list a hacker's information using admin power on /api/hacker/:id GET", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return ( - agent - .get(`/api/hacker/${TeamHacker0._id}`) - // does not have password because of to stripped json - .end(function(err, res) { - if (err) { - return done(err); - } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.HACKER_READ); - res.body.should.have.property("data"); - - let hacker = new Hacker(TeamHacker0); - chai.assert.equal( - JSON.stringify(res.body.data), - JSON.stringify(hacker.toJSON()) - ); - done(); - }) - ); - }); - }); - - // succeed on :self case - it("should list the user's hacker information on /api/hacker/:id GET", function(done) { - util.auth.login(agent, teamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return ( - agent - .get(`/api/hacker/${TeamHacker0._id}`) - // does not have password because of to stripped json - .end(function(err, res) { - if (err) { - return done(err); + // success case + it("should list the user's hacker info on /api/hacker/self GET", function(done) { + util.auth.login(agent, teamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.HACKER_READ); - res.body.should.have.property("data"); + return agent.get("/api/hacker/self").end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal(Constants.Success.HACKER_READ); + res.body.should.have.property("data"); + + let hacker = new Hacker(TeamHacker0); + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(hacker.toJSON()) + ); + done(); + }); + }); + }); - let hacker = new Hacker(TeamHacker0); + // fail case due to wrong account type + it("should FAIL to list the hacker info of an admin due to wrong account type /api/account/self GET", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent.get("/api/hacker/self").end(function(err, res) { + res.should.have.status(409); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Error.ACCOUNT_TYPE_409_MESSAGE + ); + done(); + }); + }); + }); - chai.assert.equal( - JSON.stringify(res.body.data), - JSON.stringify(hacker.toJSON()) + // succeed on admin case + it("should list a hacker's information using admin power on /api/hacker/:id GET", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .get(`/api/hacker/${TeamHacker0._id}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Success.HACKER_READ + ); + res.body.should.have.property("data"); + + let hacker = new Hacker(TeamHacker0); + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(hacker.toJSON()) + ); + + done(); + }) ); - - done(); - }) - ); + }); }); - }); - - // fail due to lack of authorization - it("should FAIL to list a hacker information due to lack of authorization on /api/hacker/:id GET", function(done) { - util.auth.login(agent, noTeamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return ( - agent - .get(`/api/hacker/${TeamHacker0._id}`) - // does not have password because of to stripped json - .end(function(err, res) { - if (err) { - return done(err); + + // succeed on :self case + it("should list the user's hacker information on /api/hacker/:id GET", function(done) { + util.auth.login(agent, teamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); } - 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(); - }) - ); - }); - }); - - // fail due to lack of hacker - it("should FAIL to list an invalid hacker /api/hacker/:id GET", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get(`/api/hacker/${invalidHacker1._id}`) - .end(function(err, res) { - if (err) { - return done(err); - } - res.should.have.status(404); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.HACKER_404_MESSAGE); - res.body.should.have.property("data"); - - done(); + return ( + agent + .get(`/api/hacker/${TeamHacker0._id}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Success.HACKER_READ + ); + res.body.should.have.property("data"); + + let hacker = new Hacker(TeamHacker0); + + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(hacker.toJSON()) + ); + + done(); + }) + ); }); }); - }); - - // succeed on admin case - it("should list a hacker's information using admin power on /api/hacker/email/:email GET", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return ( - agent - .get(`/api/hacker/email/${teamHackerAccount0.email}`) - // does not have password because of to stripped json - .end(function(err, res) { - if (err) { - return done(err); + + // fail due to lack of authorization + it("should FAIL to list a hacker information due to lack of authorization on /api/hacker/:id GET", function(done) { + util.auth.login(agent, noTeamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.HACKER_READ); - res.body.should.have.property("data"); - - let hacker = new Hacker(TeamHacker0); - chai.assert.equal( - JSON.stringify(res.body.data), - JSON.stringify(hacker.toJSON()) + return ( + agent + .get(`/api/hacker/${TeamHacker0._id}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); + } + 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(); + }) ); - - done(); - }) - ); + }); }); - }); - - // succeed on :self case - it("should list the user's hacker information on /api/hacker/email/:email GET", function(done) { - util.auth.login(agent, teamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return ( - agent - .get(`/api/hacker/email/${teamHackerAccount0.email}`) - // does not have password because of to stripped json - .end(function(err, res) { - if (err) { - return done(err); - } - res.should.have.status(200); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.HACKER_READ); - res.body.should.have.property("data"); - let hacker = new Hacker(TeamHacker0); + // fail due to lack of hacker + it("should FAIL to list an invalid hacker /api/hacker/:id GET", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get(`/api/hacker/${invalidHacker1._id}`) + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(404); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Error.HACKER_404_MESSAGE + ); + res.body.should.have.property("data"); + + done(); + }); + }); + }); - chai.assert.equal( - JSON.stringify(res.body.data), - JSON.stringify(hacker.toJSON()) + // succeed on admin case + it("should list a hacker's information using admin power on /api/hacker/email/:email GET", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .get(`/api/hacker/email/${teamHackerAccount0.email}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Success.HACKER_READ + ); + res.body.should.have.property("data"); + + let hacker = new Hacker(TeamHacker0); + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(hacker.toJSON()) + ); + + done(); + }) ); + }); + }); - done(); - }) - ); + // succeed on :self case + it("should list the user's hacker information on /api/hacker/email/:email GET", function(done) { + util.auth.login(agent, teamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .get(`/api/hacker/email/${teamHackerAccount0.email}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Success.HACKER_READ + ); + res.body.should.have.property("data"); + + let hacker = new Hacker(TeamHacker0); + + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(hacker.toJSON()) + ); + + done(); + }) + ); + }); }); - }); - - // fail due to lack of authorization - it("should FAIL to list a hacker information due to lack of authorization on /api/hacker/email/:id GET", function(done) { - util.auth.login(agent, noTeamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return ( - agent - .get(`/api/hacker/email/${teamHackerAccount0.email}`) - // does not have password because of to stripped json - .end(function(err, res) { - if (err) { - return done(err); + + // fail due to lack of authorization + it("should FAIL to list a hacker information due to lack of authorization on /api/hacker/email/:id GET", function(done) { + util.auth.login(agent, noTeamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); } - 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(); - }) - ); + return ( + agent + .get(`/api/hacker/email/${teamHackerAccount0.email}`) + // does not have password because of to stripped json + .end(function(err, res) { + if (err) { + return done(err); + } + 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(); + }) + ); + }); }); - }); }); describe("POST create hacker", function() { - // fail on authentication - it("should FAIL to create a new hacker due to lack of authentication", function(done) { - chai - .request(server.app) - .post(`/api/hacker/`) - .type("application/json") - .send(newHacker1) - .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(); - }); - }); - - // succeed on admin case - it("should SUCCEED and create a new hacker (with an account that has been confirmed) using admin credentials", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - - return done(error); - } - return agent - .post(`/api/hacker/`) - .type("application/json") - .send(newHacker0) - .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_CREATE); - res.body.should.have.property("data"); - - // create JSON version of model - // delete id as they will be different between model objects - // update status to be applied on the comparator hacker object - const hacker = new Hacker(newHacker0).toJSON(); - hacker.status = Constants.General.HACKER_STATUS_APPLIED; - delete res.body.data.id; - delete hacker.id; - chai.assert.equal( - JSON.stringify(res.body.data), - JSON.stringify(hacker), - "objects do not match" - ); - - done(); + // fail on authentication + it("should FAIL to create a new hacker due to lack of authentication", function(done) { + chai.request(server.app) + .post(`/api/hacker/`) + .type("application/json") + .send(newHacker1) + .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(); + }); + }); + + // succeed on admin case + it("should SUCCEED and create a new hacker (with an account that has been confirmed) using admin credentials", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + + return done(error); + } + return agent + .post(`/api/hacker/`) + .type("application/json") + .send(newHacker0) + .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_CREATE + ); + res.body.should.have.property("data"); + + // create JSON version of model + // delete id as they will be different between model objects + // update status to be applied on the comparator hacker object + const hacker = new Hacker(newHacker0).toJSON(); + hacker.status = Constants.General.HACKER_STATUS_APPLIED; + delete res.body.data.id; + delete hacker.id; + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(hacker), + "objects do not match" + ); + + done(); + }); }); }); - }); - - // succeed on user case - it("should SUCCEED and create a new hacker for user (with an account that has been confirmed)", function(done) { - util.auth.login(agent, newHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .post(`/api/hacker/`) - .type("application/json") - .send(newHacker0) - .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_CREATE); - res.body.should.have.property("data"); - - // create JSON version of model - // delete id as they will be different between model objects - // update status to be applied on the comparator hacker object - const hacker = new Hacker(newHacker0).toJSON(); - hacker.status = Constants.General.HACKER_STATUS_APPLIED; - delete res.body.data.id; - delete hacker.id; - chai.assert.equal( - JSON.stringify(res.body.data), - JSON.stringify(hacker) - ); - done(); + + // succeed on user case + it("should SUCCEED and create a new hacker for user (with an account that has been confirmed)", function(done) { + util.auth.login(agent, newHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .post(`/api/hacker/`) + .type("application/json") + .send(newHacker0) + .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_CREATE + ); + res.body.should.have.property("data"); + + // create JSON version of model + // delete id as they will be different between model objects + // update status to be applied on the comparator hacker object + const hacker = new Hacker(newHacker0).toJSON(); + hacker.status = Constants.General.HACKER_STATUS_APPLIED; + delete res.body.data.id; + delete hacker.id; + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify(hacker) + ); + done(); + }); }); }); - }); - - // should fail due to 'false' on code of conduct - it("should FAIL if the new hacker does not accept code of conduct", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .post(`/api/hacker/`) - .type("application/json") - .send(invalidHacker0) - .end(function(err, res) { - res.should.have.status(422); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal("Validation failed"); - res.body.should.have.property("data"); - res.body.data.should.have.property("application"); - res.body.data.application.should.have.property("other"); - res.body.data.application.other.should.have.property( - "codeOfConduct_MLH" - ); - res.body.data.application.other.should.have.property( - "codeOfConduct_MCHACKS" - ); - res.body.data.application.other.codeOfConduct_MLH.msg.should.equal( - "Must be equal to true" - ); - res.body.data.application.other.codeOfConduct_MCHACKS.msh.should.equal( - "Must be equal to true" - ); - done(); + + // should fail due to 'false' on code of conduct + it("should FAIL if the new hacker does not accept code of conduct", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .post(`/api/hacker/`) + .type("application/json") + .send(invalidHacker0) + .end(function(err, res) { + res.should.have.status(422); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal("Validation failed"); + res.body.should.have.property("data"); + res.body.data.should.have.property("application"); + res.body.data.application.should.have.property("other"); + res.body.data.application.other.should.have.property( + "codeOfConduct_MLH" + ); + res.body.data.application.other.should.have.property( + "codeOfConduct_MCHACKS" + ); + res.body.data.application.other.codeOfConduct_MLH.msg.should.equal( + "Must be equal to true" + ); + res.body.data.application.other.codeOfConduct_MCHACKS.msh.should.equal( + "Must be equal to true" + ); + done(); + }); }); }); - }); - - // fail on unconfirmed account, using admin - it("should FAIL to create a new hacker if the account hasn't been confirmed", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .post(`/api/hacker/`) - .type("application/json") - .send(util.hacker.unconfirmedAccountHacker0) - .end(function(err, res) { - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.ACCOUNT_403_MESSAGE); - res.should.have.status(403); - done(); + + // fail on unconfirmed account, using admin + it("should FAIL to create a new hacker if the account hasn't been confirmed", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .post(`/api/hacker/`) + .type("application/json") + .send(util.hacker.unconfirmedAccountHacker0) + .end(function(err, res) { + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Error.ACCOUNT_403_MESSAGE + ); + res.should.have.status(403); + done(); + }); }); }); - }); - - // fail due to duplicate accountId - it("should FAIL to create new hacker due to duplicate account link", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .post(`/api/hacker/`) - .type("application/json") - .send(duplicateAccountLinkHacker0) - .end(function(err, res) { - res.should.have.status(409); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.HACKER_ID_409_MESSAGE); - res.body.should.have.property("data"); - done(); + + // fail due to duplicate accountId + it("should FAIL to create new hacker due to duplicate account link", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .post(`/api/hacker/`) + .type("application/json") + .send(duplicateAccountLinkHacker0) + .end(function(err, res) { + res.should.have.status(409); + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Error.HACKER_ID_409_MESSAGE + ); + res.body.should.have.property("data"); + done(); + }); }); }); - }); - - // fail on invalid input - it("should FAIL to create new hacker due to invalid input", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .post(`/api/hacker/`) - .type("application/json") - .send(invalidHacker1) - .end(function(err, res) { - // replace with actual test comparisons after error handler is implemented - res.should.have.status(422); - done(); + + // fail on invalid input + it("should FAIL to create new hacker due to invalid input", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .post(`/api/hacker/`) + .type("application/json") + .send(invalidHacker1) + .end(function(err, res) { + // replace with actual test comparisons after error handler is implemented + res.should.have.status(422); + done(); + }); }); }); - }); }); describe("PATCH update one hacker", function() { - // fail on authentication - it("should FAIL to update a hacker on /api/hacker/:id GET due to authentication", function(done) { - chai - .request(server.app) - .patch(`/api/hacker/${TeamHacker0._id}`) - .type("application/json") - .send({ - gender: "Other" - }) - .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 succeed on admin case - it("should SUCCEED and update a hacker using admin power", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - let app = TeamHacker0.application; - app.other.gender = "Other"; - return agent - .patch(`/api/hacker/${TeamHacker0._id}`) - .type("application/json") - .send({ - application: app - }) - .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( - JSON.stringify(res.body.data.application.other.gender), - '"Other"' - ); - done(); + // fail on authentication + it("should FAIL to update a hacker on /api/hacker/:id GET due to authentication", function(done) { + chai.request(server.app) + .patch(`/api/hacker/${TeamHacker0._id}`) + .type("application/json") + .send({ + gender: "Other" + }) + .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 succeed on admin case + it("should SUCCEED and update a hacker using admin power", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + let app = TeamHacker0.application; + app.other.gender = "Other"; + return agent + .patch(`/api/hacker/${TeamHacker0._id}`) + .type("application/json") + .send({ + application: app + }) + .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( + JSON.stringify(res.body.data.application.other.gender), + '"Other"' + ); + done(); + }); }); }); - }); - - it("should SUCCEED and update a hacker STATUS as an Admin", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/status/${TeamHacker0._id}`) - .type("application/json") - .send({ - status: "Accepted" - }) - .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( - JSON.stringify(res.body.data), - JSON.stringify({ - status: "Accepted" - }) - ); - done(); + + it("should SUCCEED and update a hacker STATUS as an Admin", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/status/${TeamHacker0._id}`) + .type("application/json") + .send({ + status: "Accepted" + }) + .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( + JSON.stringify(res.body.data), + JSON.stringify({ + status: "Accepted" + }) + ); + done(); + }); }); }); - }); - - it("should FAIL and NOT update a hacker STATUS as a Hacker", function(done) { - util.auth.login(agent, teamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/status/${TeamHacker0._id}`) - .type("application/json") - .send({ - status: "Accepted" - }) - .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 and NOT update a hacker STATUS as a Hacker", function(done) { + util.auth.login(agent, teamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/status/${TeamHacker0._id}`) + .type("application/json") + .send({ + status: "Accepted" + }) + .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(); + }); }); }); - }); - - // volunteer should successfully checkin hacker - it("should SUCCEED and check in hacker as a volunteer", function(done) { - util.auth.login(agent, volunteerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/checkin/${TeamHacker0._id}`) - .type("application/json") - .send({ - status: "Checked-in" - }) - .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( - JSON.stringify(res.body.data), - JSON.stringify({ - status: "Checked-in" - }) - ); - done(); + + // volunteer should successfully checkin hacker + it("should SUCCEED and check in hacker as a volunteer", function(done) { + util.auth.login(agent, volunteerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/checkin/${TeamHacker0._id}`) + .type("application/json") + .send({ + status: "Checked-in" + }) + .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( + JSON.stringify(res.body.data), + JSON.stringify({ + status: "Checked-in" + }) + ); + done(); + }); }); }); - }); - - // hacker should fail to checkin hacker - it("should FAIL to check in hacker as a hacker", function(done) { - util.auth.login(agent, teamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/checkin/${TeamHacker0._id}`) - .type("application/json") - .send({ - status: "Checked-in" - }) - .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(); + + // hacker should fail to checkin hacker + it("should FAIL to check in hacker as a hacker", function(done) { + util.auth.login(agent, teamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/checkin/${TeamHacker0._id}`) + .type("application/json") + .send({ + status: "Checked-in" + }) + .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(); + }); }); }); - }); - - // should succeed on hacker case - it("should SUCCEED and update the user's hacker info", function(done) { - util.auth.login(agent, noTeamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - let app = noTeamHacker0.application; - app.other.gender = "Other"; - return agent - .patch(`/api/hacker/${noTeamHacker0._id}`) - .type("application/json") - .send({ - application: app - }) - .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( - JSON.stringify(res.body.data.application.other.gender), - '"Other"' - ); - done(); + + // should succeed on hacker case + it("should SUCCEED and update the user's hacker info", function(done) { + util.auth.login(agent, noTeamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + let app = noTeamHacker0.application; + app.other.gender = "Other"; + return agent + .patch(`/api/hacker/${noTeamHacker0._id}`) + .type("application/json") + .send({ + application: app + }) + .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( + JSON.stringify(res.body.data.application.other.gender), + '"Other"' + ); + done(); + }); }); }); - }); - - // should fail due to authorization - it("should Fail to update hacker info due to lack of authorization", function(done) { - util.auth.login(agent, noTeamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/${TeamHacker0._id}`) - .type("application/json") - .send({ - gender: "Other" - }) - .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(); + + // should fail due to authorization + it("should Fail to update hacker info due to lack of authorization", function(done) { + util.auth.login(agent, noTeamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/${TeamHacker0._id}`) + .type("application/json") + .send({ + gender: "Other" + }) + .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(); + }); }); }); - }); - - // fail due to lack of hacker - it("should fail to change an invalid hacker's info", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get(`/api/hacker/${invalidHacker1._id}`) - .end(function(err, res) { - if (err) { - return done(err); - } - res.should.have.status(404); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Error.HACKER_404_MESSAGE); - res.body.should.have.property("data"); - - done(); + + // fail due to lack of hacker + it("should fail to change an invalid hacker's info", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get(`/api/hacker/${invalidHacker1._id}`) + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(404); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Error.HACKER_404_MESSAGE + ); + res.body.should.have.property("data"); + + done(); + }); }); }); - }); - - // Succeed and change accepted to confirm - it("should succeed for hacker to update their own status from accepted to confirmed", function(done) { - util.auth.login(agent, noTeamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/confirmation/${noTeamHacker0._id}`) - .type("application/json") - .send({ - confirm: true - }) - .end(function(err, res) { - if (err) { - return done(err); - } - 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( - JSON.stringify(res.body.data), - JSON.stringify({ - status: Constants.General.HACKER_STATUS_CONFIRMED - }) - ); - done(); + // Succeed and change accepted to confirm + it("should succeed for hacker to update their own status from accepted to confirmed", function(done) { + util.auth.login(agent, noTeamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/confirmation/${noTeamHacker0._id}`) + .type("application/json") + .send({ + confirm: true + }) + .end(function(err, res) { + if (err) { + return done(err); + } + 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( + JSON.stringify(res.body.data), + JSON.stringify({ + status: Constants.General.HACKER_STATUS_CONFIRMED + }) + ); + + done(); + }); }); }); - }); - - // Succeed and change confirmed to accepted - it("should succeed for hacker to update their own status from confirmed to accepted", function(done) { - util.auth.login(agent, teamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/confirmation/${TeamHacker0._id}`) - .type("application/json") - .send({ - confirm: false - }) - .end(function(err, res) { - if (err) { - return done(err); - } - 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( - JSON.stringify(res.body.data), - JSON.stringify({ - status: Constants.General.HACKER_STATUS_WITHDRAWN - }) - ); - done(); + // Succeed and change confirmed to accepted + it("should succeed for hacker to update their own status from confirmed to accepted", function(done) { + util.auth.login(agent, teamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/confirmation/${TeamHacker0._id}`) + .type("application/json") + .send({ + confirm: false + }) + .end(function(err, res) { + if (err) { + return done(err); + } + 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( + JSON.stringify(res.body.data), + JSON.stringify({ + status: Constants.General.HACKER_STATUS_WITHDRAWN + }) + ); + + done(); + }); }); }); - }); - - // fail for a hacker that's not accepted - it("should FAIL to update hacker status when hacker status is not accepted or confirmed", function(done) { - util.auth.login(agent, util.account.waitlistedHacker0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/confirmation/${util.hacker.waitlistedHacker0._id}`) - .type("application/json") - .send({ - confirm: true - }) - .end(function(err, res) { - if (err) { - return done(err); - } - res.should.have.status(409); - res.should.be.json; - res.body.should.have.property("message"); - res.body.message.should.equal( - Constants.Error.HACKER_STATUS_409_MESSAGE - ); - res.body.should.have.property("data"); - - done(); + + // fail for a hacker that's not accepted + it("should FAIL to update hacker status when hacker status is not accepted or confirmed", function(done) { + util.auth.login(agent, util.account.waitlistedHacker0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch( + `/api/hacker/confirmation/${util.hacker.waitlistedHacker0._id}` + ) + .type("application/json") + .send({ + confirm: true + }) + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(409); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Error.HACKER_STATUS_409_MESSAGE + ); + res.body.should.have.property("data"); + + done(); + }); }); }); - }); - - // fail for a hacker that's not accepted - it("should fail for hacker trying to confirm someone else", function(done) { - util.auth.login(agent, util.account.waitlistedHacker0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .patch(`/api/hacker/confirmation/${noTeamHacker0._id}`) - .type("application/json") - .send({ - confirm: true - }) - .end(function(err, res) { - if (err) { - return done(err); - } - 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(); + + // fail for a hacker that's not accepted + it("should fail for hacker trying to confirm someone else", function(done) { + util.auth.login(agent, util.account.waitlistedHacker0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/confirmation/${noTeamHacker0._id}`) + .type("application/json") + .send({ + confirm: true + }) + .end(function(err, res) { + if (err) { + return done(err); + } + 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(); + }); }); }); - }); }); describe("POST add a hacker resume", function() { - it("It should SUCCEED and upload a resume for a hacker", function(done) { - //this takes a lot of time for some reason - util.auth.login(agent, noTeamHacker0, error => { - if (error) { - return done(error); - } - return agent - .post(`/api/hacker/resume/${noTeamHacker0._id}`) - .type("multipart/form-data") - .attach( - "resume", - fs.createReadStream(path.join(__dirname, "testResume.pdf")), - { - contentType: "application/pdf" - } - ) - .end(function(err, res) { - res.should.have.status(200); - res.should.have.property("body"); - res.body.should.have.property("message"); - res.body.message.should.equal(Constants.Success.RESUME_UPLOAD); - res.body.should.have.property("data"); - res.body.data.should.have.property("filename"); - StorageService.download(res.body.data.filename).then(value => { - const actualFile = fs.readFileSync( - path.join(__dirname, "testResume.pdf") - ); - chai.assert.equal(value[0].length, actualFile.length); - StorageService.delete(res.body.data.filename) - .then(() => { - done(); - }) - .catch(done); - }); + it("It should SUCCEED and upload a resume for a hacker", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, noTeamHacker0, (error) => { + if (error) { + return done(error); + } + return agent + .post(`/api/hacker/resume/${noTeamHacker0._id}`) + .type("multipart/form-data") + .attach( + "resume", + fs.createReadStream(path.join(__dirname, "testResume.pdf")), + { + contentType: "application/pdf" + } + ) + .end(function(err, res) { + res.should.have.status(200); + res.should.have.property("body"); + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Success.RESUME_UPLOAD + ); + res.body.should.have.property("data"); + res.body.data.should.have.property("filename"); + StorageService.download(res.body.data.filename).then( + (value) => { + const actualFile = fs.readFileSync( + path.join(__dirname, "testResume.pdf") + ); + chai.assert.equal( + value[0].length, + actualFile.length + ); + StorageService.delete(res.body.data.filename) + .then(() => { + done(); + }) + .catch(done); + } + ); + }); }); }); - }); }); describe("GET Hacker stats", function() { - it("It should FAIL and get hacker stats (invalid validation)", function(done) { - //this takes a lot of time for some reason - util.auth.login(agent, Admin0, error => { - if (error) { - return done(error); - } - return agent.get(`/api/hacker/stats`).end(function(err, res) { - res.should.have.status(422); - res.should.have.property("body"); - res.body.should.have.property("message"); - done(); - }); + it("It should FAIL and get hacker stats (invalid validation)", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, Admin0, (error) => { + if (error) { + return done(error); + } + return agent.get(`/api/hacker/stats`).end(function(err, res) { + res.should.have.status(422); + res.should.have.property("body"); + res.body.should.have.property("message"); + done(); + }); + }); }); - }); - it("It should SUCCEED and get hacker stats", function(done) { - //this takes a lot of time for some reason - util.auth.login(agent, Admin0, error => { - if (error) { - return done(error); - } - return agent - .get(`/api/hacker/stats`) - .query({ - model: "hacker", - q: JSON.stringify([]) - }) - .end(function(err, res) { - res.should.have.status(200); - res.should.have.property("body"); - res.body.should.have.property("message"); - res.body.message.should.equal("Retrieved stats"); - res.body.should.have.property("data"); - res.body.data.should.have.property("stats"); - res.body.data.stats.should.have.property("total"); - res.body.data.stats.should.have.property("status"); - res.body.data.stats.should.have.property("school"); - res.body.data.stats.should.have.property("degree"); - res.body.data.stats.should.have.property("gender"); - res.body.data.stats.should.have.property("needsBus"); - res.body.data.stats.should.have.property("ethnicity"); - res.body.data.stats.should.have.property("jobInterest"); - res.body.data.stats.should.have.property("fieldOfStudy"); - res.body.data.stats.should.have.property("graduationYear"); - res.body.data.stats.should.have.property("dietaryRestrictions"); - res.body.data.stats.should.have.property("shirtSize"); - res.body.data.stats.should.have.property("age"); - done(); + it("It should SUCCEED and get hacker stats", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, Admin0, (error) => { + if (error) { + return done(error); + } + return agent + .get(`/api/hacker/stats`) + .query({ + model: "hacker", + q: JSON.stringify([]) + }) + .end(function(err, res) { + res.should.have.status(200); + res.should.have.property("body"); + res.body.should.have.property("message"); + res.body.message.should.equal("Retrieved stats"); + res.body.should.have.property("data"); + res.body.data.should.have.property("stats"); + res.body.data.stats.should.have.property("total"); + res.body.data.stats.should.have.property("status"); + res.body.data.stats.should.have.property("school"); + res.body.data.stats.should.have.property("degree"); + res.body.data.stats.should.have.property("gender"); + res.body.data.stats.should.have.property("needsBus"); + res.body.data.stats.should.have.property("ethnicity"); + res.body.data.stats.should.have.property("jobInterest"); + res.body.data.stats.should.have.property("fieldOfStudy"); + res.body.data.stats.should.have.property("graduationYear"); + res.body.data.stats.should.have.property( + "dietaryRestrictions" + ); + res.body.data.stats.should.have.property("shirtSize"); + res.body.data.stats.should.have.property("age"); + done(); + }); + }); + }); + it("It should FAIL and get hacker stats due to invalid Authorization", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, teamHackerAccount0, (error) => { + if (error) { + return done(error); + } + return agent.get(`/api/hacker/stats`).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("It should FAIL and get hacker stats due to invalid Authorization", function(done) { - //this takes a lot of time for some reason - util.auth.login(agent, teamHackerAccount0, error => { - if (error) { - return done(error); - } - return agent.get(`/api/hacker/stats`).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("It should FAIL and get hacker stats due to invalid Authentication", function(done) { + //this takes a lot of time for some reason + chai.request(server.app) + .get(`/api/hacker/stats`) + .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(); + }); }); - }); - it("It should FAIL and get hacker stats due to invalid Authentication", function(done) { - //this takes a lot of time for some reason - chai - .request(server.app) - .get(`/api/hacker/stats`) - .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(); - }); - }); }); describe("POST send week-of email", function() { - it("It should FAIL to send the week-of email due to invalid Authentication", function(done) { - //this takes a lot of time for some reason - chai - .request(server.app) - .post(`/api/hacker/email/weekOf/${noTeamHacker0._id}`) - .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(); - }); - }); - it("It should FAIL to send the week-of email due to invalid Authorization", function(done) { - //this takes a lot of time for some reason - util.auth.login(agent, noTeamHacker0, error => { - if (error) { - return done(error); - } - return agent - .post(`/api/hacker/email/weekOf/${noTeamHacker0._id}`) - .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("It should FAIL to send the week-of email due to invalid Authentication", function(done) { + //this takes a lot of time for some reason + chai.request(server.app) + .post(`/api/hacker/email/weekOf/${noTeamHacker0._id}`) + .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(); + }); + }); + it("It should FAIL to send the week-of email due to invalid Authorization", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, noTeamHacker0, (error) => { + if (error) { + return done(error); + } + return agent + .post(`/api/hacker/email/weekOf/${noTeamHacker0._id}`) + .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("It should SUCCEED to send the week-of email", function(done) { - //this takes a lot of time for some reason - util.auth.login(agent, Admin0, error => { - if (error) { - return done(error); - } - return agent - .post(`/api/hacker/email/weekOf/${TeamHacker0._id}`) - .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_SENT_WEEK_OF); - res.body.should.have.property("data"); - done(); + it("It should SUCCEED to send the week-of email", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, Admin0, (error) => { + if (error) { + return done(error); + } + return agent + .post(`/api/hacker/email/weekOf/${TeamHacker0._id}`) + .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_SENT_WEEK_OF + ); + res.body.should.have.property("data"); + done(); + }); }); }); - }); - - it("It should FAIL to send the week-of email due to unconfirmed email of hacker", function(done) { - //this takes a lot of time for some reason - util.auth.login(agent, Admin0, error => { - if (error) { - return done(error); - } - return agent - .post(`/api/hacker/email/weekOf/${unconfirmedHacker1._id}`) - .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.ACCOUNT_403_MESSAGE); - res.body.should.have.property("data"); - done(); + + it("It should FAIL to send the week-of email due to unconfirmed email of hacker", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, Admin0, (error) => { + if (error) { + return done(error); + } + return agent + .post(`/api/hacker/email/weekOf/${unconfirmedHacker1._id}`) + .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.ACCOUNT_403_MESSAGE + ); + res.body.should.have.property("data"); + done(); + }); }); }); - }); }); describe("POST send day-of email", function() { - it("It should FAIL to send the day-of email due to invalid Authentication", function(done) { - //this takes a lot of time for some reason - chai - .request(server.app) - .post(`/api/hacker/email/dayOf/${TeamHacker1._id}`) - .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(); - }); - }); - it("It should FAIL to send the day-of email due to invalid Authorization", function(done) { - //this takes a lot of time for some reason - util.auth.login(agent, teamHackerAccount1, error => { - if (error) { - return done(error); - } - return agent - .post(`/api/hacker/email/dayOf/${TeamHacker1._id}`) - .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("It should FAIL to send the day-of email due to invalid Authentication", function(done) { + //this takes a lot of time for some reason + chai.request(server.app) + .post(`/api/hacker/email/dayOf/${TeamHacker1._id}`) + .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(); + }); + }); + it("It should FAIL to send the day-of email due to invalid Authorization", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, teamHackerAccount1, (error) => { + if (error) { + return done(error); + } + return agent + .post(`/api/hacker/email/dayOf/${TeamHacker1._id}`) + .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("It should SUCCEED to send the day-of email", function(done) { - //this takes a lot of time for some reason - util.auth.login(agent, Admin0, error => { - if (error) { - return done(error); - } - return agent - .post(`/api/hacker/email/dayOf/${TeamHacker1._id}`) - .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_SENT_DAY_OF); - res.body.should.have.property("data"); - done(); + it("It should SUCCEED to send the day-of email", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, Admin0, (error) => { + if (error) { + return done(error); + } + return agent + .post(`/api/hacker/email/dayOf/${TeamHacker1._id}`) + .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_SENT_DAY_OF + ); + res.body.should.have.property("data"); + done(); + }); }); }); - }); - - it("It should FAIL to send the day-of email due to unconfirmed email of hacker", function(done) { - //this takes a lot of time for some reason - util.auth.login(agent, Admin0, error => { - if (error) { - return done(error); - } - return agent - .post(`/api/hacker/email/dayOf/${unconfirmedHacker1._id}`) - .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.ACCOUNT_403_MESSAGE); - res.body.should.have.property("data"); - done(); + + it("It should FAIL to send the day-of email due to unconfirmed email of hacker", function(done) { + //this takes a lot of time for some reason + util.auth.login(agent, Admin0, (error) => { + if (error) { + return done(error); + } + return agent + .post(`/api/hacker/email/dayOf/${unconfirmedHacker1._id}`) + .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.ACCOUNT_403_MESSAGE + ); + res.body.should.have.property("data"); + done(); + }); }); }); - }); }); diff --git a/tests/search.service.spec.js b/tests/search.service.spec.js index 7d836acb..b494ae3d 100644 --- a/tests/search.service.spec.js +++ b/tests/search.service.spec.js @@ -11,37 +11,37 @@ const should = chai.should(); const logger = require("../services/logger.service"); const Constants = { - Error: require("../constants/error.constant") + Error: require("../constants/error.constant") }; const util = { - hacker: require("./util/hacker.test.util"), - account: require("./util/account.test.util"), - auth: require("./util/auth.test.util") + hacker: require("./util/hacker.test.util"), + account: require("./util/account.test.util"), + auth: require("./util/auth.test.util") }; const queryToExecute = [ - { - param: "application.other.gender", - operation: "equals", - value: "Female" - } + { + param: "application.other.gender", + operation: "equals", + value: "Female" + } ]; const query2 = [ - { - param: "application.general.school", - operation: "ne", - value: "McGill" - } + { + param: "application.general.school", + operation: "ne", + value: "McGill" + } ]; const badQuery = [ - { - param: "password", - operation: "equals", - value: "passowrd" - } + { + param: "password", + operation: "equals", + value: "passowrd" + } ]; const Admin0 = util.account.staffAccounts.stored[0]; @@ -49,259 +49,265 @@ const Admin0 = util.account.staffAccounts.stored[0]; const noTeamHackerAccount0 = util.account.hackerAccounts.stored.noTeam[0]; describe("Searching for hackers", function() { - it("Should FAIL to search due to invalid authentication", function(done) { - util.auth.login( - agent, - { - email: "abc", - password: "def" - }, - error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(queryToExecute) - }) - .end(function(err, res) { - res.should.have.status(401); - res.body.message.should.equal(Constants.Error.AUTH_401_MESSAGE); - res.body.should.have.property("data"); - done(); - }); - } - ); - }); + it("Should FAIL to search due to invalid authentication", function(done) { + util.auth.login( + agent, + { + email: "abc", + password: "def" + }, + (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(queryToExecute) + }) + .end(function(err, res) { + res.should.have.status(401); + res.body.message.should.equal( + Constants.Error.AUTH_401_MESSAGE + ); + res.body.should.have.property("data"); + done(); + }); + } + ); + }); - it("Should FAIL to search due to invalid authorization", function(done) { - util.auth.login(agent, noTeamHackerAccount0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(queryToExecute) - }) - .end(function(err, res) { - res.should.have.status(403); - res.body.message.should.equal(Constants.Error.AUTH_403_MESSAGE); - res.body.should.have.property("data"); - done(); + it("Should FAIL to search due to invalid authorization", function(done) { + util.auth.login(agent, noTeamHackerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(queryToExecute) + }) + .end(function(err, res) { + res.should.have.status(403); + res.body.message.should.equal( + Constants.Error.AUTH_403_MESSAGE + ); + res.body.should.have.property("data"); + done(); + }); + }); + it("Should return all female hackers", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(queryToExecute) + }) + .end(function(err, res) { + res.should.have.status(200); + res.body.should.have.property("data"); + res.body.data.should.have.length(7); + done(); + }); + }); }); }); - it("Should return all female hackers", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(queryToExecute) - }) - .end(function(err, res) { - res.should.have.status(200); - res.body.should.have.property("data"); - res.body.data.should.have.length(7); - done(); - }); - }); - }); - }); - it("Should return an error as hackers don't have password stored", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(badQuery) - }) - .end(function(err, res) { - res.should.have.status(422); - done(); + it("Should return an error as hackers don't have password stored", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(badQuery) + }) + .end(function(err, res) { + res.should.have.status(422); + done(); + }); }); }); - }); - it("Should return an error as staff aren't searchable", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "staff", - q: JSON.stringify(badQuery) - }) - .end(function(err, res) { - res.should.have.status(422); - res.body.data.model.msg.should.equal( - "Must be a valid searchable model" - ); - done(); + it("Should return an error as staff aren't searchable", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "staff", + q: JSON.stringify(badQuery) + }) + .end(function(err, res) { + res.should.have.status(422); + res.body.data.model.msg.should.equal( + "Must be a valid searchable model" + ); + done(); + }); }); }); - }); - it("Should throw an error because model is not lowercase", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "Hacker", - q: JSON.stringify(query2) - }) - .end(function(err, res) { - res.should.have.status(422); - res.body.data.model.msg.should.equal("Model must be lower case"); - done(); + it("Should throw an error because model is not lowercase", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "Hacker", + q: JSON.stringify(query2) + }) + .end(function(err, res) { + res.should.have.status(422); + res.body.data.model.msg.should.equal( + "Model must be lower case" + ); + done(); + }); }); }); - }); - it("Should throw an error because of a fake model", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hackerz", - q: JSON.stringify(query2) - }) - .end(function(err, res) { - res.should.have.status(422); - res.body.data.model.msg.should.equal( - "Must be a valid searchable model" - ); - done(); + it("Should throw an error because of a fake model", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hackerz", + q: JSON.stringify(query2) + }) + .end(function(err, res) { + res.should.have.status(422); + res.body.data.model.msg.should.equal( + "Must be a valid searchable model" + ); + done(); + }); }); }); - }); - it("Should only return 1 hacker (page size)", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(query2), - limit: 1 - }) - .end(function(err, res) { - res.should.have.status(200); - res.body.data.should.have.length(1); - done(); + it("Should only return 1 hacker (page size)", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(query2), + limit: 1 + }) + .end(function(err, res) { + res.should.have.status(200); + res.body.data.should.have.length(1); + done(); + }); }); }); - }); - it("Should only return 1 hacker (pagination)", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return ( - agent - .get("/api/search") - //There are two test samples so by making limit 1, there will be something on the second page - .query({ - model: "hacker", - q: JSON.stringify(query2), - limit: 1, - page: 1 - }) - .end(function(err, res) { - res.should.have.status(200); - res.body.data.should.have.length(1); - done(); - }) - ); + it("Should only return 1 hacker (pagination)", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return ( + agent + .get("/api/search") + //There are two test samples so by making limit 1, there will be something on the second page + .query({ + model: "hacker", + q: JSON.stringify(query2), + limit: 1, + page: 1 + }) + .end(function(err, res) { + res.should.have.status(200); + res.body.data.should.have.length(1); + done(); + }) + ); + }); }); - }); - it("Should throw an error because out of bounds (page size)", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(query2), - limit: 5000 - }) - .end(function(err, res) { - res.should.have.status(422); - done(); + it("Should throw an error because out of bounds (page size)", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(query2), + limit: 5000 + }) + .end(function(err, res) { + res.should.have.status(422); + done(); + }); }); }); - }); - it("Should throw an error because out of bounds (pagination)", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(query2), - limit: 1, - page: -1 - }) - .end(function(err, res) { - res.should.have.status(422); - done(); + it("Should throw an error because out of bounds (pagination)", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(query2), + limit: 1, + page: -1 + }) + .end(function(err, res) { + res.should.have.status(422); + done(); + }); }); }); - }); - it("Should expand the accountId when expand is set to true", function(done) { - util.auth.login(agent, Admin0, error => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "hacker", - q: JSON.stringify(queryToExecute), - expand: true - }) - .end(function(err, res) { - res.should.have.status(200); - res.body.should.have.property("data"); - res.body.data.should.have.length(7); - res.body.data[0].should.have.property("accountId"); - res.body.data[0].accountId.should.have.property("email"); - done(); + it("Should expand the accountId when expand is set to true", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(queryToExecute), + expand: true + }) + .end(function(err, res) { + res.should.have.status(200); + res.body.should.have.property("data"); + res.body.data.should.have.length(7); + res.body.data[0].should.have.property("accountId"); + res.body.data[0].accountId.should.have.property("email"); + done(); + }); }); }); - }); }); diff --git a/tests/util/hacker.test.util.js b/tests/util/hacker.test.util.js index debf5cc3..72d17d2d 100644 --- a/tests/util/hacker.test.util.js +++ b/tests/util/hacker.test.util.js @@ -1,9 +1,9 @@ "use strict"; const Util = { - Account: require("./account.test.util") + Account: require("./account.test.util") }; const Constants = { - MongoId: require("../../constants/testMongoId.constant") + MongoId: require("../../constants/testMongoId.constant") }; const mongoose = require("mongoose"); @@ -11,598 +11,598 @@ const Hacker = require("../../models/hacker.model"); const logger = require("../../services/logger.service"); const TeamHacker0 = { - _id: Constants.MongoId.hackerAId, - accountId: Util.Account.hackerAccounts.stored.team[0]._id, - status: "Confirmed", - application: { - general: { - school: "University of Blah", - degree: "Masters", - fieldOfStudy: ["EE"], - graduationYear: 2019, - jobInterest: "Full-time", - URL: { - //gcloud bucket link - resume: "www.gcloud.com/myResume100", - github: "www.github.com/Person1", - dribbble: undefined, - personal: "www.person1.com", - linkedIn: "www.linkedin.com/in/Person1", - other: undefined - } - }, - shortAnswer: { - skills: ["CSS", "HTML", "JS"], - question1: "a", - question2: "a" - }, - other: { - gender: "Male", - ethnicity: ["Native American"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true - }, - accomodation: { - needsBus: true - } - }, - teamId: Constants.MongoId.team1Id + _id: Constants.MongoId.hackerAId, + accountId: Util.Account.hackerAccounts.stored.team[0]._id, + status: "Confirmed", + application: { + general: { + school: "University of Blah", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Full-time", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume100", + github: "www.github.com/Person1", + dribbble: undefined, + personal: "www.person1.com", + linkedIn: "www.linkedin.com/in/Person1", + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Male", + ethnicity: ["Native American"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: true + } + }, + teamId: Constants.MongoId.team1Id }; const TeamHacker1 = { - _id: Constants.MongoId.hackerDId, - accountId: Util.Account.hackerAccounts.stored.team[1]._id, - status: "Checked-in", - application: { - general: { - school: "University of Blah", - degree: "Masters", - fieldOfStudy: ["EE"], - graduationYear: 2019, - jobInterest: "Internship", - URL: { - //gcloud bucket link - resume: "www.gcloud.com/myResume2", - github: "www.github.com/Personasdf", - dribbble: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined - } - }, - shortAnswer: { - skills: ["CSS", "HTML", "JS"], - question1: "a", - question2: "a" - }, - other: { - gender: "Female", - ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true - }, - accomodation: { - needsBus: false - } - }, - teamId: Constants.MongoId.team3Id + _id: Constants.MongoId.hackerDId, + accountId: Util.Account.hackerAccounts.stored.team[1]._id, + status: "Checked-in", + application: { + general: { + school: "University of Blah", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Internship", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume2", + github: "www.github.com/Personasdf", + dribbble: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } + }, + teamId: Constants.MongoId.team3Id }; const TeamHacker2 = { - _id: Constants.MongoId.hackerEId, - accountId: Util.Account.hackerAccounts.stored.team[2]._id, - status: "Waitlisted", - application: { - general: { - school: "University of Blah", - degree: "Masters", - fieldOfStudy: ["EE"], - graduationYear: 2019, - jobInterest: "Internship", - URL: { - //gcloud bucket link - resume: "www.gcloud.com/myResume2", - github: "www.github.com/Personasdf", - dribbble: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined - } - }, - shortAnswer: { - skills: ["CSS", "HTML", "JS"], - question1: "a", - question2: "a" - }, - other: { - gender: "Female", - ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true - }, - accomodation: { - needsBus: false - } - }, - teamId: Constants.MongoId.team3Id + _id: Constants.MongoId.hackerEId, + accountId: Util.Account.hackerAccounts.stored.team[2]._id, + status: "Waitlisted", + application: { + general: { + school: "University of Blah", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Internship", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume2", + github: "www.github.com/Personasdf", + dribbble: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } + }, + teamId: Constants.MongoId.team3Id }; const TeamHacker3 = { - _id: Constants.MongoId.hackerFId, - accountId: Util.Account.hackerAccounts.stored.team[3]._id, - status: "Waitlisted", - application: { - general: { - school: "University of Blah", - degree: "Masters", - fieldOfStudy: ["EE"], - graduationYear: 2019, - jobInterest: "Internship", - URL: { - //gcloud bucket link - resume: "www.gcloud.com/myResume2", - github: "www.github.com/Personasdf", - dribbble: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined - } - }, - shortAnswer: { - skills: ["CSS", "HTML", "JS"], - question1: "a", - question2: "a" - }, - other: { - gender: "Female", - ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true - }, - accomodation: { - needsBus: false - } - }, - teamId: Constants.MongoId.team3Id + _id: Constants.MongoId.hackerFId, + accountId: Util.Account.hackerAccounts.stored.team[3]._id, + status: "Waitlisted", + application: { + general: { + school: "University of Blah", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Internship", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume2", + github: "www.github.com/Personasdf", + dribbble: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } + }, + teamId: Constants.MongoId.team3Id }; const TeamHacker4 = { - _id: Constants.MongoId.hackerGId, - accountId: Util.Account.hackerAccounts.stored.team[4]._id, - status: "Waitlisted", - application: { - general: { - school: "University of Blah", - degree: "Masters", - fieldOfStudy: ["EE"], - graduationYear: 2019, - jobInterest: "Internship", - URL: { - //gcloud bucket link - resume: "www.gcloud.com/myResume2", - github: "www.github.com/Personasdf", - dribbble: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined - } - }, - shortAnswer: { - skills: ["CSS", "HTML", "JS"], - question1: "a", - question2: "a" - }, - other: { - gender: "Female", - ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true - }, - accomodation: { - needsBus: false - } - }, - teamId: Constants.MongoId.team3Id + _id: Constants.MongoId.hackerGId, + accountId: Util.Account.hackerAccounts.stored.team[4]._id, + status: "Waitlisted", + application: { + general: { + school: "University of Blah", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Internship", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume2", + github: "www.github.com/Personasdf", + dribbble: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } + }, + teamId: Constants.MongoId.team3Id }; const NoTeamHacker0 = { - _id: Constants.MongoId.hackerBId, - accountId: Util.Account.hackerAccounts.stored.noTeam[0]._id, - status: "Accepted", - application: { - general: { - school: "University of Blah", - degree: "Masters", - fieldOfStudy: ["EE"], - graduationYear: 2019, - jobInterest: "Internship", - URL: { - //gcloud bucket link - resume: "www.gcloud.com/myResume1", - github: "www.github.com/Person4", - dribbble: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined - } - }, - shortAnswer: { - skills: ["CSS", "HTML", "JS"], - question1: "a", - question2: "a" - }, - other: { - gender: "Female", - ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true - }, - accomodation: { - needsBus: false + _id: Constants.MongoId.hackerBId, + accountId: Util.Account.hackerAccounts.stored.noTeam[0]._id, + status: "Accepted", + application: { + general: { + school: "University of Blah", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Internship", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume1", + github: "www.github.com/Person4", + dribbble: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } } - } }; const newHacker0 = { - accountId: Util.Account.hackerAccounts.new[0]._id, - application: { - general: { - school: "University of ASDF", - degree: "Masters", - fieldOfStudy: ["EE"], - graduationYear: 2019, - jobInterest: "Full-time", - URL: { - //gcloud bucket link - resume: "www.gcloud.com/myResume100", - github: "www.github.com/Person1", - dribbble: undefined, - personal: "www.person1.com", - linkedIn: "www.linkedin.com/in/Person1", - other: undefined - } - }, - shortAnswer: { - skills: ["CSS", "HTML", "JS"], - question1: "a", - question2: "a" - }, - other: { - gender: "Female", - ethnicity: ["Caucasian"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true - }, - accomodation: { - needsBus: false + accountId: Util.Account.hackerAccounts.new[0]._id, + application: { + general: { + school: "University of ASDF", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Full-time", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume100", + github: "www.github.com/Person1", + dribbble: undefined, + personal: "www.person1.com", + linkedIn: "www.linkedin.com/in/Person1", + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["Caucasian"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } } - } }; const newHacker1 = { - accountId: Util.Account.hackerAccounts.new[1]._id, - application: { - general: { - school: "University of YIKES", - degree: "PhD", - fieldOfStudy: ["EE"], - graduationYear: 2019, - jobInterest: "Full-time", - URL: { - //gcloud bucket link - resume: "www.gcloud.com/myResume100", - github: "www.github.com/Person1", - dribbble: undefined, - personal: "www.person1.com", - linkedIn: "www.linkedin.com/in/Person1", - other: undefined - } - }, - shortAnswer: { - skills: ["CSS", "HTML", "JS"], - question1: "a", - question2: "a" - }, - other: { - gender: "Female", - ethnicity: ["African American"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true - }, - accomodation: { - needsBus: true + accountId: Util.Account.hackerAccounts.new[1]._id, + application: { + general: { + school: "University of YIKES", + degree: "PhD", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Full-time", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume100", + github: "www.github.com/Person1", + dribbble: undefined, + personal: "www.person1.com", + linkedIn: "www.linkedin.com/in/Person1", + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["African American"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: true + } } - } }; // duplicate of newHack0, but with false for code of conduct const invalidHacker0 = { - accountId: Util.Account.hackerAccounts.invalid[0]._id, - application: { - general: { - school: "University of ASDF", - degree: "Masters", - fieldOfStudy: ["EE"], - graduationYear: 2019, - jobInterest: "Full-time", - URL: { - //gcloud bucket link - resume: "www.gcloud.com/myResume100", - github: "www.github.com/Person1", - dribbble: undefined, - personal: "www.person1.com", - linkedIn: "www.linkedin.com/in/Person1", - other: undefined - } - }, - shortAnswer: { - skills: ["CSS", "HTML", "JS"], - question1: "a", - question2: "a" - }, - other: { - gender: "Female", - ethnicity: ["Caucasian"], - // must accept code of conduct to be valid - codeOfConduct_MCHACKS: false, - codeOfConduct_MLH: false - }, - accomodation: { - needsBus: true + accountId: Util.Account.hackerAccounts.invalid[0]._id, + application: { + general: { + school: "University of ASDF", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Full-time", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume100", + github: "www.github.com/Person1", + dribbble: undefined, + personal: "www.person1.com", + linkedIn: "www.linkedin.com/in/Person1", + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["Caucasian"], + // must accept code of conduct to be valid + codeOfConduct_MCHACKS: false, + codeOfConduct_MLH: false + }, + accomodation: { + needsBus: true + } } - } }; const invalidHacker1 = { - _id: mongoose.Types.ObjectId(), - // invalid mongoID - accountId: Util.Account.hackerAccounts.invalid[1]._invalidId, - application: { - general: { - // invalid missing school attribute - degree: "Undersaduate", - fieldOfStudy: ["EE"], - graduationYear: 2020, - // invalid job interest - jobInterest: "ASDF", - URL: { - // invalid URL links with no resume - } - }, - shortAnswer: { - skills: ["CSS", "HTML", "JS"], - question1: "a", - question2: "a" - }, - other: { - gender: "Female", - ethnicity: ["Caucasian"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true - }, - accomodation: { - needsBus: true + _id: mongoose.Types.ObjectId(), + // invalid mongoID + accountId: Util.Account.hackerAccounts.invalid[1]._invalidId, + application: { + general: { + // invalid missing school attribute + degree: "Undersaduate", + fieldOfStudy: ["EE"], + graduationYear: 2020, + // invalid job interest + jobInterest: "ASDF", + URL: { + // invalid URL links with no resume + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["Caucasian"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: true + } } - } }; const duplicateAccountLinkHacker0 = { - _id: mongoose.Types.ObjectId(), - accountId: Util.Account.hackerAccounts.stored.team[0]._id, - status: "Applied", - application: { - general: { - school: "University of Blah", - degree: "Undergraduate", - fieldOfStudy: ["CS"], - graduationYear: 2019, - jobInterest: "Full-time", - URL: { - //gcloud bucket link - resume: "www.gcloud.com/myResume100", - github: "www.github.com/Person1", - dribbble: undefined, - personal: "www.person1.com", - linkedIn: "www.linkedin.com/in/Person1", - other: undefined - } - }, - shortAnswer: { - skills: ["CSS", "HTML", "JS"], - question1: "a", - question2: "a" - }, - other: { - gender: "Male", - ethnicity: ["Caucasian"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true - }, - accomodation: { - needsBus: true + _id: mongoose.Types.ObjectId(), + accountId: Util.Account.hackerAccounts.stored.team[0]._id, + status: "Applied", + application: { + general: { + school: "University of Blah", + degree: "Undergraduate", + fieldOfStudy: ["CS"], + graduationYear: 2019, + jobInterest: "Full-time", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume100", + github: "www.github.com/Person1", + dribbble: undefined, + personal: "www.person1.com", + linkedIn: "www.linkedin.com/in/Person1", + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Male", + ethnicity: ["Caucasian"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: true + } } - } }; const waitlistedHacker0 = { - _id: Constants.MongoId.hackerCId, - accountId: Util.Account.waitlistedHacker0._id, - status: "Waitlisted", - application: { - general: { - school: "University of Blah", - degree: "Masters", - fieldOfStudy: ["EE"], - graduationYear: 2019, - jobInterest: "Intership", - URL: { - //gcloud bucket link - resume: "www.gcloud.com/myResume2", - github: "www.github.com/Personasdf", - dribbble: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined - } - }, - shortAnswer: { - skills: ["CSS", "HTML", "JS"], - question1: "a", - question2: "a" - }, - other: { - gender: "Female", - ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true - }, - accomodation: { - needsBus: false - } - }, - teamId: Constants.MongoId.team2Id + _id: Constants.MongoId.hackerCId, + accountId: Util.Account.waitlistedHacker0._id, + status: "Waitlisted", + application: { + general: { + school: "University of Blah", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Intership", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume2", + github: "www.github.com/Personasdf", + dribbble: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } + }, + teamId: Constants.MongoId.team2Id }; const unconfirmedAccountHacker0 = { - _id: Constants.MongoId.hackerCId, - accountId: Util.Account.NonConfirmedAccount2._id, - status: "Waitlisted", - application: { - general: { - school: "University of Blah1", - degree: "Masters", - fieldOfStudy: ["EE"], - graduationYear: 2019, - jobInterest: "Internship", - URL: { - //gcloud bucket link - resume: "www.gcloud.com/myResume123", - github: "www.github.com/Personasdf", - dribbble: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined - } - }, - shortAnswer: { - skills: ["CSS", "HTML", "JS"], - question1: "a", - question2: "a" - }, - other: { - gender: "Female", - ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true - }, - accomodation: { - needsBus: false + _id: Constants.MongoId.hackerCId, + accountId: Util.Account.NonConfirmedAccount2._id, + status: "Waitlisted", + application: { + general: { + school: "University of Blah1", + degree: "Masters", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Internship", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume123", + github: "www.github.com/Personasdf", + dribbble: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } } - } }; const unconfirmedAccountHacker1 = { - _id: Constants.MongoId.hackerHId, - accountId: Util.Account.hackerAccounts.stored.unconfirmed[0]._id, - status: "Accepted", - application: { - general: { - school: "University of Blah2", - degree: "Underggraduate", - fieldOfStudy: ["EE"], - graduationYear: 2019, - jobInterest: "Internship", - URL: { - //gcloud bucket link - resume: "www.gcloud.com/myResume123", - github: "www.github.com/Personasdf", - dropler: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined - } - }, - shortAnswer: { - skills: ["CSS", "HTML", "JS"], - question1: "a", - question2: "a" - }, - other: { - gender: "Female", - ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true - }, - accomodation: { - needsBus: false + _id: Constants.MongoId.hackerHId, + accountId: Util.Account.hackerAccounts.stored.unconfirmed[0]._id, + status: "Accepted", + application: { + general: { + school: "University of Blah2", + degree: "Underggraduate", + fieldOfStudy: ["EE"], + graduationYear: 2019, + jobInterest: "Internship", + URL: { + //gcloud bucket link + resume: "www.gcloud.com/myResume123", + github: "www.github.com/Personasdf", + dropler: undefined, + personal: undefined, + linkedIn: undefined, + other: undefined + } + }, + shortAnswer: { + skills: ["CSS", "HTML", "JS"], + question1: "a", + question2: "a" + }, + other: { + gender: "Female", + ethnicity: ["European"], + codeOfConduct_MCHACKS: true, + codeOfConduct_MLH: true + }, + accomodation: { + needsBus: false + } } - } }; const Hackers = [ - TeamHacker0, - TeamHacker1, - TeamHacker2, - TeamHacker3, - TeamHacker4, + TeamHacker0, + TeamHacker1, + TeamHacker2, + TeamHacker3, + TeamHacker4, - NoTeamHacker0, + NoTeamHacker0, - invalidHacker0, - unconfirmedAccountHacker1, + invalidHacker0, + unconfirmedAccountHacker1, - duplicateAccountLinkHacker0, - waitlistedHacker0 + duplicateAccountLinkHacker0, + waitlistedHacker0 ]; module.exports = { - TeamHacker0: TeamHacker0, - TeamHacker1: TeamHacker1, - TeamHacker2: TeamHacker2, - TeamHacker3: TeamHacker3, - TeamHacker4: TeamHacker4, + TeamHacker0: TeamHacker0, + TeamHacker1: TeamHacker1, + TeamHacker2: TeamHacker2, + TeamHacker3: TeamHacker3, + TeamHacker4: TeamHacker4, - NoTeamHacker0: NoTeamHacker0, + NoTeamHacker0: NoTeamHacker0, - newHacker0: newHacker0, - newHacker1: newHacker1, + newHacker0: newHacker0, + newHacker1: newHacker1, - invalidHacker0: invalidHacker0, - invalidHacker1: invalidHacker1, + invalidHacker0: invalidHacker0, + invalidHacker1: invalidHacker1, - duplicateAccountLinkHacker0: duplicateAccountLinkHacker0, - waitlistedHacker0: waitlistedHacker0, - unconfirmedAccountHacker0: unconfirmedAccountHacker0, - unconfirmedAccountHacker1: unconfirmedAccountHacker1, + duplicateAccountLinkHacker0: duplicateAccountLinkHacker0, + waitlistedHacker0: waitlistedHacker0, + unconfirmedAccountHacker0: unconfirmedAccountHacker0, + unconfirmedAccountHacker1: unconfirmedAccountHacker1, - Hackers: Hackers, - storeAll: storeAll, - dropAll: dropAll + Hackers: Hackers, + storeAll: storeAll, + dropAll: dropAll }; function store(attributes) { - const hackerDocs = []; - const hackerIds = []; - for (var i = 0; i < attributes.length; i++) { - hackerDocs.push(new Hacker(attributes[i])); - hackerIds.push(attributes[i]._id); - } + const hackerDocs = []; + const hackerIds = []; + for (var i = 0; i < attributes.length; i++) { + hackerDocs.push(new Hacker(attributes[i])); + hackerIds.push(attributes[i]._id); + } - return Hacker.collection.insertMany(hackerDocs); + return Hacker.collection.insertMany(hackerDocs); } async function storeAll() { - await store(Hackers); + await store(Hackers); } async function dropAll() { - try { - await Hacker.collection.drop(); - } catch (e) { - if (e.code === 26) { - logger.info("namespace %s not found", Hacker.collection.name); - } else { - throw e; + try { + await Hacker.collection.drop(); + } catch (e) { + if (e.code === 26) { + logger.info("namespace %s not found", Hacker.collection.name); + } else { + throw e; + } } - } } From 0a849c6e864f45978b850e62be9be85b45095121 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Fri, 6 Dec 2019 00:53:52 -0500 Subject: [PATCH 10/28] Ignore --- middlewares/auth.middleware.js | 1 + middlewares/hacker.middleware.js | 1 + middlewares/validators/validator.helper.js | 13 +++++++++---- services/account.service.js | 1 + tests/hacker.test.js | 8 ++------ tests/util/account.test.util.js | 3 ++- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/middlewares/auth.middleware.js b/middlewares/auth.middleware.js index ca2c26e4..54e34d07 100644 --- a/middlewares/auth.middleware.js +++ b/middlewares/auth.middleware.js @@ -65,6 +65,7 @@ function login(req, res, next) { */ function ensureAuthenticated() { return function(req, res, next) { + console.log("are we here"); if (req.isUnauthenticated()) { return next({ status: 401, diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index f4a75753..52f17470 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -144,6 +144,7 @@ async function validateConfirmedStatus(account, next) { * @param {(err?) => void} next */ async function validateConfirmedStatusFromAccountId(req, res, next) { + console.log(req.body.accountId); const account = await Services.Account.findById(req.body.accountId); return validateConfirmedStatus(account, next); } diff --git a/middlewares/validators/validator.helper.js b/middlewares/validators/validator.helper.js index 03fd218a..e1882a7e 100644 --- a/middlewares/validators/validator.helper.js +++ b/middlewares/validators/validator.helper.js @@ -381,6 +381,7 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { "invalid application" ); + console.log("hi"); //helper object to iterate through the items in the application and track which items are not valid. const hasValid = { school: false, @@ -456,12 +457,14 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { hasValid.codeOfConduct_MLH = booleanValidator( "body", "application.other.codeOfConduct_MLH", - false + false, + true ); hasValid.codeOfConduct_MCHACKS = booleanValidator( "body", "appliction.other.codeOfConduct_MCHACKS", - false + false, + true ); const jobInterests = Constants.JOB_INTERESTS; hasValid.github = @@ -569,12 +572,14 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { hasValid.codeOfConduct_MLH = booleanValidator( "body", "application.other.codeOfConduct_MLH", - false + false, + true ); hasValid.codeOfConduct_MCHACKS = booleanValidator( "body", "appliction.other.codeOfConduct_MCHACKS", - false + false, + true ); const jobInterests = Constants.JOB_INTERESTS; hasValid.github = diff --git a/services/account.service.js b/services/account.service.js index 0d633232..ae12f724 100644 --- a/services/account.service.js +++ b/services/account.service.js @@ -14,6 +14,7 @@ function findById(id) { const query = { _id: id }; + console.log(query); return Account.findById( query, diff --git a/tests/hacker.test.js b/tests/hacker.test.js index fed0ffcf..1cc04ada 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -28,7 +28,6 @@ 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[0]; const invalidHacker0 = util.hacker.invalidHacker0; const newHacker1 = util.hacker.newHacker1; @@ -41,10 +40,6 @@ const TeamHacker0 = util.hacker.TeamHacker0; const TeamHacker1 = util.hacker.TeamHacker1; const duplicateAccountLinkHacker0 = util.hacker.duplicateAccountLinkHacker0; -const unconfirmedHackerAccount1 = - util.account.hackerAccounts.stored.unconfirmed[0]; -const unconfirmedHackerAccount0 = util.hacker.unconfirmedAccountHacker0; - const unconfirmedHacker1 = util.hacker.unconfirmedAccountHacker1; const invalidHacker1 = util.hacker.invalidHacker1; @@ -430,7 +425,7 @@ describe("POST create hacker", function() { // should fail due to 'false' on code of conduct it("should FAIL if the new hacker does not accept code of conduct", function(done) { - util.auth.login(agent, Admin0, (error) => { + util.auth.login(agent, newHacker0, (error) => { if (error) { agent.close(); return done(error); @@ -440,6 +435,7 @@ describe("POST create hacker", function() { .type("application/json") .send(invalidHacker0) .end(function(err, res) { + console.log(res.body.message); res.should.have.status(422); res.should.be.json; res.body.should.have.property("message"); diff --git a/tests/util/account.test.util.js b/tests/util/account.test.util.js index 3dfd2021..e23cad5e 100644 --- a/tests/util/account.test.util.js +++ b/tests/util/account.test.util.js @@ -136,7 +136,8 @@ let hackerAccounts = { }) }, invalid: createNAccounts(10, { - accountType: Constants.HACKER + accountType: Constants.HACKER, + confirmed: true }) }; From a3f06b50d18b67a63b4a5f533acfe665ab4d2be0 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Fri, 6 Dec 2019 01:01:17 -0500 Subject: [PATCH 11/28] Ignore --- tests/hacker.test.js | 133 ++++++++++++++++++++++++++++++++- tests/util/hacker.test.util.js | 4 +- 2 files changed, 133 insertions(+), 4 deletions(-) diff --git a/tests/hacker.test.js b/tests/hacker.test.js index 1cc04ada..b4c3b9b4 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -40,6 +40,11 @@ const TeamHacker0 = util.hacker.TeamHacker0; const TeamHacker1 = util.hacker.TeamHacker1; const duplicateAccountLinkHacker0 = util.hacker.duplicateAccountLinkHacker0; +const unconfirmedHackerAccount1 = + util.account.hackerAccounts.stored.unconfirmed[0]; +const unconfirmedHackerAccount0 = util.hacker.unconfirmedAccountHacker0; + +const unconfirmedHacker0 = util.hacker.unconfirmedAccountHacker0; const unconfirmedHacker1 = util.hacker.unconfirmedAccountHacker1; const invalidHacker1 = util.hacker.invalidHacker1; @@ -104,6 +109,25 @@ describe("GET hacker", function() { }); }); + // fail case due to unconfirmed email address of already defined hacker + it("should FAIL to list the user's hacker info due to unconfirmed email on /api/hacker/self GET", function(done) { + util.auth.login(agent, unconfirmedHackerAccount1, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent.get("/api/hacker/self").end(function(err, res) { + res.should.have.status(409); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Error.ACCOUNT_TYPE_409_MESSAGE + ); + done(); + }); + }); + }); + // succeed on admin case it("should list a hacker's information using admin power on /api/hacker/:id GET", function(done) { util.auth.login(agent, Admin0, (error) => { @@ -470,7 +494,7 @@ describe("POST create hacker", function() { return agent .post(`/api/hacker/`) .type("application/json") - .send(util.hacker.unconfirmedAccountHacker0) + .send(unconfirmedHackerAccount0) .end(function(err, res) { res.should.be.json; res.body.should.have.property("message"); @@ -632,6 +656,32 @@ describe("PATCH update one hacker", function() { }); }); + // hacker changed email, thus cannot change their status until they confirm email + it("should FAIL and NOT update a hacker STATUS as a Hacker due to unconfirmed email", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/status/${unconfirmedHacker1._id}`) + .type("application/json") + .send({ + status: "Waitlisted" + }) + .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.ACCOUNT_403_MESSAGE + ); + res.body.should.have.property("data"); + done(); + }); + }); + }); + // volunteer should successfully checkin hacker it("should SUCCEED and check in hacker as a volunteer", function(done) { util.auth.login(agent, volunteerAccount0, (error) => { @@ -690,6 +740,32 @@ describe("PATCH update one hacker", function() { }); }); + // hacker should FAIL to checkin hacker due to unconfirmed email + it("should FAIL to check in hacker as a volunteer due to unconfirmed email", function(done) { + util.auth.login(agent, volunteerAccount0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/checkin/${unconfirmedHacker1._id}`) + .type("application/json") + .send({ + status: "Checked-in" + }) + .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.ACCOUNT_403_MESSAGE + ); + res.body.should.have.property("data"); + done(); + }); + }); + }); + // should succeed on hacker case it("should SUCCEED and update the user's hacker info", function(done) { util.auth.login(agent, noTeamHackerAccount0, (error) => { @@ -722,6 +798,32 @@ describe("PATCH update one hacker", function() { }); }); + // should FAIL to change hacker data with an unconfirmed email + it("should FAIL and not update the user's hacker info due to unconfirmed email", function(done) { + util.auth.login(agent, unconfirmedHackerAccount1, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/${unconfirmedHacker1._id}`) + .type("application/json") + .send({ + gender: "Other" + }) + .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.ACCOUNT_403_MESSAGE + ); + res.body.should.have.property("data"); + done(); + }); + }); + }); + // should fail due to authorization it("should Fail to update hacker info due to lack of authorization", function(done) { util.auth.login(agent, noTeamHackerAccount0, (error) => { @@ -811,6 +913,35 @@ describe("PATCH update one hacker", function() { }); }); + // Fail and don't change to accepted + it("should FAIL for hacker to update their own status from accepted to confirmed due to unconfirmed email", function(done) { + util.auth.login(agent, unconfirmedHackerAccount1, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .patch(`/api/hacker/confirmation/${unconfirmedHacker1._id}`) + .type("application/json") + .send({ + confirm: true + }) + .end(function(err, res) { + if (err) { + return done(err); + } + res.should.have.status(403); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal( + Constants.Error.ACCOUNT_403_MESSAGE + ); + res.body.should.have.property("data"); + done(); + }); + }); + }); + // Succeed and change confirmed to accepted it("should succeed for hacker to update their own status from confirmed to accepted", function(done) { util.auth.login(agent, teamHackerAccount0, (error) => { diff --git a/tests/util/hacker.test.util.js b/tests/util/hacker.test.util.js index 72d17d2d..03cfe337 100644 --- a/tests/util/hacker.test.util.js +++ b/tests/util/hacker.test.util.js @@ -465,7 +465,7 @@ const waitlistedHacker0 = { const unconfirmedAccountHacker0 = { _id: Constants.MongoId.hackerCId, - accountId: Util.Account.NonConfirmedAccount2._id, + accountId: Util.Account.NonConfirmedAccount3._id, status: "Waitlisted", application: { general: { @@ -547,8 +547,6 @@ const Hackers = [ TeamHacker4, NoTeamHacker0, - - invalidHacker0, unconfirmedAccountHacker1, duplicateAccountLinkHacker0, From f164708326a1b5076882a890d4498e8bc856f9d8 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Fri, 6 Dec 2019 01:06:45 -0500 Subject: [PATCH 12/28] Ignore --- tests/hacker.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/hacker.test.js b/tests/hacker.test.js index b4c3b9b4..a53274e7 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -44,7 +44,6 @@ const unconfirmedHackerAccount1 = util.account.hackerAccounts.stored.unconfirmed[0]; const unconfirmedHackerAccount0 = util.hacker.unconfirmedAccountHacker0; -const unconfirmedHacker0 = util.hacker.unconfirmedAccountHacker0; const unconfirmedHacker1 = util.hacker.unconfirmedAccountHacker1; const invalidHacker1 = util.hacker.invalidHacker1; From 009e024976e9c3d4128dbf1f7a0f3675d1ca1632 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Fri, 6 Dec 2019 13:53:14 -0500 Subject: [PATCH 13/28] Ignore --- middlewares/validators/hacker.validator.js | 2 +- middlewares/validators/validator.helper.js | 125 ++++++++++++--------- tests/hacker.test.js | 4 +- tests/util/account.test.util.js | 3 +- tests/util/hacker.test.util.js | 2 +- 5 files changed, 76 insertions(+), 60 deletions(-) diff --git a/middlewares/validators/hacker.validator.js b/middlewares/validators/hacker.validator.js index 87e50470..daa2aca2 100644 --- a/middlewares/validators/hacker.validator.js +++ b/middlewares/validators/hacker.validator.js @@ -15,7 +15,7 @@ module.exports = { ], updateHackerValidator: [ - VALIDATOR.applicationValidator("body", "application", false) + VALIDATOR.applicationValidator("body", "application", true) ], updateStatusValidator: [ VALIDATOR.enumValidator( diff --git a/middlewares/validators/validator.helper.js b/middlewares/validators/validator.helper.js index e1882a7e..01f601a3 100644 --- a/middlewares/validators/validator.helper.js +++ b/middlewares/validators/validator.helper.js @@ -380,8 +380,6 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { fieldname, "invalid application" ); - - console.log("hi"); //helper object to iterate through the items in the application and track which items are not valid. const hasValid = { school: false, @@ -406,17 +404,17 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { question2: false, team: false }; + if (optional) { return application .optional({ checkFalsy: true }) .custom((app) => { - hasValid.school = stringValidator( - "body", - "application.general.school", - false - ); + hasValid.school = + !app.general.school || + typeof app.general.school === "string"; + hasValid.degree = stringValidator( "body", "application.general.degree", @@ -427,11 +425,6 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { "application.general.fieldOfStudy", false ); - hasValid.general = stringValidator( - "body", - "application.other.gender", - false - ); hasValid.bus = booleanValidator( "body", "application.accomodation.needsBus", @@ -527,41 +520,53 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { } else { return application .custom((app) => { - hasValid.school = stringValidator( - "body", - "application.general.school", - false - ); - hasValid.degree = stringValidator( - "body", - "application.general.degree", - false - ); - hasValid.fieldOfStudy = alphaArrayValidator( - "body", - "application.general.fieldOfStudy", - false - ); - hasValid.general = stringValidator( - "body", - "application.other.gender", - false - ); - hasValid.bus = booleanValidator( - "body", - "application.accomodation.needsBus", - false - ); + // hasValid.school = stringValidator( + // "body", + // "application.general.school", + // false + // ); + // console.log(hasValid.school); + hasValid.school = + !app.general.school || + typeof app.general.school === "string"; + + // hasValid.degree = stringValidator( + // "body", + // "application.general.degree", + // false + // ); + hasValid.degree = + !app.general.degree || + typeof app.general.degree === "string"; + // hasValid.fieldOfStudy = alphaArrayValidator( + // "body", + // "application.general.fieldOfStudy", + // false + // ); + hasValid.bus = + !app.accomodation.needsBus || + (typeof app.accomodation.needsBus === "boolean" && + app.accomodation.needsBus === true); + + // hasValid.bus = booleanValidator( + // "body", + // "application.accomodation.needsBus", + // false + // ); + hasValid.ethnicity = alphaArrayValidator( "body", "application.other.ethnicity", false ); - hasValid.gender = stringValidator( - "body", - "application.other.gender", - false - ); + hasValid.gender = + !app.other.gender || typeof app.other.gender === "string"; + + // hasValid.gender = stringValidator( + // "body", + // "application.other.gender", + // false + // ); hasValid.graduationYear = integerValidator( "body", "application.general.graduationYear", @@ -569,18 +574,29 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { 2019, 2030 ); - hasValid.codeOfConduct_MLH = booleanValidator( - "body", - "application.other.codeOfConduct_MLH", - false, - true - ); - hasValid.codeOfConduct_MCHACKS = booleanValidator( - "body", - "appliction.other.codeOfConduct_MCHACKS", - false, - true - ); + + hasValid.codeOfConduct_MLH = + !app.other.codeOfConduct_MLH || + (typeof app.other.codeOfConduct_MLH === "boolean" && + app.other.codeOfConduct_MLH === true); + // hasValid.codeOfConduct_MLH = booleanValidator( + // "body", + // "application.other.codeOfConduct_MLH", + // false, + // true + // ); + + hasValid.codeOfConduct_MCHACKS = + !app.other.codeOfConduct_MCHACKS || + (typeof app.other.codeOfConduct_MCHACKS === "boolean" && + app.other.codeOfConduct_MCHACKS === true); + // hasValid.codeOfConduct_MCHACKS = booleanValidator( + // "body", + // "appliction.other.codeOfConduct_MCHACKS", + // false, + // true + // ); + console.log("Before"); const jobInterests = Constants.JOB_INTERESTS; hasValid.github = !app.general.URL.github || @@ -614,6 +630,7 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { typeof app.shortAnswer.question2 === "string"; hasValid.team = !app.team || mongoose.Types.ObjectId.isValid(app.team); + console.log(hasValid.codeOfConduct_MCHACKS); return ( hasValid.comments && hasValid.school && diff --git a/tests/hacker.test.js b/tests/hacker.test.js index a53274e7..f92b0f8a 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -448,7 +448,7 @@ describe("POST create hacker", function() { // should fail due to 'false' on code of conduct it("should FAIL if the new hacker does not accept code of conduct", function(done) { - util.auth.login(agent, newHacker0, (error) => { + util.auth.login(agent, newHackerAccount0, (error) => { if (error) { agent.close(); return done(error); @@ -458,7 +458,7 @@ describe("POST create hacker", function() { .type("application/json") .send(invalidHacker0) .end(function(err, res) { - console.log(res.body.message); + console.log(res.body.data); res.should.have.status(422); res.should.be.json; res.body.should.have.property("message"); diff --git a/tests/util/account.test.util.js b/tests/util/account.test.util.js index e23cad5e..3dfd2021 100644 --- a/tests/util/account.test.util.js +++ b/tests/util/account.test.util.js @@ -136,8 +136,7 @@ let hackerAccounts = { }) }, invalid: createNAccounts(10, { - accountType: Constants.HACKER, - confirmed: true + accountType: Constants.HACKER }) }; diff --git a/tests/util/hacker.test.util.js b/tests/util/hacker.test.util.js index 03cfe337..918d812c 100644 --- a/tests/util/hacker.test.util.js +++ b/tests/util/hacker.test.util.js @@ -317,7 +317,7 @@ const newHacker1 = { // duplicate of newHack0, but with false for code of conduct const invalidHacker0 = { - accountId: Util.Account.hackerAccounts.invalid[0]._id, + accountId: Util.Account.hackerAccounts.new[0]._id, application: { general: { school: "University of ASDF", From ee5b24d7a8c0aa6f5d99355cc95c4dffa0ba66fb Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Fri, 6 Dec 2019 15:47:39 -0500 Subject: [PATCH 14/28] All tests finally working, replaced undefined with null as well in hacker util --- middlewares/validators/hacker.validator.js | 33 ++++ middlewares/validators/validator.helper.js | 176 ++------------------- tests/hacker.test.js | 53 +++++-- tests/util/hacker.test.util.js | 85 +++++----- 4 files changed, 128 insertions(+), 219 deletions(-) diff --git a/middlewares/validators/hacker.validator.js b/middlewares/validators/hacker.validator.js index daa2aca2..5280a1ce 100644 --- a/middlewares/validators/hacker.validator.js +++ b/middlewares/validators/hacker.validator.js @@ -6,7 +6,40 @@ module.exports = { newHackerValidator: [ // status will be added automatically VALIDATOR.mongoIdValidator("body", "accountId", false), + VALIDATOR.stringValidator("body", "application.general.school", false), + VALIDATOR.stringValidator("body", "application.general.degree", false), + VALIDATOR.alphaArrayValidator( + "body", + "application.general.fieldOfStudy", + false + ), + VALIDATOR.integerValidator( + "body", + "application.general.graduationYear", + false, + 2019, + 2030 + ), VALIDATOR.applicationValidator("body", "application", false), + VALIDATOR.stringValidator("body", "application.other.gender", false), + VALIDATOR.alphaArrayValidator( + "body", + "application.other.ethnicity", + false + ), + VALIDATOR.booleanValidator( + "body", + "application.other.codeOfConduct_MLH", + false, + true + ), + VALIDATOR.booleanValidator( + "body", + "application.other.codeOfConduct_MCHACKS", + false, + true + ), + VALIDATOR.booleanValidator("body", "application.accomodation.needsBus"), VALIDATOR.mongoIdValidator("body", "teamId", true) ], diff --git a/middlewares/validators/validator.helper.js b/middlewares/validators/validator.helper.js index 01f601a3..16c3f46d 100644 --- a/middlewares/validators/validator.helper.js +++ b/middlewares/validators/validator.helper.js @@ -382,16 +382,6 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { ); //helper object to iterate through the items in the application and track which items are not valid. const hasValid = { - school: false, - degree: false, - fieldOfStudy: false, - gender: false, - ethnicity: false, - bus: false, - gender: false, - graduationYear: false, - codeOfConduct_MLH: false, - codeOfConduct_MCHACKS: false, github: false, dribbble: false, personal: false, @@ -411,54 +401,6 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { checkFalsy: true }) .custom((app) => { - hasValid.school = - !app.general.school || - typeof app.general.school === "string"; - - hasValid.degree = stringValidator( - "body", - "application.general.degree", - false - ); - hasValid.fieldOfStudy = alphaArrayValidator( - "body", - "application.general.fieldOfStudy", - false - ); - hasValid.bus = booleanValidator( - "body", - "application.accomodation.needsBus", - false - ); - hasValid.ethnicity = alphaArrayValidator( - "body", - "application.other.ethnicity", - false - ); - hasValid.gender = stringValidator( - "body", - "application.other.gender", - false - ); - hasValid.graduationYear = integerValidator( - "body", - "application.general.graduationYear", - false, - 2019, - 2030 - ); - hasValid.codeOfConduct_MLH = booleanValidator( - "body", - "application.other.codeOfConduct_MLH", - false, - true - ); - hasValid.codeOfConduct_MCHACKS = booleanValidator( - "body", - "appliction.other.codeOfConduct_MCHACKS", - false, - true - ); const jobInterests = Constants.JOB_INTERESTS; hasValid.github = !app.general.URL.github || @@ -476,7 +418,7 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { !app.general.URL.other || typeof app.general.URL.other === "string"; hasValid.jobInterest = - !app.general.jobInterest || + !!app.general.jobInterest && jobInterests.includes(app.general.jobInterest); hasValid.skills = !app.shortAnswer.skills || @@ -485,24 +427,14 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { !app.shortAnswer.comments || typeof app.shortAnswer.comments === "string"; hasValid.question1 = - !app.shortAnswer.question1 || + !!app.shortAnswer.question1 && typeof app.shortAnswer.question1 === "string"; hasValid.question2 = - !app.shortAnswer.question2 || + !!app.shortAnswer.question2 && typeof app.shortAnswer.question2 === "string"; hasValid.team = !app.team || mongoose.Types.ObjectId.isValid(app.team); return ( - hasValid.comments && - hasValid.school && - hasValid.degree && - hasValid.fieldOfStudy && - hasValid.graduationYear && - hasValid.ethnicity && - hasValid.gender && - hasValid.bus && - hasValid.codeOfConduct_MLH && - hasValid.codeOfConduct_MCHACKS && hasValid.github && hasValid.dribbble && hasValid.personal && @@ -510,6 +442,9 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { hasValid.other && hasValid.jobInterest && hasValid.skills && + hasValid.comments && + hasValid.question1 && + hasValid.question2 && hasValid.team ); }) @@ -520,89 +455,12 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { } else { return application .custom((app) => { - // hasValid.school = stringValidator( - // "body", - // "application.general.school", - // false - // ); - // console.log(hasValid.school); - hasValid.school = - !app.general.school || - typeof app.general.school === "string"; - - // hasValid.degree = stringValidator( - // "body", - // "application.general.degree", - // false - // ); - hasValid.degree = - !app.general.degree || - typeof app.general.degree === "string"; - // hasValid.fieldOfStudy = alphaArrayValidator( - // "body", - // "application.general.fieldOfStudy", - // false - // ); - hasValid.bus = - !app.accomodation.needsBus || - (typeof app.accomodation.needsBus === "boolean" && - app.accomodation.needsBus === true); - - // hasValid.bus = booleanValidator( - // "body", - // "application.accomodation.needsBus", - // false - // ); - - hasValid.ethnicity = alphaArrayValidator( - "body", - "application.other.ethnicity", - false - ); - hasValid.gender = - !app.other.gender || typeof app.other.gender === "string"; - - // hasValid.gender = stringValidator( - // "body", - // "application.other.gender", - // false - // ); - hasValid.graduationYear = integerValidator( - "body", - "application.general.graduationYear", - false, - 2019, - 2030 - ); - - hasValid.codeOfConduct_MLH = - !app.other.codeOfConduct_MLH || - (typeof app.other.codeOfConduct_MLH === "boolean" && - app.other.codeOfConduct_MLH === true); - // hasValid.codeOfConduct_MLH = booleanValidator( - // "body", - // "application.other.codeOfConduct_MLH", - // false, - // true - // ); - - hasValid.codeOfConduct_MCHACKS = - !app.other.codeOfConduct_MCHACKS || - (typeof app.other.codeOfConduct_MCHACKS === "boolean" && - app.other.codeOfConduct_MCHACKS === true); - // hasValid.codeOfConduct_MCHACKS = booleanValidator( - // "body", - // "appliction.other.codeOfConduct_MCHACKS", - // false, - // true - // ); - console.log("Before"); const jobInterests = Constants.JOB_INTERESTS; hasValid.github = !app.general.URL.github || typeof app.general.URL.github === "string"; hasValid.dribbble = - !app.general.URL.dribbble || + !app.general.URL.hasOwnProperty("dribbble") || typeof app.general.URL.dribbble === "string"; hasValid.personal = !app.general.URL.personal || @@ -614,7 +472,7 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { !app.general.URL.other || typeof app.general.URL.other === "string"; hasValid.jobInterest = - !app.general.jobInterest || + !!app.general.jobInterest && jobInterests.includes(app.general.jobInterest); hasValid.skills = !app.shortAnswer.skills || @@ -623,32 +481,24 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { !app.shortAnswer.comments || typeof app.shortAnswer.comments === "string"; hasValid.question1 = - !app.shortAnswer.question1 || + !!app.shortAnswer.question1 && typeof app.shortAnswer.question1 === "string"; hasValid.question2 = - !app.shortAnswer.question2 || + !!app.shortAnswer.question2 && typeof app.shortAnswer.question2 === "string"; hasValid.team = !app.team || mongoose.Types.ObjectId.isValid(app.team); - console.log(hasValid.codeOfConduct_MCHACKS); return ( - hasValid.comments && - hasValid.school && - hasValid.degree && - hasValid.fieldOfStudy && - hasValid.graduationYear && - hasValid.ethnicity && - hasValid.gender && - hasValid.bus && - hasValid.codeOfConduct_MLH && - hasValid.codeOfConduct_MCHACKS && hasValid.github && hasValid.dribbble && hasValid.personal && hasValid.linkedIn && hasValid.other && hasValid.jobInterest && + hasValid.comments && hasValid.skills && + hasValid.question1 && + hasValid.question2 && hasValid.team ); }) diff --git a/tests/hacker.test.js b/tests/hacker.test.js index f92b0f8a..86fd4a60 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -458,26 +458,24 @@ describe("POST create hacker", function() { .type("application/json") .send(invalidHacker0) .end(function(err, res) { - console.log(res.body.data); + console.log(res.body.data.application.msg.isValid); res.should.have.status(422); res.should.be.json; res.body.should.have.property("message"); res.body.message.should.equal("Validation failed"); res.body.should.have.property("data"); - res.body.data.should.have.property("application"); - res.body.data.application.should.have.property("other"); - res.body.data.application.other.should.have.property( - "codeOfConduct_MLH" - ); - res.body.data.application.other.should.have.property( - "codeOfConduct_MCHACKS" - ); - res.body.data.application.other.codeOfConduct_MLH.msg.should.equal( - "Must be equal to true" + res.body.data.should.have.property( + "application.other.codeOfConduct_MLH" ); - res.body.data.application.other.codeOfConduct_MCHACKS.msh.should.equal( - "Must be equal to true" + res.body.data[ + "application.other.codeOfConduct_MLH" + ].msg.should.equal("Must be equal to true"); + res.body.data.should.have.property( + "application.other.codeOfConduct_MCHACKS" ); + res.body.data[ + "application.other.codeOfConduct_MCHACKS" + ].msg.should.equal("Must be equal to true"); done(); }); }); @@ -541,8 +539,35 @@ describe("POST create hacker", function() { .type("application/json") .send(invalidHacker1) .end(function(err, res) { - // replace with actual test comparisons after error handler is implemented + console.log(res.body.data.application.msg); res.should.have.status(422); + res.should.be.json; + res.body.should.have.property("message"); + res.body.message.should.equal("Validation failed"); + res.body.should.have.property("data"); + res.body.data.should.have.property("accountId"); + res.body.data.accountId.should.have.property("msg"); + res.body.data.accountId.msg.should.equal("invalid mongoID"); + res.body.data.should.have.property( + "application.general.school" + ); + res.body.data[ + "application.general.school" + ].should.have.property("msg"); + res.body.data[ + "application.general.school" + ].msg.should.equal("name must exist"); + res.body.data.should.have.property("application"); + res.body.data.application.should.have.property("msg"); + res.body.data.application.msg.should.have.property( + "isValid" + ); + res.body.data.application.msg.isValid.should.have.property( + "jobInterest" + ); + res.body.data.application.msg.isValid.jobInterest.should.equal( + false + ); done(); }); }); diff --git a/tests/util/hacker.test.util.js b/tests/util/hacker.test.util.js index 918d812c..9bd982c7 100644 --- a/tests/util/hacker.test.util.js +++ b/tests/util/hacker.test.util.js @@ -25,10 +25,10 @@ const TeamHacker0 = { //gcloud bucket link resume: "www.gcloud.com/myResume100", github: "www.github.com/Person1", - dribbble: undefined, + dribbble: null, personal: "www.person1.com", linkedIn: "www.linkedin.com/in/Person1", - other: undefined + other: null } }, shortAnswer: { @@ -64,10 +64,10 @@ const TeamHacker1 = { //gcloud bucket link resume: "www.gcloud.com/myResume2", github: "www.github.com/Personasdf", - dribbble: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined + dribbble: null, + personal: null, + linkedIn: null, + other: null } }, shortAnswer: { @@ -103,10 +103,10 @@ const TeamHacker2 = { //gcloud bucket link resume: "www.gcloud.com/myResume2", github: "www.github.com/Personasdf", - dribbble: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined + dribbble: null, + personal: null, + linkedIn: null, + other: null } }, shortAnswer: { @@ -142,10 +142,10 @@ const TeamHacker3 = { //gcloud bucket link resume: "www.gcloud.com/myResume2", github: "www.github.com/Personasdf", - dribbble: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined + dribbble: null, + personal: null, + linkedIn: null, + other: null } }, shortAnswer: { @@ -181,10 +181,10 @@ const TeamHacker4 = { //gcloud bucket link resume: "www.gcloud.com/myResume2", github: "www.github.com/Personasdf", - dribbble: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined + dribbble: null, + personal: null, + linkedIn: null, + other: null } }, shortAnswer: { @@ -220,10 +220,10 @@ const NoTeamHacker0 = { //gcloud bucket link resume: "www.gcloud.com/myResume1", github: "www.github.com/Person4", - dribbble: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined + dribbble: null, + personal: null, + linkedIn: null, + other: null } }, shortAnswer: { @@ -256,10 +256,10 @@ const newHacker0 = { //gcloud bucket link resume: "www.gcloud.com/myResume100", github: "www.github.com/Person1", - dribbble: undefined, + dribbble: null, personal: "www.person1.com", linkedIn: "www.linkedin.com/in/Person1", - other: undefined + other: null } }, shortAnswer: { @@ -292,10 +292,10 @@ const newHacker1 = { //gcloud bucket link resume: "www.gcloud.com/myResume100", github: "www.github.com/Person1", - dribbble: undefined, + dribbble: null, personal: "www.person1.com", linkedIn: "www.linkedin.com/in/Person1", - other: undefined + other: null } }, shortAnswer: { @@ -329,10 +329,10 @@ const invalidHacker0 = { //gcloud bucket link resume: "www.gcloud.com/myResume100", github: "www.github.com/Person1", - dribbble: undefined, + dribbble: null, personal: "www.person1.com", linkedIn: "www.linkedin.com/in/Person1", - other: undefined + other: null } }, shortAnswer: { @@ -367,6 +367,7 @@ const invalidHacker1 = { jobInterest: "ASDF", URL: { // invalid URL links with no resume + github: null } }, shortAnswer: { @@ -401,10 +402,10 @@ const duplicateAccountLinkHacker0 = { //gcloud bucket link resume: "www.gcloud.com/myResume100", github: "www.github.com/Person1", - dribbble: undefined, + dribbble: null, personal: "www.person1.com", linkedIn: "www.linkedin.com/in/Person1", - other: undefined + other: null } }, shortAnswer: { @@ -439,10 +440,10 @@ const waitlistedHacker0 = { //gcloud bucket link resume: "www.gcloud.com/myResume2", github: "www.github.com/Personasdf", - dribbble: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined + dribbble: null, + personal: null, + linkedIn: null, + other: null } }, shortAnswer: { @@ -478,10 +479,10 @@ const unconfirmedAccountHacker0 = { //gcloud bucket link resume: "www.gcloud.com/myResume123", github: "www.github.com/Personasdf", - dribbble: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined + dribbble: null, + personal: null, + linkedIn: null, + other: null } }, shortAnswer: { @@ -516,10 +517,10 @@ const unconfirmedAccountHacker1 = { //gcloud bucket link resume: "www.gcloud.com/myResume123", github: "www.github.com/Personasdf", - dropler: undefined, - personal: undefined, - linkedIn: undefined, - other: undefined + dropler: null, + personal: null, + linkedIn: null, + other: null } }, shortAnswer: { From 70efd5fd58d770207d85d9b49b2314353cfdb146 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Fri, 6 Dec 2019 15:48:56 -0500 Subject: [PATCH 15/28] Removed console.log statements --- middlewares/auth.middleware.js | 1 - middlewares/hacker.middleware.js | 1 - services/account.service.js | 2 -- tests/hacker.test.js | 2 -- 4 files changed, 6 deletions(-) diff --git a/middlewares/auth.middleware.js b/middlewares/auth.middleware.js index 54e34d07..ca2c26e4 100644 --- a/middlewares/auth.middleware.js +++ b/middlewares/auth.middleware.js @@ -65,7 +65,6 @@ function login(req, res, next) { */ function ensureAuthenticated() { return function(req, res, next) { - console.log("are we here"); if (req.isUnauthenticated()) { return next({ status: 401, diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index 52f17470..f4a75753 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -144,7 +144,6 @@ async function validateConfirmedStatus(account, next) { * @param {(err?) => void} next */ async function validateConfirmedStatusFromAccountId(req, res, next) { - console.log(req.body.accountId); const account = await Services.Account.findById(req.body.accountId); return validateConfirmedStatus(account, next); } diff --git a/services/account.service.js b/services/account.service.js index ae12f724..bafd0442 100644 --- a/services/account.service.js +++ b/services/account.service.js @@ -14,8 +14,6 @@ function findById(id) { const query = { _id: id }; - console.log(query); - return Account.findById( query, logger.queryCallbackFactory(TAG, "account", query) diff --git a/tests/hacker.test.js b/tests/hacker.test.js index 86fd4a60..0291f199 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -458,7 +458,6 @@ describe("POST create hacker", function() { .type("application/json") .send(invalidHacker0) .end(function(err, res) { - console.log(res.body.data.application.msg.isValid); res.should.have.status(422); res.should.be.json; res.body.should.have.property("message"); @@ -539,7 +538,6 @@ describe("POST create hacker", function() { .type("application/json") .send(invalidHacker1) .end(function(err, res) { - console.log(res.body.data.application.msg); res.should.have.status(422); res.should.be.json; res.body.should.have.property("message"); From 90acd4a9b852c5e5fbc02d3b0d2f9c3f48f2441f Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Fri, 6 Dec 2019 17:19:33 -0500 Subject: [PATCH 16/28] Final commit, all tests passed locally --- middlewares/validators/hacker.validator.js | 45 +++++++++++++++++++++- middlewares/validators/validator.helper.js | 2 +- tests/util/hacker.test.util.js | 1 - 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/middlewares/validators/hacker.validator.js b/middlewares/validators/hacker.validator.js index 5280a1ce..2af40875 100644 --- a/middlewares/validators/hacker.validator.js +++ b/middlewares/validators/hacker.validator.js @@ -39,7 +39,11 @@ module.exports = { false, true ), - VALIDATOR.booleanValidator("body", "application.accomodation.needsBus"), + VALIDATOR.booleanValidator( + "body", + "application.accomodation.needsBus", + true + ), VALIDATOR.mongoIdValidator("body", "teamId", true) ], @@ -48,7 +52,44 @@ module.exports = { ], updateHackerValidator: [ - VALIDATOR.applicationValidator("body", "application", true) + VALIDATOR.stringValidator("body", "application.general.school", true), + VALIDATOR.stringValidator("body", "application.general.degree", true), + VALIDATOR.alphaArrayValidator( + "body", + "application.general.fieldOfStudy", + true + ), + VALIDATOR.integerValidator( + "body", + "application.general.graduationYear", + true, + 2019, + 2030 + ), + VALIDATOR.applicationValidator("body", "application", true), + VALIDATOR.stringValidator("body", "application.other.gender", true), + VALIDATOR.alphaArrayValidator( + "body", + "application.other.ethnicity", + true + ), + VALIDATOR.booleanValidator( + "body", + "application.other.codeOfConduct_MLH", + true, + true + ), + VALIDATOR.booleanValidator( + "body", + "application.other.codeOfConduct_MCHACKS", + true, + true + ), + VALIDATOR.booleanValidator( + "body", + "application.accomodation.needsBus", + true + ) ], updateStatusValidator: [ VALIDATOR.enumValidator( diff --git a/middlewares/validators/validator.helper.js b/middlewares/validators/validator.helper.js index 16c3f46d..6c6808fc 100644 --- a/middlewares/validators/validator.helper.js +++ b/middlewares/validators/validator.helper.js @@ -460,7 +460,7 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { !app.general.URL.github || typeof app.general.URL.github === "string"; hasValid.dribbble = - !app.general.URL.hasOwnProperty("dribbble") || + !app.general.URL.dribbble || typeof app.general.URL.dribbble === "string"; hasValid.personal = !app.general.URL.personal || diff --git a/tests/util/hacker.test.util.js b/tests/util/hacker.test.util.js index 9bd982c7..beb42ffe 100644 --- a/tests/util/hacker.test.util.js +++ b/tests/util/hacker.test.util.js @@ -367,7 +367,6 @@ const invalidHacker1 = { jobInterest: "ASDF", URL: { // invalid URL links with no resume - github: null } }, shortAnswer: { From 3bb8c2818663f845741825c39a700c13a8abf6ac Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Sat, 7 Dec 2019 21:02:36 -0500 Subject: [PATCH 17/28] Fixing tests again and updating application and account models --- controllers/account.controller.js | 2 +- middlewares/account.middleware.js | 10 +- middlewares/hacker.middleware.js | 1 - middlewares/parse-body.middleware.js | 1 + middlewares/validators/account.validator.js | 18 +-- middlewares/validators/hacker.validator.js | 37 ++++-- models/account.model.js | 14 +-- models/hacker.model.js | 27 +++-- routes/api/account.js | 14 --- routes/api/hacker.js | 24 ++-- services/account.service.js | 4 +- services/hacker.service.js | 23 ++-- tests/account.test.js | 6 +- tests/hacker.test.js | 8 +- tests/search.service.spec.js | 10 +- tests/util/account.test.util.js | 40 ++----- tests/util/hacker.test.util.js | 126 +++++++++++--------- 17 files changed, 174 insertions(+), 191 deletions(-) diff --git a/controllers/account.controller.js b/controllers/account.controller.js index 2a58d364..0b891f46 100644 --- a/controllers/account.controller.js +++ b/controllers/account.controller.js @@ -25,7 +25,7 @@ function showAccount(req, res) { /** * @function addUser - * @param {{body: {accountDetails: {_id: ObjectId, firstName: string, lastName: string, email: string, password: string, dietaryRestrictions: string, shirtSize: string}}}} req + * @param {{body: {accountDetails: {_id: ObjectId, firstName: string, lastName: string, email: string, password: string}}}} req * @param {*} res * @return {JSON} Success or error status * @description Adds a user from information in req.body.accountDetails diff --git a/middlewares/account.middleware.js b/middlewares/account.middleware.js index 806126f7..3b609e8e 100644 --- a/middlewares/account.middleware.js +++ b/middlewares/account.middleware.js @@ -34,12 +34,12 @@ function parsePatch(req, res, next) { /** * @function parseAccount - * @param {{body: {firstName: string, lastName: string, email: string, password: string, dietaryRestrictions: string, shirtSize: string}}} req + * @param {{body: {firstName: string, lastName: string, email: string, password: string}}} req * @param {*} res * @param {(err?)=>void} next * @return {void} * @description - * Moves firstName, lastName, email, password, dietaryRestrictions, shirtSize from req.body to req.body.accountDetails. + * Moves firstName, lastName, email, password from req.body to req.body.accountDetails. * Hashes the password. * Adds _id to accountDetails. */ @@ -49,10 +49,9 @@ function parseAccount(req, res, next) { firstName: req.body.firstName, lastName: req.body.lastName, pronoun: req.body.pronoun, + gender: req.body.gender, email: req.body.email, password: Services.Account.hashPassword(req.body.password), - dietaryRestrictions: req.body.dietaryRestrictions, - shirtSize: req.body.shirtSize, birthDate: req.body.birthDate, phoneNumber: req.body.phoneNumber }; @@ -60,10 +59,9 @@ function parseAccount(req, res, next) { delete req.body.firstName; delete req.body.lastName; delete req.body.pronoun; + delete req.body.gender; delete req.body.email; delete req.body.password; - delete req.body.dietaryRestrictions; - delete req.body.shirtSize; delete req.body.birthDate; delete req.body.phoneNumber; diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index f4a75753..a5b9b311 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -542,7 +542,6 @@ async function createHacker(req, res, next) { }); } const hacker = await Services.Hacker.createHacker(hackerDetails); - if (!!hacker) { req.body.hacker = hacker; return next(); diff --git a/middlewares/parse-body.middleware.js b/middlewares/parse-body.middleware.js index b24f1155..29c86ec7 100644 --- a/middlewares/parse-body.middleware.js +++ b/middlewares/parse-body.middleware.js @@ -18,6 +18,7 @@ module.exports = { function middleware(req, res, next) { const errors = validationResult(req); if (!errors.isEmpty()) { + console.log(errors); return next({ status: 422, message: Constants.Error.VALIDATION_422_MESSAGE, diff --git a/middlewares/validators/account.validator.js b/middlewares/validators/account.validator.js index 0828d529..1eea19ee 100644 --- a/middlewares/validators/account.validator.js +++ b/middlewares/validators/account.validator.js @@ -7,14 +7,9 @@ module.exports = { VALIDATOR.stringValidator("body", "firstName", false), VALIDATOR.stringValidator("body", "lastName", false), VALIDATOR.stringValidator("body", "pronoun", false), + VALIDATOR.stringValidator("body", "gender", false), VALIDATOR.regexValidator("body", "email", false, Constants.EMAIL_REGEX), - VALIDATOR.alphaArrayValidator("body", "dietaryRestrictions", false), - VALIDATOR.enumValidator( - "body", - "shirtSize", - Constants.SHIRT_SIZES, - false - ), + VALIDATOR.passwordValidator("body", "password", false), VALIDATOR.jwtValidator( "header", @@ -29,14 +24,9 @@ module.exports = { VALIDATOR.stringValidator("body", "firstName", true), VALIDATOR.stringValidator("body", "lastName", true), VALIDATOR.stringValidator("body", "pronoun", true), + VALIDATOR.stringValidator("body", "gender", true), VALIDATOR.regexValidator("body", "email", true, Constants.EMAIL_REGEX), - VALIDATOR.alphaArrayValidator("body", "dietaryRestrictions", true), - VALIDATOR.enumValidator( - "body", - "shirtSize", - Constants.SHIRT_SIZES, - true - ), + VALIDATOR.dateValidator("body", "birthDate", true), VALIDATOR.phoneNumberValidator("body", "phoneNumber", true) ], diff --git a/middlewares/validators/hacker.validator.js b/middlewares/validators/hacker.validator.js index 2af40875..64fd6f35 100644 --- a/middlewares/validators/hacker.validator.js +++ b/middlewares/validators/hacker.validator.js @@ -20,8 +20,19 @@ module.exports = { 2019, 2030 ), + + VALIDATOR.alphaArrayValidator( + "body", + "application.accommodation.dietaryRestrictions", + true + ), + VALIDATOR.enumValidator( + "body", + "application.accommodation.shirtSize", + Constants.SHIRT_SIZES, + true + ), VALIDATOR.applicationValidator("body", "application", false), - VALIDATOR.stringValidator("body", "application.other.gender", false), VALIDATOR.alphaArrayValidator( "body", "application.other.ethnicity", @@ -29,19 +40,19 @@ module.exports = { ), VALIDATOR.booleanValidator( "body", - "application.other.codeOfConduct_MLH", + "application.other.privacyPolicy", false, true ), VALIDATOR.booleanValidator( "body", - "application.other.codeOfConduct_MCHACKS", + "application.other.codeOfConduct", false, true ), VALIDATOR.booleanValidator( "body", - "application.accomodation.needsBus", + "application.accommodation.needsBus", true ), VALIDATOR.mongoIdValidator("body", "teamId", true) @@ -66,8 +77,18 @@ module.exports = { 2019, 2030 ), + VALIDATOR.alphaArrayValidator( + "body", + "application.accommodation.dietaryRestrictions", + true + ), + VALIDATOR.enumValidator( + "body", + "application.accommodation.shirtSize", + Constants.SHIRT_SIZES, + true + ), VALIDATOR.applicationValidator("body", "application", true), - VALIDATOR.stringValidator("body", "application.other.gender", true), VALIDATOR.alphaArrayValidator( "body", "application.other.ethnicity", @@ -75,19 +96,19 @@ module.exports = { ), VALIDATOR.booleanValidator( "body", - "application.other.codeOfConduct_MLH", + "application.other.privacyPolicy", true, true ), VALIDATOR.booleanValidator( "body", - "application.other.codeOfConduct_MCHACKS", + "application.other.codeOfConduct", true, true ), VALIDATOR.booleanValidator( "body", - "application.accomodation.needsBus", + "application.accommodation.needsBus", true ) ], diff --git a/models/account.model.js b/models/account.model.js index 87e2b9e1..778d068b 100644 --- a/models/account.model.js +++ b/models/account.model.js @@ -16,6 +16,10 @@ const AccountSchema = new mongoose.Schema({ type: String, default: "Prefer not to say" }, + gender: { + type: String, + required: true + }, email: { type: String, trim: true, @@ -28,16 +32,6 @@ const AccountSchema = new mongoose.Schema({ type: String, required: true }, - dietaryRestrictions: [ - { - type: String - } - ], - shirtSize: { - type: String, - enum: Constants.SHIRT_SIZES, - required: true - }, confirmed: { type: Boolean, default: false diff --git a/models/hacker.model.js b/models/hacker.model.js index d122446c..5b2b17f9 100644 --- a/models/hacker.model.js +++ b/models/hacker.model.js @@ -98,22 +98,31 @@ const HackerSchema = new mongoose.Schema({ ], required: true }, - gender: { - type: String, - required: true - }, - codeOfConduct_MLH: { + privacyPolicy: { type: Boolean, required: true }, - codeOfConduct_MCHACKS: { + codeOfConduct: { type: Boolean, required: true } }, - accomodation: { - needsBus: Boolean, - default: false + accommodation: { + dietaryRestrictions: { + type: [ + { + type: String + } + ], + required: true + }, + + shirtSize: { + type: String, + enum: Constants.SHIRT_SIZES, + required: true + }, + needsBus: { type: Boolean, default: false } }, team: { type: mongoose.Schema.Types.ObjectId, diff --git a/routes/api/account.js b/routes/api/account.js index 33e31d87..41528799 100644 --- a/routes/api/account.js +++ b/routes/api/account.js @@ -41,9 +41,7 @@ module.exports = { "lastName":"Klein", "pronoun":"he/him", "email":"theo@klein.com", - "dietaryRestrictions":["Halal"], "phoneNumber":1234567890, - "shirtSize":"S", "birthDate":Date("10/30/1997") } } @@ -70,8 +68,6 @@ module.exports = { * @apiParam (body) {String} lastName Last name of the account creator. * @apiParam (body) {String} pronoun the pronoun of the account creator. * @apiParam (body) {String} email Email of the account. - * @apiParam (body) {String[]} dietaryRestrictions Any dietary restrictions for the user. 'None' if there are no restrictions - * @apiParam (body) {String} shirtSize Size of the shirt that the user will receive. * @apiParam (body) {String} password The password of the account. * @apiParam (body) {String} birthDate a Date parsable string. * @apiParam (body) {Number} phoneNumber the user's phone number, represented as a string. @@ -84,9 +80,7 @@ module.exports = { "pronoun":"he/him", "email":"theo@klein.com", "password":"hunter2", - "dietaryRestrictions":["Halal"], "phoneNumber":1234567890, - "shirtSize":"S", "birthDate":"10/30/1997" * } * @@ -101,9 +95,7 @@ module.exports = { "lastName":"Klein", "pronoun":"he/him", "email":"theo@klein.com", - "dietaryRestrictions":["Halal"], "phoneNumber":1234567890, - "shirtSize":"S", "birthDate":Date("10/30/1997") } } @@ -214,8 +206,6 @@ module.exports = { * @apiParam (body) {String} [lastName] Last name of the account creator. * @apiParam (body) {String} [pronoun] the pronoun of the account creator. * @apiParam (body) {String} [email] Email of the account. - * @apiParam (body) {String[]} [dietaryRestrictions] Any dietary restrictions for the user. 'None' if there are no restrictions - * @apiParam (body) {String} [shirtSize] Size of the shirt that the user will receive. * @apiParam (body) {String} [birthDate] a Date parsable string. * @apiParam (body) {Number} [phoneNumber] the user's phone number, represented as a string. @@ -234,9 +224,7 @@ module.exports = { "lastName":"Klein", "pronoun":"he/him", "email":"theo@klein.com", - "dietaryRestrictions":["Halal"], "phoneNumber":1234567890, - "shirtSize":"M", "birthDate":Date("10/30/1997") } } @@ -282,9 +270,7 @@ module.exports = { "lastName":"Klein", "pronoun":"he/him", "email":"theo@klein.com", - "dietaryRestrictions":["Halal"], "phoneNumber":1234567890, - "shirtSize":"S", "birthDate":Date("10/30/1997") } } diff --git a/routes/api/hacker.js b/routes/api/hacker.js index 8954c038..0c1357f5 100644 --- a/routes/api/hacker.js +++ b/routes/api/hacker.js @@ -120,8 +120,8 @@ module.exports = { "other:" { "gender": "male", "ethnicity": "Asian or Pacific Islander", - "codeOfConduct_MLH": true, - "codeOfConduct_MCHACKS": true, + "privacyPolicy": true, + "codeOfConduct": true, } "accomodation": { "needsBus": "false" @@ -162,8 +162,8 @@ module.exports = { "other:" { "gender": "male", "ethnicity": "Asian or Pacific Islander", - "codeOfConduct_MLH": true, - "codeOfConduct_MCHACKS": true, + "privacyPolicy": true, + "codeOfConduct": true, } "accomodation": { "needsBus": "false" @@ -356,8 +356,8 @@ module.exports = { "other:" { "gender": "male", "ethnicity": "Asian or Pacific Islander", - "codeOfConduct_MLH": true, - "codeOfConduct_MCHACKS": true, + "privacyPolicy": true, + "codeOfConduct": true, } "accomodation": { "needsBus": "false" @@ -398,8 +398,8 @@ module.exports = { "other:" { "gender": "male", "ethnicity": "Asian or Pacific Islander", - "codeOfConduct_MLH": true, - "codeOfConduct_MCHACKS": true, + "privacyPolicy": true, + "codeOfConduct": true, } "accomodation": { "needsBus": "false" @@ -469,8 +469,8 @@ module.exports = { "other:" { "gender": "male", "ethnicity": "Asian or Pacific Islander", - "codeOfConduct_MLH": true, - "codeOfConduct_MCHACKS": true, + "privacyPolicy": true, + "codeOfConduct": true, } "accomodation": { "needsBus": "false" @@ -536,8 +536,8 @@ module.exports = { "other:" { "gender": "male", "ethnicity": "Asian or Pacific Islander", - "codeOfConduct_MLH": true, - "codeOfConduct_MCHACKS": true, + "privacyPolicy": true, + "codeOfConduct": true, } "accomodation": { "needsBus": "false" diff --git a/services/account.service.js b/services/account.service.js index bafd0442..b11bd79f 100644 --- a/services/account.service.js +++ b/services/account.service.js @@ -75,7 +75,7 @@ function findOne(query) { /** * @function addOneAccount - * @param {{_id: ObjectId, firstName: string, lastName: string, email: string, password: string, dietaryRestrictions: string, shirtSize: string}} accountDetails + * @param {{_id: ObjectId, firstName: string, lastName: string, email: string, password: string}} accountDetails * @return {Promise} The promise will resolve to the account object if save is successful. * @description Adds a new account to database. */ @@ -90,7 +90,7 @@ function addOneAccount(accountDetails) { /** * @function updateOne * @param {ObjectId} id - * @param {{_id?: ObjectId, firstName?: string, lastName?: string, email?: string, password?: string, dietaryRestrictions?: string, shirtSize?: string}} accountDetails + * @param {{_id?: ObjectId, firstName?: string, lastName?: string, email?: string, password?: string}} accountDetails * @return {DocumentQuery} The document query will resolve to either account or null. * @description Changes account information to the specified information in accountDetails. */ diff --git a/services/hacker.service.js b/services/hacker.service.js index 1126d916..037947cc 100644 --- a/services/hacker.service.js +++ b/services/hacker.service.js @@ -166,14 +166,14 @@ function getStats(hackers) { ] ? stats.degree[hacker.application.general.degree] + 1 : 1; - stats.gender[hacker.application.other.gender] = stats.gender[ - hacker.application.other.gender + stats.gender[hacker.accountId.gender] = stats.gender[ + hacker.accountId.gender ] - ? stats.gender[hacker.application.other.gender] + 1 + ? stats.gender[hacker.accountId.gender] + 1 : 1; - stats.needsBus[hacker.application.accomodation.needsBus] = stats - .needsBus[hacker.application.accomodation.needsBus] - ? stats.needsBus[hacker.application.accomodation.needsBus] + 1 + stats.needsBus[hacker.application.accommodation.needsBus] = stats + .needsBus[hacker.application.accommodation.needsBus] + ? stats.needsBus[hacker.application.accommodation.needsBus] + 1 : 1; for (const ethnicity of hacker.application.other.ethnicity) { @@ -196,17 +196,16 @@ function getStats(hackers) { 1 : 1; - for (const dietaryRestrictions of hacker.accountId - .dietaryRestrictions) { + for (const dietaryRestrictions of hacker.application.accommodation + .dietaryRestrictions.dietaryRestrictions) { stats.dietaryRestrictions[dietaryRestrictions] = stats .dietaryRestrictions[dietaryRestrictions] ? stats.dietaryRestrictions[dietaryRestrictions] + 1 : 1; } - stats.shirtSize[hacker.accountId.shirtSize] = stats.shirtSize[ - hacker.accountId.shirtSize - ] - ? stats.shirtSize[hacker.accountId.shirtSize] + 1 + stats.shirtSize[hacker.application.accommodation.shirtSize] = stats + .shirtSize[hacker.application.accommodation.shirtSize] + ? stats.shirtSize[hacker.application.accommodation.shirtSize] + 1 : 1; const age = hacker.accountId.getAge(); stats.age[age] = stats.age[age] ? stats.age[age] + 1 : 1; diff --git a/tests/account.test.js b/tests/account.test.js index 947f4ea9..11d900a9 100644 --- a/tests/account.test.js +++ b/tests/account.test.js @@ -95,10 +95,6 @@ describe("GET user account", function() { res.body.data.should.have.property("firstName"); res.body.data.should.have.property("lastName"); res.body.data.should.have.property("email"); - res.body.data.should.have.property( - "dietaryRestrictions" - ); - res.body.data.should.have.property("shirtSize"); done(); }) ); @@ -211,6 +207,8 @@ describe("POST create account", function() { .type("application/json") .send(newAccount0) .end(function(err, res) { + console.log(res.body); + console.log(newAccount0); res.should.have.status(200); res.should.be.json; res.body.should.have.property("message"); diff --git a/tests/hacker.test.js b/tests/hacker.test.js index 0291f199..ce0e03fc 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -464,16 +464,16 @@ describe("POST create hacker", function() { res.body.message.should.equal("Validation failed"); res.body.should.have.property("data"); res.body.data.should.have.property( - "application.other.codeOfConduct_MLH" + "application.other.privacyPolicy" ); res.body.data[ - "application.other.codeOfConduct_MLH" + "application.other.privacyPolicy" ].msg.should.equal("Must be equal to true"); res.body.data.should.have.property( - "application.other.codeOfConduct_MCHACKS" + "application.other.codeOfConduct" ); res.body.data[ - "application.other.codeOfConduct_MCHACKS" + "application.other.codeOfConduct" ].msg.should.equal("Must be equal to true"); done(); }); diff --git a/tests/search.service.spec.js b/tests/search.service.spec.js index b494ae3d..9e04f89b 100644 --- a/tests/search.service.spec.js +++ b/tests/search.service.spec.js @@ -22,7 +22,7 @@ const util = { const queryToExecute = [ { - param: "application.other.gender", + param: "gender", operation: "equals", value: "Female" } @@ -64,7 +64,7 @@ describe("Searching for hackers", function() { return agent .get("/api/search") .query({ - model: "hacker", + model: "account", q: JSON.stringify(queryToExecute) }) .end(function(err, res) { @@ -88,7 +88,7 @@ describe("Searching for hackers", function() { return agent .get("/api/search") .query({ - model: "hacker", + model: "account", q: JSON.stringify(queryToExecute) }) .end(function(err, res) { @@ -109,7 +109,7 @@ describe("Searching for hackers", function() { return agent .get("/api/search") .query({ - model: "hacker", + model: "account", q: JSON.stringify(queryToExecute) }) .end(function(err, res) { @@ -296,7 +296,7 @@ describe("Searching for hackers", function() { return agent .get("/api/search") .query({ - model: "hacker", + model: "account", q: JSON.stringify(queryToExecute), expand: true }) diff --git a/tests/util/account.test.util.js b/tests/util/account.test.util.js index 3dfd2021..bec9e3c0 100644 --- a/tests/util/account.test.util.js +++ b/tests/util/account.test.util.js @@ -51,6 +51,8 @@ function generateRandomValue(atr) { .toString(36) .replace(/[^a-z]+/g, "") .substr(0, Math.floor(Math.random() * 3 + 2)); + case "gender": + return "Female"; case "email": const email = `abc.def${counters.emailCounter}@blahblah.com`; return email; @@ -58,19 +60,6 @@ function generateRandomValue(atr) { return Math.random() .toString(36) .substr(0, 10); - case "dietaryRestrictions": - return [ - Constants.SAMPLE_DIET_RESTRICTIONS[ - Math.floor( - Math.random() * - Constants.SAMPLE_DIET_RESTRICTIONS.length - ) - ] - ]; - case "shirtSize": - return Constants.SHIRT_SIZES[ - Math.floor(Math.random() * Constants.SHIRT_SIZES.length) - ]; case "confirmed": // return false, because if an account is confirmed there should be a document of that account type, // which this does not create @@ -88,7 +77,6 @@ function generateRandomValue(atr) { function createAccount(acc = {}) { incrementCounters(); - const extractedAcc = extractAccountInfo(acc); for (const atr in Account.schema.paths) { @@ -254,10 +242,9 @@ const waitlistedHacker0 = { firstName: "abcd", lastName: "defg3", pronoun: "They/Them", + gender: "Female", email: "waitlisted1@blahblah.com", password: "probsShouldBeHashed2", - dietaryRestrictions: ["vegetarian"], - shirtSize: "M", confirmed: true, accountType: Constants.HACKER, birthDate: "1990-01-04", @@ -270,10 +257,9 @@ const NonConfirmedAccount1 = { firstName: "LMAO", lastName: "ROFL", pronoun: "Ey/Em", + gender: "Female", email: "notconfirmed1@blahblah.com", password: "probsShouldBeHashed5", - dietaryRestrictions: ["something1", "something2"], - shirtSize: "XXL", confirmed: false, birthDate: "1980-07-30", phoneNumber: 1001230236, @@ -284,10 +270,9 @@ const NonConfirmedAccount2 = { _id: mongoose.Types.ObjectId(), firstName: "LMAO", lastName: "ROFL", + gender: "Female", email: "notconfirmed2@blahblah.com", password: "probsShouldBeHashed5", - dietaryRestrictions: ["something1", "something2"], - shirtSize: "XXL", confirmed: false, accountType: Constants.HACKER }; @@ -397,17 +382,6 @@ function equals(acc1, acc2) { const lastName = acc1.lastName === acc2.lastName; const pronoun = acc1.pronoun === acc2.pronoun; const email = acc1.email === acc2.email; - const dietaryRestrictions = - acc1.dietaryRestrictions.join(",") === - acc2.dietaryRestrictions.join(","); - const shirtSize = acc1.shirtSize === acc2.shirtSize; - return [ - id, - firstName, - lastName, - email, - dietaryRestrictions, - shirtSize, - pronoun - ]; + const gender = acc1.gender === acc2.gender; + return [id, firstName, lastName, email, pronoun, gender]; } diff --git a/tests/util/hacker.test.util.js b/tests/util/hacker.test.util.js index beb42ffe..d8f1ef70 100644 --- a/tests/util/hacker.test.util.js +++ b/tests/util/hacker.test.util.js @@ -37,12 +37,13 @@ const TeamHacker0 = { question2: "a" }, other: { - gender: "Male", ethnicity: ["Native American"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true + codeOfConduct: true, + privacyPolicy: true }, - accomodation: { + accommodation: { + dietaryRestrictions: ["Gluten-Free"], + shirtSize: "L", needsBus: true } }, @@ -76,12 +77,13 @@ const TeamHacker1 = { question2: "a" }, other: { - gender: "Female", ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true + codeOfConduct: true, + privacyPolicy: true }, - accomodation: { + accommodation: { + dietaryRestrictions: ["Gluten-Free"], + shirtSize: "L", needsBus: false } }, @@ -115,12 +117,13 @@ const TeamHacker2 = { question2: "a" }, other: { - gender: "Female", ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true + codeOfConduct: true, + privacyPolicy: true }, - accomodation: { + accommodation: { + dietaryRestrictions: ["Gluten-Free"], + shirtSize: "L", needsBus: false } }, @@ -154,12 +157,13 @@ const TeamHacker3 = { question2: "a" }, other: { - gender: "Female", ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true + codeOfConduct: true, + privacyPolicy: true }, - accomodation: { + accommodation: { + dietaryRestrictions: ["Gluten-Free"], + shirtSize: "L", needsBus: false } }, @@ -193,12 +197,13 @@ const TeamHacker4 = { question2: "a" }, other: { - gender: "Female", ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true + codeOfConduct: true, + privacyPolicy: true }, - accomodation: { + accommodation: { + dietaryRestrictions: ["Gluten-Free"], + shirtSize: "L", needsBus: false } }, @@ -232,12 +237,13 @@ const NoTeamHacker0 = { question2: "a" }, other: { - gender: "Female", ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true + codeOfConduct: true, + privacyPolicy: true }, - accomodation: { + accommodation: { + dietaryRestrictions: ["Gluten-Free"], + shirtSize: "L", needsBus: false } } @@ -268,12 +274,13 @@ const newHacker0 = { question2: "a" }, other: { - gender: "Female", ethnicity: ["Caucasian"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true + codeOfConduct: true, + privacyPolicy: true }, - accomodation: { + accommodation: { + dietaryRestrictions: ["Gluten-Free"], + shirtSize: "L", needsBus: false } } @@ -304,12 +311,13 @@ const newHacker1 = { question2: "a" }, other: { - gender: "Female", ethnicity: ["African American"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true + codeOfConduct: true, + privacyPolicy: true }, - accomodation: { + accommodation: { + dietaryRestrictions: ["Gluten-Free"], + shirtSize: "L", needsBus: true } } @@ -341,13 +349,14 @@ const invalidHacker0 = { question2: "a" }, other: { - gender: "Female", ethnicity: ["Caucasian"], // must accept code of conduct to be valid - codeOfConduct_MCHACKS: false, - codeOfConduct_MLH: false + codeOfConduct: false, + privacyPolicy: false }, - accomodation: { + accommodation: { + dietaryRestrictions: ["Gluten-Free"], + shirtSize: "L", needsBus: true } } @@ -375,12 +384,13 @@ const invalidHacker1 = { question2: "a" }, other: { - gender: "Female", ethnicity: ["Caucasian"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true + codeOfConduct: true, + privacyPolicy: true }, - accomodation: { + accommodation: { + dietaryRestrictions: ["Gluten-Free"], + shirtSize: "L", needsBus: true } } @@ -413,12 +423,13 @@ const duplicateAccountLinkHacker0 = { question2: "a" }, other: { - gender: "Male", ethnicity: ["Caucasian"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true + codeOfConduct: true, + privacyPolicy: true }, - accomodation: { + accommodation: { + dietaryRestrictions: ["Gluten-Free"], + shirtSize: "L", needsBus: true } } @@ -451,12 +462,13 @@ const waitlistedHacker0 = { question2: "a" }, other: { - gender: "Female", ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true + codeOfConduct: true, + privacyPolicy: true }, - accomodation: { + accommodation: { + dietaryRestrictions: ["Gluten-Free"], + shirtSize: "L", needsBus: false } }, @@ -490,12 +502,13 @@ const unconfirmedAccountHacker0 = { question2: "a" }, other: { - gender: "Female", ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true + codeOfConduct: true, + privacyPolicy: true }, - accomodation: { + accommodation: { + dietaryRestrictions: ["Gluten-Free"], + shirtSize: "L", needsBus: false } } @@ -528,12 +541,13 @@ const unconfirmedAccountHacker1 = { question2: "a" }, other: { - gender: "Female", ethnicity: ["European"], - codeOfConduct_MCHACKS: true, - codeOfConduct_MLH: true + codeOfConduct: true, + privacyPolicy: true }, - accomodation: { + accommodation: { + dietaryRestrictions: ["Gluten-Free"], + shirtSize: "L", needsBus: false } } From ce49ad130885279a588af079ad1589626d29c5e8 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Sat, 7 Dec 2019 21:34:02 -0500 Subject: [PATCH 18/28] Fixing more tests --- controllers/hacker.controller.js | 1 + middlewares/hacker.middleware.js | 1 + middlewares/validators/validator.helper.js | 26 +++++++++++----------- services/hacker.service.js | 4 ++-- tests/hacker.test.js | 14 +++++++----- 5 files changed, 26 insertions(+), 20 deletions(-) diff --git a/controllers/hacker.controller.js b/controllers/hacker.controller.js index bbcdd3bd..214d4cfd 100644 --- a/controllers/hacker.controller.js +++ b/controllers/hacker.controller.js @@ -70,6 +70,7 @@ function downloadedResume(req, res) { } function gotStats(req, res) { + console.log("gotStats?"); return res.status(200).json({ message: "Retrieved stats", data: { diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index a5b9b311..ec77f2b8 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -26,6 +26,7 @@ const Constants = { * @description Delete the req.body.id that was added by the validation of route parameter. */ function parsePatch(req, res, next) { + console.log(req.body); delete req.body.id; return next(); } diff --git a/middlewares/validators/validator.helper.js b/middlewares/validators/validator.helper.js index 6c6808fc..9c6712bc 100644 --- a/middlewares/validators/validator.helper.js +++ b/middlewares/validators/validator.helper.js @@ -33,7 +33,7 @@ function integerValidator( const value = setProperValidationChainBuilder( fieldLocation, fieldname, - "invalid integer" + "Invalid integer" ); if (optional) { @@ -74,7 +74,7 @@ function mongoIdValidator(fieldLocation, fieldname, optional = true) { const mongoId = setProperValidationChainBuilder( fieldLocation, fieldname, - "invalid mongoID" + "Invalid mongoID" ); if (optional) { @@ -102,7 +102,7 @@ function mongoIdArrayValidator(fieldLocation, fieldname, optional = true) { const arr = setProperValidationChainBuilder( fieldLocation, fieldname, - "invalid mongoID array" + "Invalid mongoID array" ); if (optional) { @@ -135,7 +135,7 @@ function booleanValidator( const booleanField = setProperValidationChainBuilder( fieldLocation, fieldname, - "invalid boolean" + "Invalid boolean" ); if (optional) { @@ -176,7 +176,7 @@ function asciiValidator(fieldLocation, fieldname, optional = true) { const name = setProperValidationChainBuilder( fieldLocation, fieldname, - "invalid name" + "Invalid name" ); if (optional) { return name @@ -240,7 +240,7 @@ function regexValidator( const match = setProperValidationChainBuilder( fieldLocation, fieldname, - "invalid name" + "Invalid name" ); if (optional) { @@ -269,7 +269,7 @@ function alphaValidator(fieldLocation, fieldname, optional = true) { const name = setProperValidationChainBuilder( fieldLocation, fieldname, - "invalid alpha string" + "Invalid alpha string" ); if (optional) { return name @@ -296,7 +296,7 @@ function alphaArrayValidator(fieldLocation, fieldname, optional = true) { const name = setProperValidationChainBuilder( fieldLocation, fieldname, - "invalid alpha array" + "Invalid alpha array" ); if (optional) { @@ -346,7 +346,7 @@ function passwordValidator(fieldLocation, fieldname, optional = true) { const password = setProperValidationChainBuilder( fieldLocation, fieldname, - "invalid password" + "Invalid password" ); if (optional) { return password @@ -378,7 +378,7 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { const application = setProperValidationChainBuilder( fieldLocation, fieldname, - "invalid application" + "Invalid application" ); //helper object to iterate through the items in the application and track which items are not valid. const hasValid = { @@ -707,7 +707,7 @@ function dateValidator(fieldLocation, fieldname, optional = true) { const date = setProperValidationChainBuilder( fieldLocation, fieldname, - "invalid date" + "Invalid date" ); if (optional) { return date @@ -747,7 +747,7 @@ function phoneNumberValidator(fieldLocation, fieldname, optional = true) { const number = setProperValidationChainBuilder( fieldLocation, fieldname, - "invalid phone number" + "Invalid phone number" ); if (optional) { return number @@ -875,7 +875,7 @@ function checkEnum(value, enums) { * * @param {"query" | "body" | "header" | "param"} fieldLocation the location where the field should be found * @param {string} fieldname name of the field that needs to be validated. - * @param {*} errorString the string that is sent back to the user if the field is invalid + * @param {*} errorString the string that is sent back to the user if the field is Invalid */ function setProperValidationChainBuilder( fieldLocation, diff --git a/services/hacker.service.js b/services/hacker.service.js index 037947cc..88848266 100644 --- a/services/hacker.service.js +++ b/services/hacker.service.js @@ -35,6 +35,7 @@ function updateOne(id, hackerDetails) { const query = { _id: id }; + console.log(query, hackerDetails); return Hacker.findOneAndUpdate( query, @@ -195,9 +196,8 @@ function getStats(hackers) { ? stats.graduationYear[hacker.application.general.graduationYear] + 1 : 1; - for (const dietaryRestrictions of hacker.application.accommodation - .dietaryRestrictions.dietaryRestrictions) { + .dietaryRestrictions) { stats.dietaryRestrictions[dietaryRestrictions] = stats .dietaryRestrictions[dietaryRestrictions] ? stats.dietaryRestrictions[dietaryRestrictions] + 1 diff --git a/tests/hacker.test.js b/tests/hacker.test.js index ce0e03fc..82655678 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -597,13 +597,11 @@ describe("PATCH update one hacker", function() { agent.close(); return done(error); } - let app = TeamHacker0.application; - app.other.gender = "Other"; return agent .patch(`/api/hacker/${TeamHacker0._id}`) .type("application/json") .send({ - application: app + accommodation: { shirtSize: "M" } }) .end(function(err, res) { res.should.have.status(200); @@ -613,9 +611,15 @@ describe("PATCH update one hacker", function() { Constants.Success.HACKER_UPDATE ); res.body.should.have.property("data"); + // console.log(res.body); + res.body.should.have.property("application"); + res.body.should.have.property("accommodation"); + res.body.should.have.property("shirtSize"); chai.assert.equal( - JSON.stringify(res.body.data.application.other.gender), - '"Other"' + JSON.stringify( + res.body.data.application.accommodation.shirtSize + ), + '"M"' ); done(); }); From a6e71001af414737e0c95fa904fac580b23ca027 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Sun, 8 Dec 2019 00:04:42 -0500 Subject: [PATCH 19/28] Fixed tests and changed account and hacker models appropriately. Also adjusted hacker validation --- controllers/hacker.controller.js | 1 - middlewares/hacker.middleware.js | 1 - middlewares/parse-body.middleware.js | 1 - middlewares/validators/hacker.validator.js | 163 +++++++++++- middlewares/validators/validator.helper.js | 292 ++++++++++++++------- models/hacker.model.js | 9 +- services/hacker.service.js | 1 - tests/account.test.js | 2 - tests/hacker.test.js | 65 +++-- tests/search.service.spec.js | 47 ++-- tests/util/hacker.test.util.js | 4 +- 11 files changed, 428 insertions(+), 158 deletions(-) diff --git a/controllers/hacker.controller.js b/controllers/hacker.controller.js index 214d4cfd..bbcdd3bd 100644 --- a/controllers/hacker.controller.js +++ b/controllers/hacker.controller.js @@ -70,7 +70,6 @@ function downloadedResume(req, res) { } function gotStats(req, res) { - console.log("gotStats?"); return res.status(200).json({ message: "Retrieved stats", data: { diff --git a/middlewares/hacker.middleware.js b/middlewares/hacker.middleware.js index ec77f2b8..a5b9b311 100644 --- a/middlewares/hacker.middleware.js +++ b/middlewares/hacker.middleware.js @@ -26,7 +26,6 @@ const Constants = { * @description Delete the req.body.id that was added by the validation of route parameter. */ function parsePatch(req, res, next) { - console.log(req.body); delete req.body.id; return next(); } diff --git a/middlewares/parse-body.middleware.js b/middlewares/parse-body.middleware.js index 29c86ec7..b24f1155 100644 --- a/middlewares/parse-body.middleware.js +++ b/middlewares/parse-body.middleware.js @@ -18,7 +18,6 @@ module.exports = { function middleware(req, res, next) { const errors = validationResult(req); if (!errors.isEmpty()) { - console.log(errors); return next({ status: 422, message: Constants.Error.VALIDATION_422_MESSAGE, diff --git a/middlewares/validators/hacker.validator.js b/middlewares/validators/hacker.validator.js index 64fd6f35..379ab479 100644 --- a/middlewares/validators/hacker.validator.js +++ b/middlewares/validators/hacker.validator.js @@ -6,6 +6,8 @@ module.exports = { newHackerValidator: [ // status will be added automatically VALIDATOR.mongoIdValidator("body", "accountId", false), + // validate that application is a valid object + VALIDATOR.applicationValidator("body", "application", false), VALIDATOR.stringValidator("body", "application.general.school", false), VALIDATOR.stringValidator("body", "application.general.degree", false), VALIDATOR.alphaArrayValidator( @@ -20,19 +22,84 @@ module.exports = { 2019, 2030 ), - VALIDATOR.alphaArrayValidator( "body", "application.accommodation.dietaryRestrictions", - true + false ), VALIDATOR.enumValidator( "body", "application.accommodation.shirtSize", Constants.SHIRT_SIZES, + false + ), + VALIDATOR.stringValidator( + "body", + "application.accommodation.impairments", true ), - VALIDATOR.applicationValidator("body", "application", false), + VALIDATOR.stringValidator( + "body", + "application.accommodation.barriers", + true + ), + VALIDATOR.stringValidator( + "body", + "application.general.URL.resume", + false + ), + VALIDATOR.stringValidator( + "body", + "application.general.URL.github", + true + ), + VALIDATOR.stringValidator( + "body", + "application.general.URL.dribbble", + true + ), + VALIDATOR.stringValidator( + "body", + "application.general.URL.linkedin", + true + ), + VALIDATOR.stringValidator( + "body", + "application.general.URL.other", + true + ), + VALIDATOR.stringValidator( + "body", + "application.general.URL.personal", + true + ), + VALIDATOR.enumValidator( + "body", + "application.general.jobInterest", + Constants.JOB_INTERESTS, + false + ), + VALIDATOR.alphaArrayValidator( + "body", + "application.shortAnswer.skills", + true + ), + VALIDATOR.stringValidator( + "body", + "application.shortAnswer.comments", + true + ), + VALIDATOR.stringValidator( + "body", + "application.shortAnswer.question1", + false + ), + VALIDATOR.stringValidator( + "body", + "application.shortAnswer.question2", + false + ), + VALIDATOR.alphaArrayValidator( "body", "application.other.ethnicity", @@ -55,6 +122,7 @@ module.exports = { "application.accommodation.needsBus", true ), + VALIDATOR.mongoIdValidator("body", "application.team", true), VALIDATOR.mongoIdValidator("body", "teamId", true) ], @@ -63,54 +131,123 @@ module.exports = { ], updateHackerValidator: [ - VALIDATOR.stringValidator("body", "application.general.school", true), - VALIDATOR.stringValidator("body", "application.general.degree", true), + // validate that application is a valid object + VALIDATOR.applicationValidator("body", "application", false), + VALIDATOR.stringValidator("body", "application.general.school", false), + VALIDATOR.stringValidator("body", "application.general.degree", false), VALIDATOR.alphaArrayValidator( "body", "application.general.fieldOfStudy", - true + false ), VALIDATOR.integerValidator( "body", "application.general.graduationYear", - true, + false, 2019, 2030 ), VALIDATOR.alphaArrayValidator( "body", "application.accommodation.dietaryRestrictions", - true + false ), VALIDATOR.enumValidator( "body", "application.accommodation.shirtSize", Constants.SHIRT_SIZES, + false + ), + VALIDATOR.stringValidator( + "body", + "application.accommodation.impairments", + true + ), + VALIDATOR.stringValidator( + "body", + "application.accommodation.barriers", + true + ), + VALIDATOR.stringValidator( + "body", + "application.general.URL.resume", + false + ), + VALIDATOR.stringValidator( + "body", + "application.general.URL.github", true ), - VALIDATOR.applicationValidator("body", "application", true), + VALIDATOR.stringValidator( + "body", + "application.general.URL.dribbble", + true + ), + VALIDATOR.stringValidator( + "body", + "application.general.URL.linkedin", + true + ), + VALIDATOR.stringValidator( + "body", + "application.general.URL.other", + true + ), + VALIDATOR.stringValidator( + "body", + "application.general.URL.personal", + true + ), + VALIDATOR.enumValidator( + "body", + "application.general.jobInterest", + Constants.JOB_INTERESTS, + false + ), VALIDATOR.alphaArrayValidator( "body", - "application.other.ethnicity", + "application.shortAnswer.skills", true ), + VALIDATOR.stringValidator( + "body", + "application.shortAnswer.comments", + true + ), + VALIDATOR.stringValidator( + "body", + "application.shortAnswer.question1", + false + ), + VALIDATOR.stringValidator( + "body", + "application.shortAnswer.question2", + false + ), + + VALIDATOR.alphaArrayValidator( + "body", + "application.other.ethnicity", + false + ), VALIDATOR.booleanValidator( "body", "application.other.privacyPolicy", - true, + false, true ), VALIDATOR.booleanValidator( "body", "application.other.codeOfConduct", - true, + false, true ), VALIDATOR.booleanValidator( "body", "application.accommodation.needsBus", true - ) + ), + VALIDATOR.mongoIdValidator("body", "application.team", true) ], updateStatusValidator: [ VALIDATOR.enumValidator( diff --git a/middlewares/validators/validator.helper.js b/middlewares/validators/validator.helper.js index 9c6712bc..0e77e1e9 100644 --- a/middlewares/validators/validator.helper.js +++ b/middlewares/validators/validator.helper.js @@ -380,19 +380,26 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { fieldname, "Invalid application" ); - //helper object to iterate through the items in the application and track which items are not valid. + let valid = true; + //helper object to iterate through the items in the application and track which items are not present. const hasValid = { - github: false, - dribbble: false, - personal: false, - linkedIn: false, - other: false, + general: false, + school: false, + degree: false, + graduationYear: false, + fieldOfStudy: false, jobInterest: false, - skills: false, - comments: false, + URL: false, + shortAnswer: false, question1: false, question2: false, - team: false + accommodation: false, + dietaryRestrictions: false, + shirtSize: false, + other: false, + ethnicity: false, + codeOfConduct: false, + privacyPolicy: false }; if (optional) { @@ -401,51 +408,101 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { checkFalsy: true }) .custom((app) => { - const jobInterests = Constants.JOB_INTERESTS; - hasValid.github = - !app.general.URL.github || - typeof app.general.URL.github === "string"; - hasValid.dribbble = - !app.general.URL.dribbble || - typeof app.general.URL.dribbble === "string"; - hasValid.personal = - !app.general.URL.personal || - typeof app.general.URL.personal === "string"; - hasValid.linkedIn = - !app.general.URL.linkedIn || - typeof app.general.URL.linkedIn === "string"; - hasValid.other = - !app.general.URL.other || - typeof app.general.URL.other === "string"; - hasValid.jobInterest = - !!app.general.jobInterest && - jobInterests.includes(app.general.jobInterest); - hasValid.skills = - !app.shortAnswer.skills || - alphaArrayValidationHelper(app.shortAnswer.skills); - hasValid.comments = - !app.shortAnswer.comments || - typeof app.shortAnswer.comments === "string"; - hasValid.question1 = - !!app.shortAnswer.question1 && - typeof app.shortAnswer.question1 === "string"; - hasValid.question2 = - !!app.shortAnswer.question2 && - typeof app.shortAnswer.question2 === "string"; - hasValid.team = - !app.team || mongoose.Types.ObjectId.isValid(app.team); + hasValid.general = app.hasOwnProperty("general"); + if (hasValid.general) { + hasValid.school = app.general.hasOwnProperty("school"); + hasValid.degree = app.general.hasOwnProperty("degree"); + hasValid.fieldOfStudy = app.general.hasOwnProperty( + "fieldOfStudy" + ); + hasValid.graduationYear = app.general.hasOwnProperty( + "graduationYear" + ); + hasValid.jobInterest = app.general.hasOwnProperty( + "jobInterest" + ); + hasValid.URL = app.general.hasOwnProperty("URL"); + } + + hasValid.shortAnswer = app.hasOwnProperty("shortAnswer"); + if (hasValid.shortAnswer) { + hasValid.question1 = app.shortAnswer.hasOwnProperty( + "question1" + ); + hasValid.question2 = app.shortAnswer.hasOwnProperty( + "question2" + ); + } + hasValid.accommodation = app.hasOwnProperty("accommodation"); + if (hasValid.accommodation) { + hasValid.dietaryRestrictions = app.accommodation.hasOwnProperty( + "dietaryRestrictions" + ); + hasValid.shirtSize = app.accommodation.hasOwnProperty( + "shirtSize" + ); + } + hasValid.other = app.hasOwnProperty("other"); + if (hasValid.other) { + hasValid.ethnicity = app.other.hasOwnProperty("ethnicity"); + hasValid.codeOfConduct = app.other.hasOwnProperty( + "codeOfConduct" + ); + hasValid.privacyPolicy = app.other.hasOwnProperty( + "privacyPolicy" + ); + } + // hasValid.github = + // !app.general.URL.github || + // typeof app.general.URL.github === "string"; + // hasValid.dribbble = + // !app.general.URL.dribbble || + // typeof app.general.URL.dribbble === "string"; + // hasValid.personal = + // !app.general.URL.personal || + // typeof app.general.URL.personal === "string"; + // hasValid.linkedIn = + // !app.general.URL.linkedIn || + // typeof app.general.URL.linkedIn === "string"; + // hasValid.other = + // !app.general.URL.other || + // typeof app.general.URL.other === "string"; + // hasValid.jobInterest = + // !!app.general.jobInterest && + // jobInterests.includes(app.general.jobInterest); + // hasValid.skills = + // !app.shortAnswer.skills || + // alphaArrayValidationHelper(app.shortAnswer.skills); + // hasValid.comments = + // !app.shortAnswer.comments || + // typeof app.shortAnswer.comments === "string"; + // hasValid.question1 = + // !!app.shortAnswer.question1 && + // typeof app.shortAnswer.question1 === "string"; + // hasValid.question2 = + // !!app.shortAnswer.question2 && + // typeof app.shortAnswer.question2 === "string"; + // hasValid.team = + // !app.team || mongoose.Types.ObjectId.isValid(app.team); + return ( - hasValid.github && - hasValid.dribbble && - hasValid.personal && - hasValid.linkedIn && - hasValid.other && + hasValid.general && + hasValid.school && + hasValid.degree && + hasValid.graduationYear && + hasValid.fieldOfStudy && hasValid.jobInterest && - hasValid.skills && - hasValid.comments && + hasValid.URL && + hasValid.shortAnswer && hasValid.question1 && hasValid.question2 && - hasValid.team + hasValid.accommodation && + hasValid.dietaryRestrictions && + hasValid.shirtSize && + hasValid.other && + hasValid.ethnicity && + hasValid.privacyPolicy && + hasValid.codeOfConduct ); }) .withMessage({ @@ -455,55 +512,106 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { } else { return application .custom((app) => { - const jobInterests = Constants.JOB_INTERESTS; - hasValid.github = - !app.general.URL.github || - typeof app.general.URL.github === "string"; - hasValid.dribbble = - !app.general.URL.dribbble || - typeof app.general.URL.dribbble === "string"; - hasValid.personal = - !app.general.URL.personal || - typeof app.general.URL.personal === "string"; - hasValid.linkedIn = - !app.general.URL.linkedIn || - typeof app.general.URL.linkedIn === "string"; - hasValid.other = - !app.general.URL.other || - typeof app.general.URL.other === "string"; - hasValid.jobInterest = - !!app.general.jobInterest && - jobInterests.includes(app.general.jobInterest); - hasValid.skills = - !app.shortAnswer.skills || - alphaArrayValidationHelper(app.shortAnswer.skills); - hasValid.comments = - !app.shortAnswer.comments || - typeof app.shortAnswer.comments === "string"; - hasValid.question1 = - !!app.shortAnswer.question1 && - typeof app.shortAnswer.question1 === "string"; - hasValid.question2 = - !!app.shortAnswer.question2 && - typeof app.shortAnswer.question2 === "string"; - hasValid.team = - !app.team || mongoose.Types.ObjectId.isValid(app.team); + hasValid.general = app.hasOwnProperty("general"); + if (hasValid.general) { + hasValid.school = app.general.hasOwnProperty("school"); + hasValid.degree = app.general.hasOwnProperty("degree"); + hasValid.fieldOfStudy = app.general.hasOwnProperty( + "fieldOfStudy" + ); + hasValid.graduationYear = app.general.hasOwnProperty( + "graduationYear" + ); + hasValid.jobInterest = app.general.hasOwnProperty( + "jobInterest" + ); + hasValid.URL = app.general.hasOwnProperty("URL"); + } + + hasValid.shortAnswer = app.hasOwnProperty("shortAnswer"); + if (hasValid.shortAnswer) { + hasValid.question1 = app.shortAnswer.hasOwnProperty( + "question1" + ); + hasValid.question2 = app.shortAnswer.hasOwnProperty( + "question2" + ); + } + hasValid.accommodation = app.hasOwnProperty("accommodation"); + if (hasValid.accommodation) { + hasValid.dietaryRestrictions = app.accommodation.hasOwnProperty( + "dietaryRestrictions" + ); + hasValid.shirtSize = app.accommodation.hasOwnProperty( + "shirtSize" + ); + } + hasValid.other = app.hasOwnProperty("other"); + if (hasValid.other) { + hasValid.ethnicity = app.other.hasOwnProperty("ethnicity"); + hasValid.codeOfConduct = app.other.hasOwnProperty( + "codeOfConduct" + ); + hasValid.privacyPolicy = app.other.hasOwnProperty( + "privacyPolicy" + ); + } + // hasValid.github = + // !app.general.URL.github || + // typeof app.general.URL.github === "string"; + // hasValid.dribbble = + // !app.general.URL.dribbble || + // typeof app.general.URL.dribbble === "string"; + // hasValid.personal = + // !app.general.URL.personal || + // typeof app.general.URL.personal === "string"; + // hasValid.linkedIn = + // !app.general.URL.linkedIn || + // typeof app.general.URL.linkedIn === "string"; + // hasValid.other = + // !app.general.URL.other || + // typeof app.general.URL.other === "string"; + // hasValid.jobInterest = + // !!app.general.jobInterest && + // jobInterests.includes(app.general.jobInterest); + // hasValid.skills = + // !app.shortAnswer.skills || + // alphaArrayValidationHelper(app.shortAnswer.skills); + // hasValid.comments = + // !app.shortAnswer.comments || + // typeof app.shortAnswer.comments === "string"; + // hasValid.question1 = + // !!app.shortAnswer.question1 && + // typeof app.shortAnswer.question1 === "string"; + // hasValid.question2 = + // !!app.shortAnswer.question2 && + // typeof app.shortAnswer.question2 === "string"; + // hasValid.team = + // !app.team || mongoose.Types.ObjectId.isValid(app.team); + return ( - hasValid.github && - hasValid.dribbble && - hasValid.personal && - hasValid.linkedIn && - hasValid.other && + hasValid.general && + hasValid.school && + hasValid.degree && + hasValid.graduationYear && + hasValid.fieldOfStudy && hasValid.jobInterest && - hasValid.comments && - hasValid.skills && + hasValid.URL && + hasValid.shortAnswer && hasValid.question1 && hasValid.question2 && - hasValid.team + hasValid.accommodation && + hasValid.dietaryRestrictions && + hasValid.shirtSize && + hasValid.other && + hasValid.ethnicity && + hasValid.privacyPolicy && + hasValid.codeOfConduct ); }) .withMessage({ - message: "Not all items of the application are valid", + message: + "Application does not have all of the required fields.", isValid: hasValid }); } diff --git a/models/hacker.model.js b/models/hacker.model.js index 5b2b17f9..649d02a6 100644 --- a/models/hacker.model.js +++ b/models/hacker.model.js @@ -116,7 +116,14 @@ const HackerSchema = new mongoose.Schema({ ], required: true }, - + impairments: { + type: String, + default: "" + }, + barriers: { + type: String, + default: "" + }, shirtSize: { type: String, enum: Constants.SHIRT_SIZES, diff --git a/services/hacker.service.js b/services/hacker.service.js index 88848266..2a3bca60 100644 --- a/services/hacker.service.js +++ b/services/hacker.service.js @@ -35,7 +35,6 @@ function updateOne(id, hackerDetails) { const query = { _id: id }; - console.log(query, hackerDetails); return Hacker.findOneAndUpdate( query, diff --git a/tests/account.test.js b/tests/account.test.js index 11d900a9..1d17efb0 100644 --- a/tests/account.test.js +++ b/tests/account.test.js @@ -207,8 +207,6 @@ describe("POST create account", function() { .type("application/json") .send(newAccount0) .end(function(err, res) { - console.log(res.body); - console.log(newAccount0); res.should.have.status(200); res.should.be.json; res.body.should.have.property("message"); diff --git a/tests/hacker.test.js b/tests/hacker.test.js index 82655678..a650d00b 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -8,6 +8,7 @@ const should = chai.should(); const Hacker = require("../models/hacker.model"); const fs = require("fs"); const path = require("path"); +const cloneDeep = require("lodash/clonedeep"); const Constants = { Success: require("../constants/success.constant"), General: require("../constants/general.constant"), @@ -545,7 +546,7 @@ describe("POST create hacker", function() { res.body.should.have.property("data"); res.body.data.should.have.property("accountId"); res.body.data.accountId.should.have.property("msg"); - res.body.data.accountId.msg.should.equal("invalid mongoID"); + res.body.data.accountId.msg.should.equal("Invalid mongoID"); res.body.data.should.have.property( "application.general.school" ); @@ -555,17 +556,20 @@ describe("POST create hacker", function() { res.body.data[ "application.general.school" ].msg.should.equal("name must exist"); - res.body.data.should.have.property("application"); - res.body.data.application.should.have.property("msg"); - res.body.data.application.msg.should.have.property( - "isValid" - ); - res.body.data.application.msg.isValid.should.have.property( - "jobInterest" - ); - res.body.data.application.msg.isValid.jobInterest.should.equal( - false + res.body.data.should.have.property( + "application.general.jobInterest" ); + res.body.data[ + "application.general.jobInterest" + ].should.have.property("msg"); + + res.body.data[ + "application.general.jobInterest" + ].should.have.property("msg"); + res.body.data[ + "application.general.jobInterest" + ].msg.should.equal("The value must be part of the enum"); + done(); }); }); @@ -597,11 +601,14 @@ describe("PATCH update one hacker", function() { agent.close(); return done(error); } + // this endpoint requires an application object to work properly, so will clone and mutate the object + let app = cloneDeep(TeamHacker0.application); + app.accommodation.shirtSize = "M"; return agent .patch(`/api/hacker/${TeamHacker0._id}`) .type("application/json") .send({ - accommodation: { shirtSize: "M" } + application: app }) .end(function(err, res) { res.should.have.status(200); @@ -611,10 +618,13 @@ describe("PATCH update one hacker", function() { Constants.Success.HACKER_UPDATE ); res.body.should.have.property("data"); - // console.log(res.body); - res.body.should.have.property("application"); - res.body.should.have.property("accommodation"); - res.body.should.have.property("shirtSize"); + res.body.data.should.have.property("application"); + res.body.data.application.should.have.property( + "accommodation" + ); + res.body.data.application.accommodation.should.have.property( + "shirtSize" + ); chai.assert.equal( JSON.stringify( res.body.data.application.accommodation.shirtSize @@ -799,8 +809,9 @@ describe("PATCH update one hacker", function() { agent.close(); return done(error); } - let app = noTeamHacker0.application; - app.other.gender = "Other"; + // this endpoint requires an application object to work properly, so will clone and mutate the object + let app = cloneDeep(noTeamHacker0.application); + app.accommodation.shirtSize = "M"; return agent .patch(`/api/hacker/${noTeamHacker0._id}`) .type("application/json") @@ -815,9 +826,18 @@ describe("PATCH update one hacker", function() { Constants.Success.HACKER_UPDATE ); res.body.should.have.property("data"); + res.body.data.should.have.property("application"); + res.body.data.application.should.have.property( + "accommodation" + ); + res.body.data.application.accommodation.should.have.property( + "shirtSize" + ); chai.assert.equal( - JSON.stringify(res.body.data.application.other.gender), - '"Other"' + JSON.stringify( + res.body.data.application.accommodation.shirtSize + ), + '"M"' ); done(); }); @@ -831,11 +851,14 @@ describe("PATCH update one hacker", function() { agent.close(); return done(error); } + // this endpoint requires an application object to work properly, so will clone and mutate the object + let app = cloneDeep(noTeamHacker0.application); + app.accommodation.shirtSize = "M"; return agent .patch(`/api/hacker/${unconfirmedHacker1._id}`) .type("application/json") .send({ - gender: "Other" + application: app }) .end(function(err, res) { res.should.have.status(403); diff --git a/tests/search.service.spec.js b/tests/search.service.spec.js index 9e04f89b..0495c1a2 100644 --- a/tests/search.service.spec.js +++ b/tests/search.service.spec.js @@ -22,9 +22,9 @@ const util = { const queryToExecute = [ { - param: "gender", + param: "application.general.degree", operation: "equals", - value: "Female" + value: "Undergraduate" } ]; @@ -100,27 +100,28 @@ describe("Searching for hackers", function() { done(); }); }); - it("Should return all female hackers", function(done) { - util.auth.login(agent, Admin0, (error) => { - if (error) { - agent.close(); - return done(error); - } - return agent - .get("/api/search") - .query({ - model: "account", - q: JSON.stringify(queryToExecute) - }) - .end(function(err, res) { - res.should.have.status(200); - res.body.should.have.property("data"); - res.body.data.should.have.length(7); - done(); - }); - }); + }); + it("Should return all undergraduate hackers", function(done) { + util.auth.login(agent, Admin0, (error) => { + if (error) { + agent.close(); + return done(error); + } + return agent + .get("/api/search") + .query({ + model: "hacker", + q: JSON.stringify(queryToExecute) + }) + .end(function(err, res) { + res.should.have.status(200); + res.body.should.have.property("data"); + res.body.data.should.have.length(2); + done(); + }); }); }); + it("Should return an error as hackers don't have password stored", function(done) { util.auth.login(agent, Admin0, (error) => { if (error) { @@ -296,14 +297,14 @@ describe("Searching for hackers", function() { return agent .get("/api/search") .query({ - model: "account", + model: "hacker", q: JSON.stringify(queryToExecute), expand: true }) .end(function(err, res) { res.should.have.status(200); res.body.should.have.property("data"); - res.body.data.should.have.length(7); + res.body.data.should.have.length(2); res.body.data[0].should.have.property("accountId"); res.body.data[0].accountId.should.have.property("email"); done(); diff --git a/tests/util/hacker.test.util.js b/tests/util/hacker.test.util.js index d8f1ef70..fa343bfe 100644 --- a/tests/util/hacker.test.util.js +++ b/tests/util/hacker.test.util.js @@ -369,7 +369,7 @@ const invalidHacker1 = { application: { general: { // invalid missing school attribute - degree: "Undersaduate", + degree: "Undergraduate", fieldOfStudy: ["EE"], graduationYear: 2020, // invalid job interest @@ -521,7 +521,7 @@ const unconfirmedAccountHacker1 = { application: { general: { school: "University of Blah2", - degree: "Underggraduate", + degree: "Undergraduate", fieldOfStudy: ["EE"], graduationYear: 2019, jobInterest: "Internship", From 67271ccedd7da457e24ff36484e1b28ed60ea46a Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Sun, 8 Dec 2019 00:22:08 -0500 Subject: [PATCH 20/28] Added lodash module --- package-lock.json | 6 +++--- package.json | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4a882b83..d963b3b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5412,9 +5412,9 @@ } }, "lodash": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", - "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==" + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "lodash.at": { "version": "4.6.0", diff --git a/package.json b/package.json index 27f8d4b6..7c6b427d 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "express-winston": "^2.6.0", "handlebars": "^4.5.3", "jsonwebtoken": "^8.5.1", + "lodash": "^4.17.15", "memory-cache": "^0.2.0", "mongoose": "^5.7.5", "multer": "^1.4.2", From d1234ce424c41d1320277ca01a1aaafcf14b7da9 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Sun, 8 Dec 2019 00:25:34 -0500 Subject: [PATCH 21/28] Added lodash module --- package-lock.json | 5 +++++ package.json | 1 + 2 files changed, 6 insertions(+) diff --git a/package-lock.json b/package-lock.json index d963b3b0..96ef79df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5431,6 +5431,11 @@ "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, "lodash.has": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz", diff --git a/package.json b/package.json index 7c6b427d..f0d18ed8 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "handlebars": "^4.5.3", "jsonwebtoken": "^8.5.1", "lodash": "^4.17.15", + "lodash.clonedeep": "^4.5.0", "memory-cache": "^0.2.0", "mongoose": "^5.7.5", "multer": "^1.4.2", From 768381c6ae15314a730d5bea8b7133a466564d80 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Sun, 8 Dec 2019 00:39:57 -0500 Subject: [PATCH 22/28] Added nvm --- package-lock.json | 5 +++++ package.json | 1 + 2 files changed, 6 insertions(+) diff --git a/package-lock.json b/package-lock.json index 96ef79df..85450497 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6250,6 +6250,11 @@ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, + "nvm": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/nvm/-/nvm-0.0.4.tgz", + "integrity": "sha1-OKF46dMbKDUIyS0VydqGHRqSELw=" + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", diff --git a/package.json b/package.json index f0d18ed8..d9bc4d4b 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "memory-cache": "^0.2.0", "mongoose": "^5.7.5", "multer": "^1.4.2", + "nvm": "0.0.4", "passport": "^0.4.0", "passport-local": "^1.0.0", "q": "^1.5.1", From 40b1404b695f0e9f971428f6bda01fd70532b7b7 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Sun, 8 Dec 2019 00:59:00 -0500 Subject: [PATCH 23/28] Removed dependence on lodash --- tests/hacker.test.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/hacker.test.js b/tests/hacker.test.js index a650d00b..cb7de317 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -8,7 +8,7 @@ const should = chai.should(); const Hacker = require("../models/hacker.model"); const fs = require("fs"); const path = require("path"); -const cloneDeep = require("lodash/clonedeep"); +// const cloneDeep = require("lodash/clonedeep"); const Constants = { Success: require("../constants/success.constant"), General: require("../constants/general.constant"), @@ -602,7 +602,8 @@ describe("PATCH update one hacker", function() { return done(error); } // this endpoint requires an application object to work properly, so will clone and mutate the object - let app = cloneDeep(TeamHacker0.application); + // let app = cloneDeep(TeamHacker0.application); + let app = TeamHacker0.application; app.accommodation.shirtSize = "M"; return agent .patch(`/api/hacker/${TeamHacker0._id}`) @@ -810,7 +811,8 @@ describe("PATCH update one hacker", function() { return done(error); } // this endpoint requires an application object to work properly, so will clone and mutate the object - let app = cloneDeep(noTeamHacker0.application); + // let app = cloneDeep(noTeamHacker0.application); + let app = noTeamHacker0.application; app.accommodation.shirtSize = "M"; return agent .patch(`/api/hacker/${noTeamHacker0._id}`) @@ -852,7 +854,8 @@ describe("PATCH update one hacker", function() { return done(error); } // this endpoint requires an application object to work properly, so will clone and mutate the object - let app = cloneDeep(noTeamHacker0.application); + // let app = cloneDeep(noTeamHacker0.application); + let app = noTeamHacker0.application; app.accommodation.shirtSize = "M"; return agent .patch(`/api/hacker/${unconfirmedHacker1._id}`) From 3d1814b8685a37558f41d7b0adcdb898e8fa9b75 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Sun, 8 Dec 2019 01:00:31 -0500 Subject: [PATCH 24/28] Removed dependence on lodash --- tests/hacker.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/hacker.test.js b/tests/hacker.test.js index cb7de317..7b70d462 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -854,8 +854,8 @@ describe("PATCH update one hacker", function() { return done(error); } // this endpoint requires an application object to work properly, so will clone and mutate the object - // let app = cloneDeep(noTeamHacker0.application); - let app = noTeamHacker0.application; + // let app = cloneDeep(unconfirmedHacker1.application); + let app = unconfirmedHacker1.application; app.accommodation.shirtSize = "M"; return agent .patch(`/api/hacker/${unconfirmedHacker1._id}`) From 1cd3d7b7bdb88d5090be83d6b0ec1b1e0e0a9b45 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Wed, 11 Dec 2019 01:37:45 -0500 Subject: [PATCH 25/28] Changed application to have travel take an integer --- middlewares/validators/hacker.validator.js | 19 +++++----- middlewares/validators/validator.helper.js | 9 ----- models/hacker.model.js | 2 +- routes/api/hacker.js | 43 +++++++++++----------- services/hacker.service.js | 9 +++-- tests/hacker.test.js | 39 ++++++++++++-------- tests/util/hacker.test.util.js | 28 +++++++------- 7 files changed, 75 insertions(+), 74 deletions(-) diff --git a/middlewares/validators/hacker.validator.js b/middlewares/validators/hacker.validator.js index 379ab479..3c6bf1d8 100644 --- a/middlewares/validators/hacker.validator.js +++ b/middlewares/validators/hacker.validator.js @@ -22,11 +22,6 @@ module.exports = { 2019, 2030 ), - VALIDATOR.alphaArrayValidator( - "body", - "application.accommodation.dietaryRestrictions", - false - ), VALIDATOR.enumValidator( "body", "application.accommodation.shirtSize", @@ -117,10 +112,12 @@ module.exports = { false, true ), - VALIDATOR.booleanValidator( + VALIDATOR.integerValidator( "body", - "application.accommodation.needsBus", - true + "application.accommodation.travel", + true, + 0, + 100 ), VALIDATOR.mongoIdValidator("body", "application.team", true), VALIDATOR.mongoIdValidator("body", "teamId", true) @@ -244,8 +241,10 @@ module.exports = { ), VALIDATOR.booleanValidator( "body", - "application.accommodation.needsBus", - true + "application.accommodation.travel", + true, + 0, + 100 ), VALIDATOR.mongoIdValidator("body", "application.team", true) ], diff --git a/middlewares/validators/validator.helper.js b/middlewares/validators/validator.helper.js index 79b5cc22..634e2e9c 100644 --- a/middlewares/validators/validator.helper.js +++ b/middlewares/validators/validator.helper.js @@ -388,7 +388,6 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { question1: false, question2: false, accommodation: false, - dietaryRestrictions: false, shirtSize: false, other: false, ethnicity: false, @@ -429,9 +428,6 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { } hasValid.accommodation = app.hasOwnProperty("accommodation"); if (hasValid.accommodation) { - hasValid.dietaryRestrictions = app.accommodation.hasOwnProperty( - "dietaryRestrictions" - ); hasValid.shirtSize = app.accommodation.hasOwnProperty( "shirtSize" ); @@ -491,7 +487,6 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { hasValid.question1 && hasValid.question2 && hasValid.accommodation && - hasValid.dietaryRestrictions && hasValid.shirtSize && hasValid.other && hasValid.ethnicity && @@ -533,9 +528,6 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { } hasValid.accommodation = app.hasOwnProperty("accommodation"); if (hasValid.accommodation) { - hasValid.dietaryRestrictions = app.accommodation.hasOwnProperty( - "dietaryRestrictions" - ); hasValid.shirtSize = app.accommodation.hasOwnProperty( "shirtSize" ); @@ -595,7 +587,6 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { hasValid.question1 && hasValid.question2 && hasValid.accommodation && - hasValid.dietaryRestrictions && hasValid.shirtSize && hasValid.other && hasValid.ethnicity && diff --git a/models/hacker.model.js b/models/hacker.model.js index 649d02a6..357f491d 100644 --- a/models/hacker.model.js +++ b/models/hacker.model.js @@ -129,7 +129,7 @@ const HackerSchema = new mongoose.Schema({ enum: Constants.SHIRT_SIZES, required: true }, - needsBus: { type: Boolean, default: false } + travel: { type: Number, default: 0 } }, team: { type: mongoose.Schema.Types.ObjectId, diff --git a/routes/api/hacker.js b/routes/api/hacker.js index 29da42f2..c84bbe2a 100644 --- a/routes/api/hacker.js +++ b/routes/api/hacker.js @@ -58,7 +58,7 @@ module.exports = { "accountId":"5bff2a35e533b0f6562b4998", "school":"McPherson College", "gender":"Female", - "needsBus":false, + "travel":0, "major":["Accounting"], "graduationYear":2019, "codeOfConduct":true, @@ -87,7 +87,7 @@ module.exports = { * @apiParam (body) {MongoID} accountId ObjectID of the respective account * @apiParam (body) {String} school Name of the school the hacker goes to * @apiParam (body) {String} gender Gender of the hacker - * @apiParam (body) {Boolean} needsBus Whether the hacker requires a bus for transportation + * @apiParam (body) {Number} travel Whether the hacker requires a bus for transportation * @apiParam (body) {String[]} ethnicity the ethnicities of the hacker * @apiParam (body) {String[]} major the major of the hacker * @apiParam (body) {Number} graduationYear the graduation year of the hacker @@ -124,7 +124,7 @@ module.exports = { "codeOfConduct": true, } "accomodation": { - "needsBus": "false" + "travel": 0 }, } @@ -166,7 +166,7 @@ module.exports = { "codeOfConduct": true, } "accomodation": { - "needsBus": "false" + "travel": 0 }, } * } @@ -219,7 +219,7 @@ module.exports = { "school": { "McGill University": 3, "Harvard University": 7 }, degree: { "Undergraduate": 10 }, gender: { "Male": 1, "Female": 9 }, - needsBus: { "true": 7, "false": 3 }, + travel: { "true": 7, "false": 3 }, ethnicity: { "White": 10, }, jobInterest: { "Internship": 10 }, major: { "Computer Science": 10 }, @@ -295,17 +295,18 @@ module.exports = { * } * @apiPermission Administrator */ - hackerRouter.route("/accept/:id").patch( - Middleware.Validator.RouteParam.idValidator, - Middleware.Auth.ensureAuthenticated(), - Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), - Middleware.Hacker.validateConfirmedStatusFromHackerId, - Middleware.Hacker.parseAccept, - Middleware.Hacker.updateHacker, - Middleware.Hacker.sendStatusUpdateEmail, - Controllers.Hacker.updatedHacker - ); - + hackerRouter + .route("/accept/:id") + .patch( + Middleware.Validator.RouteParam.idValidator, + Middleware.Auth.ensureAuthenticated(), + Middleware.Auth.ensureAuthorized([Services.Hacker.findById]), + Middleware.Hacker.validateConfirmedStatusFromHackerId, + Middleware.Hacker.parseAccept, + Middleware.Hacker.updateHacker, + Middleware.Hacker.sendStatusUpdateEmail, + 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. @@ -353,7 +354,7 @@ module.exports = { * * @apiParam (body) {String} [school] Name of the school the hacker goes to * @apiParam (body) {String} [gender] Gender of the hacker - * @apiParam (body) {Boolean} [needsBus] Whether the hacker requires a bus for transportation + * @apiParam (body) {Number} [travel] How much the hacker requires a bus for transportation * @apiParam (body) {String[]} [ethnicity] the ethnicities of the hacker * @apiParam (body) {String[]} [major] the major of the hacker * @apiParam (body) {Number} [graduationYear] the graduation year of the hacker @@ -389,7 +390,7 @@ module.exports = { "codeOfConduct": true, } "accomodation": { - "needsBus": "false" + "travel": 0 }, } } @@ -431,7 +432,7 @@ module.exports = { "codeOfConduct": true, } "accomodation": { - "needsBus": "false" + "travel": 0 }, } } @@ -502,7 +503,7 @@ module.exports = { "codeOfConduct": true, } "accomodation": { - "needsBus": "false" + "travel": 0 }, } } @@ -569,7 +570,7 @@ module.exports = { "codeOfConduct": true, } "accomodation": { - "needsBus": "false" + "travel": 0 }, } } diff --git a/services/hacker.service.js b/services/hacker.service.js index 2a3bca60..366dd721 100644 --- a/services/hacker.service.js +++ b/services/hacker.service.js @@ -137,7 +137,7 @@ function getStats(hackers) { school: {}, degree: {}, gender: {}, - needsBus: {}, + travel: {}, ethnicity: {}, jobInterest: {}, fieldOfStudy: {}, @@ -171,9 +171,10 @@ function getStats(hackers) { ] ? stats.gender[hacker.accountId.gender] + 1 : 1; - stats.needsBus[hacker.application.accommodation.needsBus] = stats - .needsBus[hacker.application.accommodation.needsBus] - ? stats.needsBus[hacker.application.accommodation.needsBus] + 1 + stats.travel[hacker.application.accommodation.travel] = stats.travel[ + hacker.application.accommodation.travel + ] + ? stats.travel[hacker.application.accommodation.travel] + 1 : 1; for (const ethnicity of hacker.application.other.ethnicity) { diff --git a/tests/hacker.test.js b/tests/hacker.test.js index c972fbd6..74c45b3a 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -594,12 +594,12 @@ describe("PATCH update one hacker", function() { }); }); - it("should FAIL to accept a hacker on /api/hacker/accept/:id due to authentication", function (done) { + 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}`) .type("application/json") .send() - .end(function (err, res) { + .end(function(err, res) { res.should.have.status(401); res.should.be.json; res.body.should.have.property("message"); @@ -609,7 +609,7 @@ describe("PATCH update one hacker", function() { }); // should FAIL due to authorization - it("should FAIL to accept hacker info due to lack of authorization on /api/hacker/accept/:id", function (done) { + it("should FAIL to accept hacker info due to lack of authorization on /api/hacker/accept/:id", function(done) { util.auth.login(agent, noTeamHackerAccount0, (error) => { if (error) { agent.close(); @@ -619,11 +619,13 @@ describe("PATCH update one hacker", function() { .patch(`/api/hacker/accept/${TeamHacker0._id}`) .type("application/json") .send() - .end(function (err, res) { + .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.message.should.equal( + Constants.Error.AUTH_403_MESSAGE + ); res.body.should.have.property("data"); done(); @@ -631,7 +633,7 @@ describe("PATCH update one hacker", function() { }); }); - it("should FAIL to accept an invalid hacker's info on /api/hacker/accept/:id", function (done) { + it("should FAIL to accept an invalid hacker's info on /api/hacker/accept/:id", function(done) { util.auth.login(agent, Admin0, (error) => { if (error) { agent.close(); @@ -641,11 +643,13 @@ describe("PATCH update one hacker", function() { .patch(`/api/hacker/accept/${invalidHacker1._id}`) .type("application/json") .send() - .end(function (err, res) { + .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.HACKER_404_MESSAGE); + res.body.message.should.equal( + Constants.Error.HACKER_404_MESSAGE + ); res.body.should.have.property("data"); done(); @@ -653,7 +657,7 @@ describe("PATCH update one hacker", function() { }); }); - it("should SUCCEED and accept a hacker on /api/hacker/accept/:id as an Admin", function (done) { + it("should SUCCEED and accept a hacker on /api/hacker/accept/:id as an Admin", function(done) { util.auth.login(agent, Admin0, (error) => { if (error) { agent.close(); @@ -663,15 +667,20 @@ describe("PATCH update one hacker", function() { .patch(`/api/hacker/accept/${TeamHacker0._id}`) .type("application/json") .send() - .end(function (err, res) { + .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.message.should.equal( + Constants.Success.HACKER_UPDATE + ); res.body.should.have.property("data"); - chai.assert.equal(JSON.stringify(res.body.data), JSON.stringify({ - status: "Accepted" - })); + chai.assert.equal( + JSON.stringify(res.body.data), + JSON.stringify({ + status: "Accepted" + }) + ); done(); }); }); @@ -1262,7 +1271,7 @@ describe("GET Hacker stats", function() { res.body.data.stats.should.have.property("school"); res.body.data.stats.should.have.property("degree"); res.body.data.stats.should.have.property("gender"); - res.body.data.stats.should.have.property("needsBus"); + res.body.data.stats.should.have.property("travel"); res.body.data.stats.should.have.property("ethnicity"); res.body.data.stats.should.have.property("jobInterest"); res.body.data.stats.should.have.property("fieldOfStudy"); diff --git a/tests/util/hacker.test.util.js b/tests/util/hacker.test.util.js index fa343bfe..3c786b63 100644 --- a/tests/util/hacker.test.util.js +++ b/tests/util/hacker.test.util.js @@ -44,7 +44,7 @@ const TeamHacker0 = { accommodation: { dietaryRestrictions: ["Gluten-Free"], shirtSize: "L", - needsBus: true + travel: 0 } }, teamId: Constants.MongoId.team1Id @@ -84,7 +84,7 @@ const TeamHacker1 = { accommodation: { dietaryRestrictions: ["Gluten-Free"], shirtSize: "L", - needsBus: false + travel: 0 } }, teamId: Constants.MongoId.team3Id @@ -124,7 +124,7 @@ const TeamHacker2 = { accommodation: { dietaryRestrictions: ["Gluten-Free"], shirtSize: "L", - needsBus: false + travel: 0 } }, teamId: Constants.MongoId.team3Id @@ -164,7 +164,7 @@ const TeamHacker3 = { accommodation: { dietaryRestrictions: ["Gluten-Free"], shirtSize: "L", - needsBus: false + travel: 0 } }, teamId: Constants.MongoId.team3Id @@ -204,7 +204,7 @@ const TeamHacker4 = { accommodation: { dietaryRestrictions: ["Gluten-Free"], shirtSize: "L", - needsBus: false + travel: 0 } }, teamId: Constants.MongoId.team3Id @@ -244,7 +244,7 @@ const NoTeamHacker0 = { accommodation: { dietaryRestrictions: ["Gluten-Free"], shirtSize: "L", - needsBus: false + travel: 0 } } }; @@ -281,7 +281,7 @@ const newHacker0 = { accommodation: { dietaryRestrictions: ["Gluten-Free"], shirtSize: "L", - needsBus: false + travel: 0 } } }; @@ -318,7 +318,7 @@ const newHacker1 = { accommodation: { dietaryRestrictions: ["Gluten-Free"], shirtSize: "L", - needsBus: true + travel: 0 } } }; @@ -357,7 +357,7 @@ const invalidHacker0 = { accommodation: { dietaryRestrictions: ["Gluten-Free"], shirtSize: "L", - needsBus: true + travel: 0 } } }; @@ -391,7 +391,7 @@ const invalidHacker1 = { accommodation: { dietaryRestrictions: ["Gluten-Free"], shirtSize: "L", - needsBus: true + travel: 0 } } }; @@ -430,7 +430,7 @@ const duplicateAccountLinkHacker0 = { accommodation: { dietaryRestrictions: ["Gluten-Free"], shirtSize: "L", - needsBus: true + travel: 0 } } }; @@ -469,7 +469,7 @@ const waitlistedHacker0 = { accommodation: { dietaryRestrictions: ["Gluten-Free"], shirtSize: "L", - needsBus: false + travel: 0 } }, teamId: Constants.MongoId.team2Id @@ -509,7 +509,7 @@ const unconfirmedAccountHacker0 = { accommodation: { dietaryRestrictions: ["Gluten-Free"], shirtSize: "L", - needsBus: false + travel: 0 } } }; @@ -548,7 +548,7 @@ const unconfirmedAccountHacker1 = { accommodation: { dietaryRestrictions: ["Gluten-Free"], shirtSize: "L", - needsBus: false + travel: 0 } } }; From b42fe56b3d52f3bc3e6ff04e51a53845b116a50e Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Wed, 11 Dec 2019 04:15:55 -0500 Subject: [PATCH 26/28] Fixed hacker validator based on hacker model --- middlewares/validators/hacker.validator.js | 5 -- middlewares/validators/validator.helper.js | 1 - models/hacker.model.js | 8 --- tests/hacker.test.js | 60 +++++++++++++++++----- 4 files changed, 46 insertions(+), 28 deletions(-) diff --git a/middlewares/validators/hacker.validator.js b/middlewares/validators/hacker.validator.js index 3c6bf1d8..42504e4c 100644 --- a/middlewares/validators/hacker.validator.js +++ b/middlewares/validators/hacker.validator.js @@ -144,11 +144,6 @@ module.exports = { 2019, 2030 ), - VALIDATOR.alphaArrayValidator( - "body", - "application.accommodation.dietaryRestrictions", - false - ), VALIDATOR.enumValidator( "body", "application.accommodation.shirtSize", diff --git a/middlewares/validators/validator.helper.js b/middlewares/validators/validator.helper.js index 634e2e9c..961e2c53 100644 --- a/middlewares/validators/validator.helper.js +++ b/middlewares/validators/validator.helper.js @@ -574,7 +574,6 @@ function applicationValidator(fieldLocation, fieldname, optional = true) { // typeof app.shortAnswer.question2 === "string"; // hasValid.team = // !app.team || mongoose.Types.ObjectId.isValid(app.team); - return ( hasValid.general && hasValid.school && diff --git a/models/hacker.model.js b/models/hacker.model.js index 357f491d..b370ebc0 100644 --- a/models/hacker.model.js +++ b/models/hacker.model.js @@ -108,14 +108,6 @@ const HackerSchema = new mongoose.Schema({ } }, accommodation: { - dietaryRestrictions: { - type: [ - { - type: String - } - ], - required: true - }, impairments: { type: String, default: "" diff --git a/tests/hacker.test.js b/tests/hacker.test.js index 74c45b3a..4ad47f27 100644 --- a/tests/hacker.test.js +++ b/tests/hacker.test.js @@ -8,7 +8,6 @@ const should = chai.should(); const Hacker = require("../models/hacker.model"); const fs = require("fs"); const path = require("path"); -// const cloneDeep = require("lodash/clonedeep"); const Constants = { Success: require("../constants/success.constant"), General: require("../constants/general.constant"), @@ -583,7 +582,7 @@ describe("PATCH update one hacker", function() { .patch(`/api/hacker/${TeamHacker0._id}`) .type("application/json") .send({ - gender: "Other" + application: TeamHacker0.application }) .end(function(err, res) { res.should.have.status(401); @@ -693,15 +692,23 @@ describe("PATCH update one hacker", function() { agent.close(); return done(error); } - // this endpoint requires an application object to work properly, so will clone and mutate the object - // let app = cloneDeep(TeamHacker0.application); let app = TeamHacker0.application; app.accommodation.shirtSize = "M"; return agent .patch(`/api/hacker/${TeamHacker0._id}`) .type("application/json") .send({ - application: app + application: { + general: app.general, + shortAnswer: app.shortAnswer, + other: app.other, + accommodation: { + barriers: app.accommodation.barriers, + impairments: app.accommodation.impairments, + travel: app.accommodation.travel, + shirtSize: "M" + } + } }) .end(function(err, res) { res.should.have.status(200); @@ -902,15 +909,22 @@ describe("PATCH update one hacker", function() { agent.close(); return done(error); } - // this endpoint requires an application object to work properly, so will clone and mutate the object - // let app = cloneDeep(noTeamHacker0.application); let app = noTeamHacker0.application; - app.accommodation.shirtSize = "M"; return agent .patch(`/api/hacker/${noTeamHacker0._id}`) .type("application/json") .send({ - application: app + application: { + general: app.general, + shortAnswer: app.shortAnswer, + other: app.other, + accommodation: { + barriers: app.accommodation.barriers, + impairments: app.accommodation.impairments, + travel: app.accommodation.travel, + shirtSize: "M" + } + } }) .end(function(err, res) { res.should.have.status(200); @@ -945,15 +959,22 @@ describe("PATCH update one hacker", function() { agent.close(); return done(error); } - // this endpoint requires an application object to work properly, so will clone and mutate the object - // let app = cloneDeep(unconfirmedHacker1.application); let app = unconfirmedHacker1.application; - app.accommodation.shirtSize = "M"; return agent .patch(`/api/hacker/${unconfirmedHacker1._id}`) .type("application/json") .send({ - application: app + application: { + general: app.general, + shortAnswer: app.shortAnswer, + other: app.other, + accommodation: { + barriers: app.accommodation.barriers, + impairments: app.accommodation.impairments, + travel: app.accommodation.travel, + shirtSize: "M" + } + } }) .end(function(err, res) { res.should.have.status(403); @@ -975,11 +996,22 @@ describe("PATCH update one hacker", function() { agent.close(); return done(error); } + let app = TeamHacker0.application; return agent .patch(`/api/hacker/${TeamHacker0._id}`) .type("application/json") .send({ - gender: "Other" + application: { + general: app.general, + shortAnswer: app.shortAnswer, + other: app.other, + accommodation: { + barriers: app.accommodation.barriers, + impairments: app.accommodation.impairments, + travel: app.accommodation.travel, + shirtSize: "M" + } + } }) .end(function(err, res) { res.should.have.status(403); From 42ddd3ee4f80c66d49b739300cf46c892af9e832 Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Wed, 11 Dec 2019 04:26:55 -0500 Subject: [PATCH 27/28] Fixed getStats --- services/hacker.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/hacker.service.js b/services/hacker.service.js index 366dd721..8e0ea501 100644 --- a/services/hacker.service.js +++ b/services/hacker.service.js @@ -196,7 +196,7 @@ function getStats(hackers) { ? stats.graduationYear[hacker.application.general.graduationYear] + 1 : 1; - for (const dietaryRestrictions of hacker.application.accommodation + for (const dietaryRestrictions of hacker.accountId .dietaryRestrictions) { stats.dietaryRestrictions[dietaryRestrictions] = stats .dietaryRestrictions[dietaryRestrictions] From 08734aad1800aef603018f923b8eac38100910ba Mon Sep 17 00:00:00 2001 From: RohitGarudadri Date: Wed, 11 Dec 2019 04:38:42 -0500 Subject: [PATCH 28/28] removed redundancies --- routes/api/account.js | 46 ------------------------------------------- 1 file changed, 46 deletions(-) diff --git a/routes/api/account.js b/routes/api/account.js index c51058d2..dfa7c802 100644 --- a/routes/api/account.js +++ b/routes/api/account.js @@ -42,14 +42,7 @@ module.exports = { "pronoun":"he/him", "email":"theo@klein.com", "phoneNumber":1234567890, -<<<<<<< HEAD "gender":"Male", -======= -<<<<<<< HEAD -======= - "gender":"Male", ->>>>>>> develop ->>>>>>> test "birthDate":Date("10/30/1997") } } @@ -76,15 +69,8 @@ module.exports = { * @apiParam (body) {String} lastName Last name of the account creator. * @apiParam (body) {String} pronoun the pronoun of the account creator. * @apiParam (body) {String} email Email of the account. -<<<<<<< HEAD * @apiParam (body) {String} gender Gender of the account creator. -======= -<<<<<<< HEAD -======= * @apiParam (body) {String[]} dietaryRestrictions Any dietary restrictions for the user. 'None' if there are no restrictions - * @apiParam (body) {String} gender Gender of the account creator. ->>>>>>> develop ->>>>>>> test * @apiParam (body) {String} password The password of the account. * @apiParam (body) {String} birthDate a Date parsable string. * @apiParam (body) {Number} phoneNumber the user's phone number, represented as a string. @@ -98,14 +84,7 @@ module.exports = { "email":"theo@klein.com", "password":"hunter2", "phoneNumber":1234567890, -<<<<<<< HEAD - "gender":"Male", -======= -<<<<<<< HEAD -======= "gender":"Male", ->>>>>>> develop ->>>>>>> test "birthDate":"10/30/1997" * } * @@ -121,14 +100,7 @@ module.exports = { "pronoun":"he/him", "email":"theo@klein.com", "phoneNumber":1234567890, -<<<<<<< HEAD "gender":"Male", -======= -<<<<<<< HEAD -======= - "gender":"Male", ->>>>>>> develop ->>>>>>> test "birthDate":Date("10/30/1997") } } @@ -239,22 +211,11 @@ module.exports = { * @apiParam (body) {String} [lastName] Last name of the account creator. * @apiParam (body) {String} [pronoun] The pronoun of the account creator. * @apiParam (body) {String} [email] Email of the account. -<<<<<<< HEAD * @apiParam (body) {String} [gender] Gender of the account creator. * @apiParam (body) {String} [birthDate] A Date parsable string. * @apiParam (body) {Number} [phoneNumber] The user's phone number, represented as a string. -======= -<<<<<<< HEAD * @apiParam (body) {String} [birthDate] a Date parsable string. - * @apiParam (body) {Number} [phoneNumber] the user's phone number, represented as a string. -======= * @apiParam (body) {String[]} [dietaryRestrictions] Any dietary restrictions for the user. 'None' if there are no restrictions - * @apiParam (body) {String} [gender] Gender of the account creator. - * @apiParam (body) {String} [birthDate] A Date parsable string. - * @apiParam (body) {Number} [phoneNumber] The user's phone number, represented as a string. ->>>>>>> develop ->>>>>>> test - * @apiParamExample {json} Request-Example: * { "gender": "Male" } * @@ -271,14 +232,7 @@ module.exports = { "pronoun":"he/him", "email":"theo@klein.com", "phoneNumber":1234567890, -<<<<<<< HEAD - "gender": "Male", -======= -<<<<<<< HEAD -======= "gender": "Male", ->>>>>>> develop ->>>>>>> test "birthDate":Date("10/30/1997") } }