Skip to content

Commit 1bee227

Browse files
author
Harsh Patel
committed
Works for batch status updates
1 parent 04932d1 commit 1bee227

File tree

7 files changed

+222
-13
lines changed

7 files changed

+222
-13
lines changed

constants/general.constant.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ const HACKER_STATUSES = [
2323
HACKER_STATUS_CANCELLED,
2424
HACKER_STATUS_CHECKED_IN
2525
];
26+
const VALID_SEARCH_ACTIONS = [
27+
"change_status",
28+
"email",
29+
"change_status_and_email"
30+
];
31+
32+
const CORRESPONDING_STATUSES = {
33+
"change_status": HACKER_STATUSES,
34+
"email": ["Acceptance", "Waitlist", "Reminder"]
35+
}
2636

2737
const SAMPLE_DIET_RESTRICTIONS = [
2838
"None",
@@ -151,4 +161,6 @@ module.exports = {
151161
MAX_TEAM_SIZE: MAX_TEAM_SIZE,
152162
WEEK_OF: WEEK_OF,
153163
SAMPLE_DIET_RESTRICTIONS: SAMPLE_DIET_RESTRICTIONS,
164+
VALID_SEARCH_ACTIONS: VALID_SEARCH_ACTIONS,
165+
CORRESPONDING_STATUSES: CORRESPONDING_STATUSES
154166
};

constants/routes.constant.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,11 @@ const searchRoutes = {
214214
"get": {
215215
requestType: Constants.REQUEST_TYPES.GET,
216216
uri: "/api/search/"
217-
}
217+
},
218+
"bactchAction": {
219+
requestType: Constants.REQUEST_TYPES.GET,
220+
uri: "/api/search/action",
221+
},
218222
};
219223

220224
const staffRoutes = {

middlewares/search.middleware.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ const Middleware = {
1515
*/
1616
function parseQuery(req, res, next) {
1717
let query = req.body.q;
18-
1918
req.body.q = JSON.parse(query);
2019

2120
//Default page
@@ -62,6 +61,29 @@ async function executeQuery(req, res, next) {
6261
return next();
6362
}
6463

64+
/**
65+
*
66+
* @param {} req
67+
* @param {*} res
68+
* @param {*} next
69+
*
70+
* @returns
71+
*/
72+
async function executeStatusAction(req, res, next) {
73+
// NOW HAVE req.body.results as an array of "hackers potentially"
74+
console.log("GETS IN EXECUTE STATUS ACTION BRO!")
75+
req.body.results = await Services.Search.executeAction(req.body.model,
76+
req.body.q,
77+
req.body.page,
78+
req.body.limit,
79+
req.body.sort,
80+
req.body.sort_by,
81+
req.body.expand,
82+
req.body.status
83+
);
84+
return next();
85+
}
86+
6587
function setExpandTrue(req, res, next) {
6688
req.body.expand = true;
6789
next();
@@ -71,5 +93,6 @@ function setExpandTrue(req, res, next) {
7193
module.exports = {
7294
parseQuery: parseQuery,
7395
executeQuery: Middleware.Util.asyncMiddleware(executeQuery),
96+
executeStatusAction: Middleware.Util.asyncMiddleware(executeStatusAction),
7497
setExpandTrue: setExpandTrue,
7598
};

middlewares/validators/search.validator.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,16 @@ module.exports = {
1111
VALIDATOR.booleanValidator("query", "expand", true),
1212
VALIDATOR.searchValidator("query", "q")
1313
],
14+
searchActionValidator: [
15+
VALIDATOR.searchModelValidator("query", "model", false),
16+
VALIDATOR.alphaValidator("query", "sort", true),
17+
VALIDATOR.integerValidator("query", "page", true, 0),
18+
VALIDATOR.integerValidator("query", "limit", true, 0, 1000),
19+
VALIDATOR.searchSortValidator("query", "sort_by"),
20+
VALIDATOR.booleanValidator("query", "expand", true),
21+
VALIDATOR.searchValidator("query", "q"),
22+
/* ACTION VALIDATOR NEED TO MAKE WORK! */
23+
VALIDATOR.actionValidator("query", "action"),
24+
VALIDATOR.statusValidator("query", "status")
25+
],
1426
};

middlewares/validators/validator.helper.js

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ function integerValidator(fieldLocation, fieldname, optional = true, lowerBound
2727

2828
if (optional) {
2929
return value.optional({
30-
checkFalsy: true
31-
})
30+
checkFalsy: true
31+
})
3232
.isInt().withMessage(`${fieldname} must be an integer.`)
3333
.custom((value) => {
3434
return value >= lowerBound && value <= upperBound;
@@ -71,8 +71,8 @@ function mongoIdArrayValidator(fieldLocation, fieldname, optional = true) {
7171

7272
if (optional) {
7373
return arr.optional({
74-
checkFalsy: true
75-
})
74+
checkFalsy: true
75+
})
7676
.custom(isMongoIdArray).withMessage("Value must be an array of mongoIDs");
7777
} else {
7878
return arr.exists()
@@ -137,8 +137,8 @@ function regexValidator(fieldLocation, fieldname, optional = true, desire = Cons
137137

138138
if (optional) {
139139
return match.optional({
140-
checkFalsy: true
141-
})
140+
checkFalsy: true
141+
})
142142
.matches(desire)
143143
.withMessage("must be valid url");
144144
} else {
@@ -315,8 +315,8 @@ function jwtValidator(fieldLocation, fieldname, jwtSecret, optional = true) {
315315
const jwtValidationChain = setProperValidationChainBuilder(fieldLocation, fieldname, "Must be vali jwt");
316316
if (optional) {
317317
return jwtValidationChain.optional({
318-
checkFalsy: true
319-
})
318+
checkFalsy: true
319+
})
320320
.custom(value => {
321321
const token = jwt.verify(value, jwtSecret);
322322
if (typeof token !== "undefined") {
@@ -406,8 +406,8 @@ function searchValidator(fieldLocation, fieldname) {
406406
function searchSortValidator(fieldLocation, fieldName) {
407407
const searchSort = setProperValidationChainBuilder(fieldLocation, fieldName, "Invalid sort criteria")
408408
return searchSort.optional({
409-
checkFalsy: true
410-
})
409+
checkFalsy: true
410+
})
411411
.custom((value, {
412412
req
413413
}) => {
@@ -551,6 +551,38 @@ function enumValidator(fieldLocation, fieldname, enums, optional = true) {
551551
}
552552
}
553553

554+
/**
555+
* Validates that action field is a valid action from constants passed, and checks if corresponding new status is valid.
556+
* @param {"query" | "body" | "header" | "param"} fieldLocation The location where the field should be found.
557+
* @param {string} actionFieldName The name of the action that needs to be performed.
558+
* @param {string} statusFieldName The name of the action that needs to be performed.
559+
*/
560+
function actionValidator(fieldLocation, actionFieldName) {
561+
const actionValue = setProperValidationChainBuilder(fieldLocation, actionFieldName, "Invalid action.");
562+
563+
return actionValue.exists()
564+
.withMessage("The action must exist.")
565+
.custom(actionValidatorHelper).withMessage("The value must be a valid action.");
566+
}
567+
568+
569+
function statusValidator(fieldLocation, statusFieldName) {
570+
const statusValue = setProperValidationChainBuilder(fieldLocation, statusFieldName, "Invalid status.");
571+
return statusValue.exists().withMessage("The status must exist!").custom((val, {
572+
req
573+
}) => {
574+
return Constants.CORRESPONDING_STATUSES[req.query.action].includes(val);
575+
}).withMessage("The value must be a proper status.")
576+
}
577+
578+
function actionValidatorHelper(action) {
579+
if (Constants.VALID_SEARCH_ACTIONS.includes(action)) {
580+
return true;
581+
}
582+
return false;
583+
}
584+
585+
554586
/**
555587
* Checks that 'value' is part of 'enums'. 'enums' should be an enum dict.
556588
* @param {*} value Should be of the same type as the values of the enum
@@ -614,4 +646,6 @@ module.exports = {
614646
dateValidator: dateValidator,
615647
enumValidator: enumValidator,
616648
routesValidator: routesValidator,
649+
actionValidator: actionValidator,
650+
statusValidator: statusValidator
617651
};

routes/api/search.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,56 @@ module.exports = {
6767
Controllers.Search.searchResults
6868
);
6969

70+
/**
71+
* @api {get} /search/action execute an action on a specific query for any defined model
72+
* @apiName search
73+
* @apiGroup Search
74+
* @apiVersion 0.0.8
75+
*
76+
* @apiParam (query) {String} model the model to be searched
77+
* @apiParam (query) {Array} q the query to be executed. For more information on how to format this, please see https://docs.mchacks.ca/architecture/
78+
* @apiParam (query) {String} model the model to be searched
79+
* @apiParam (query) {String} sort either "asc" or "desc"
80+
* @apiParam (query) {number} page the page number that you would like
81+
* @apiParam (query) {number} limit the maximum number of results that you would like returned
82+
* @apiParam (query) {any} sort_by any parameter you want to sort the results by
83+
* @apiParam (query) {boolean} expand whether you want to expand sub documents within the results
84+
* @apiParam (query) {String} action type of action either Status or Email
85+
* @apiParam (query) {String} status new status or type of email
86+
*
87+
* @apiSuccess {String} message Success message
88+
* @apiSuccess {Object} data Results
89+
* @apiSuccessExample {object} Success-Response:
90+
* {
91+
"message": "Successfully executed query, returning all results",
92+
"data": [
93+
{...}
94+
]
95+
}
96+
*
97+
* @apiSuccess {String} message Success message
98+
* @apiSuccess {Object} data Empty object
99+
* @apiSuccessExample {object} Success-Response:
100+
* {
101+
"message": "No results found.",
102+
"data": {}
103+
}
104+
*
105+
* @apiError {String} message Error message
106+
* @apiError {Object} data empty
107+
* @apiErrorExample {object} Error-Response:
108+
* {"message": "Validation failed", "data": {}}
109+
*/
110+
searchRouter.route("/action").get(
111+
Middleware.Auth.ensureAuthenticated(),
112+
Middleware.Auth.ensureAuthorized(),
113+
Middleware.Validator.Search.searchActionValidator,
114+
Middleware.parseBody.middleware,
115+
Middleware.Search.parseQuery,
116+
Middleware.Search.executeStatusAction,
117+
Controllers.Search.searchResults
118+
);
119+
70120
apiRouter.use("/search", searchRouter);
71121
}
72122
};

services/search.service.js

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,80 @@ function executeQuery(model, queryArray, page, limit, sort, sort_by, shouldExpan
7272
.exec();
7373
}
7474

75+
76+
/**
77+
* @function executeAction
78+
* @param {string} model the model which is being searched
79+
* @param {Array} queryArray array of clauses for the query
80+
* @param {number} page the page number you want
81+
* @param {number} limit the limit to the number of responses you want
82+
* @param {"asc"|"desc"} sort which direction you want to sort by
83+
* @param {string} sort_by the attribute you want to sort by
84+
* @param {string} new_status the status you want to set of the queried model
85+
* @returns {Promise<[Array]>}
86+
* @description Builds and executes a search query based on a subset of mongodb
87+
*/
88+
function executeAction(model, queryArray, page, limit, sort, sort_by, shouldExpand = false, new_status) {
89+
var query;
90+
console.log("GETS IN EXECUTE ACTION BRO!")
91+
switch (model.toLowerCase()) {
92+
case "hacker":
93+
query = (shouldExpand) ? Hacker.find().populate([{
94+
path: "accountId",
95+
select: " -password"
96+
}, {
97+
path: "teamId"
98+
}]) : Hacker.find();
99+
break;
100+
default:
101+
return [];
102+
}
103+
for (var i in queryArray) {
104+
var clause = queryArray[i];
105+
var param = clause.param;
106+
var val = clause.value;
107+
switch (clause.operation) {
108+
case "equals":
109+
query.where(param).equals(val);
110+
break;
111+
case "ne":
112+
query.where(param).ne(val);
113+
break;
114+
case "lt":
115+
query.where(param).lt(val);
116+
break;
117+
case "gt":
118+
query.where(param).gt(val);
119+
break;
120+
case "lte":
121+
query.where(param).lte(val);
122+
break;
123+
case "gte":
124+
query.where(param).gte(val);
125+
break;
126+
case "in":
127+
query.where(param).in(val);
128+
break;
129+
case "regex":
130+
query.where(param).regex(val);
131+
break;
132+
case "elemMatch":
133+
query.where(param).elemMatch(val);
134+
break;
135+
}
136+
}
137+
138+
if (sort == "desc") {
139+
query.sort("-" + sort_by);
140+
} else if (sort == "asc") {
141+
query.sort(sort_by);
142+
}
143+
return query.limit(limit)
144+
.skip(limit * page).updateMany({ $set: { "status": new_status } })
145+
.exec();
146+
}
147+
75148
module.exports = {
76-
executeQuery: executeQuery
149+
executeQuery: executeQuery,
150+
executeAction: executeAction
77151
};

0 commit comments

Comments
 (0)