Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- 'use strict';
- // npm packages
- const Joi = require('joi');
- const _ = require('lodash');
- const Async = require('async');
- const Promise = require('bluebird');
- const Debug = require('debug');
- const boom = require('boom');
- // custom modules
- const qHelpers = require('./queryParserHelpers');
- const qSchemas = require('./queryParserSchemas');
- // unpack helpers
- const extractModelProps = qHelpers.extractModelProps;
- const collectionFetcher = qHelpers.collectionFetcher;
- const typesFromModelGleamer = qHelpers.typesFromModelGleamer;
- const schemaBuilder = qHelpers.schemaBuilder;
- // unpack schemas
- const schemaTypeMap = qSchemas.schemaTypeMap;
- const querierSort = qSchemas.querierSort;
- const paginationSchema = qSchemas.paginationSchema;
- const querierFilter = qSchemas.querierFilter;
- /**
- * This function builds a waterline-query from a
- * strongloop-style `query` object before executing the query
- * and returning a promise that resolves or rejects. It is used
- * from within route handlers
- * @param {object} model Waterline ORM model
- * @param {object} query Query parameter passed in from route handler `request.query.filter`
- * @return {promise} returns a promise that `resolve`s or `reject`s based on whether schema checks pass or other error
- */
- module.exports = (model, query) => {
- const pr = model;
- const collectionFields = collectionFetcher(pr);
- const modelProps = extractModelProps(pr);
- let where = query.where === undefined ? {} : query.where;
- let include = query.include;
- let order = query.order;
- let populates = query.populates;
- let sort = query.sort;
- let skip = query.skip;
- let limit = query.limit;
- let pagination = query.pagination === undefined ? {} : query.pagination;
- populates = include || populates || null;
- sort = order || sort || null;
- const populatesSchema = Joi.array().items(
- Joi.string().valid(collectionFields)
- ).default([]);
- return new Promise((resolve,reject) => {
- Async.auto({
- setCondition: (cb) => {
- if (!_.isObject(where)) {
- return cb(null, {});
- }
- typesFromModelGleamer(model, where, (err, keyTypes) => {
- if (where.or) {
- keyTypes.or = 'array';
- }
- schemaBuilder(keyTypes, schemaTypeMap, where, (err, condSchema) => {
- condSchema.validate(where, (err, value) => {
- if (err) {
- return cb(boom.badRequest('where clause validation failure', err)); //eslint-disable-line
- }
- return cb(null, value);
- });
- });
- });
- },
- where: ['setCondition', (cb, results) => {
- if (where.or && where.or instanceof Array) {
- where.or.map(e => {
- // filter out keys of objects in or which arent props of model
- Object.keys(e).filter(k => modelProps.indexOf())
- });
- }
- }],
- populate: ['setCondition', (cb, results) => {
- }],
- sort: ['setCondition', (cb, results) => {
- }],
- pagination: ['setCondition', (cb, results) => {
- }],
- buildQuery: ['setCondition', (cb, results) => {
- let condition = results.setCondition;
- let replacedOperator = {};
- let replacedCondition = {};
- let query;
- let lookupMap = { gt: '>', lt: '<', gte: '>=', lte: '<=' };
- if (!_.isEmpty(condition)) {
- // TODO: the following two lines are questionable
- let keysArray = Object.keys(condition);
- let conditionKey = condition[keysArray];
- // if conditionKey is an object, and there is not an OR condition in there
- if (_.isObject(conditionKey) && keysArray[0].toLowerCase() !== 'or') {
- // generate new conditions
- _.each(conditionKey, (value, key) => {
- key = lookupMap[key] || key;
- replacedOperator[key] = value;
- });
- Async.forEachOf(condition,(value,key,cbk)=> {
- replacedCondition[key] = replacedOperator;
- cbk();
- });
- query = model.find(replacedCondition);
- } else {
- query = model.find(condition);
- }
- } else {
- query = model.find({});
- }
- // sort
- if (_.isString(sort)) {
- let sortArr = sort.split(' ');
- let sortObj = {
- field: sortArr[0],
- order: sortArr[1]
- };
- querierSort.validate(sortObj, (err, value) => {
- if (err) {
- return reject(boom.badRequest('sort filter validation error', err));
- }
- let sortString = `${value.field} ${value.order}`;
- query.sort(sortString);
- });
- }
- if (_.isObject(pagination)) {
- const paginationSchemas = Joi.object().keys({
- page: Joi.number().integer().positive().min(1),
- limit: Joi.number().integer().positive().min(1)
- });
- // { page: x, limit: y } pagination
- paginationSchemas.validate(pagination, (err, value) => {
- if (err) {
- return reject(boom.badRequest('pagination validation error', err));
- }
- query.paginate(value);
- });
- } else {
- // skip, limit pagination
- Debug('limit', limit); // eslint-disable-line
- if (_.isNumber(limit)) {
- query.limit(limit);
- }
- if (_.isNumber(skip)) {
- query.skip(skip);
- }
- }
- return cb(null, query);
- }],
- setPopulate: ['buildQuery', (cb, results) => {
- let query = results.buildQuery;
- if (_.isEmpty(populates)) {
- return cb(null, query);
- }
- if (_.isString(populates)) {
- // make populates an array if it is not
- populates = populates.split();
- }
- populatesSchema.validate(populates, (err, values) => {
- if (err) {
- return cb(err);
- }
- Async.each(values, (val, eachCb) => {
- query.populate(val);
- return eachCb();
- }, () => {
- cb(null, query);
- });
- });
- }],
- execQuery: ['setPopulate', (cb, results) => results.setPopulate.exec(cb)]
- }, (err, results) => (err) ? reject(err) : resolve(results.execQuery));
- });
- };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement