四好公路
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

162 lines
5.1 KiB

'use strict';
var util = require('util'),
Base = require('./base'),
_ = require('lodash'),
errors = require('../Errors');
var List = function(args) {
List.super_.call(this, args);
};
util.inherits(List, Base);
List.prototype.action = 'list';
List.prototype.method = 'get';
List.prototype.plurality = 'plural';
List.prototype._safeishParse = function(value) {
try {
return JSON.parse(value);
} catch(err) {
return value;
}
};
var stringOperators = /like|iLike|notLike|notILike/;
List.prototype.fetch = function(req, res, context) {
var self = this,
model = this.model,
options = context.options || {},
criteria = context.criteria || {},
include = this.include,
includeAttributes = this.includeAttributes,
Sequelize = this.resource.sequelize,
defaultCount = 100,
count = +context.count || +req.query.count || defaultCount,
offset = +context.offset || +req.query.offset || 0;
// only look up attributes we care about
options.attributes = options.attributes || this.resource.attributes;
// account for offset and count
offset += context.page * count || req.query.page * count || 0;
if (count > 1000) count = 1000;
if (count < 0) count = defaultCount;
options.offset = offset;
options.limit = count;
if (!this.resource.pagination)
delete options.limit;
if (context.include && context.include.length) {
include = include.concat(context.include);
}
if (include.length) {
options.include = include;
}
var searchParams = this.resource.search.length ? this.resource.search : [this.resource.search];
searchParams.forEach(function(searchData) {
var searchParam = searchData.param;
if (_.has(req.query, searchParam)) {
var search = [];
var searchOperator = searchData.operator || '$like';
var searchAttributes =
searchData.attributes || Object.keys(model.rawAttributes);
searchAttributes.forEach(function(attr) {
if(stringOperators.test(searchOperator)){
var attrType = model.rawAttributes[attr].type;
if (!(attrType instanceof Sequelize.STRING) &&
!(attrType instanceof Sequelize.TEXT)) {
// NOTE: Sequelize has added basic validation on types, so we can't get
// away with blind comparisons anymore. The feature is up for
// debate so this may be changed in the future
return;
}
}
var item = {};
var query = {};
var searchString;
if (!~searchOperator.toLowerCase().indexOf('like')) {
searchString = req.query[searchParam];
} else {
searchString = '%' + req.query[searchParam] + '%';
}
query[searchOperator] = searchString;
item[attr] = query;
search.push(item);
});
if (Object.keys(criteria).length)
criteria = Sequelize.and(criteria, Sequelize.or.apply(null, search));
else
criteria = Sequelize.or.apply(null, search);
}
});
var sortParam = this.resource.sort.param;
if (_.has(req.query, sortParam) || _.has(this.resource.sort, 'default')) {
var order = [];
var columnNames = [];
var sortQuery = req.query[sortParam] || this.resource.sort.default || '';
var sortColumns = sortQuery.split(',');
sortColumns.forEach(function(sortColumn) {
if (sortColumn.indexOf('-') === 0) {
var actualName = sortColumn.substring(1);
order.push([actualName, 'DESC']);
columnNames.push(actualName);
} else {
columnNames.push(sortColumn);
order.push([sortColumn, 'ASC']);
}
});
var allowedColumns = this.resource.sort.attributes || Object.keys(model.rawAttributes);
var disallowedColumns = _.difference(columnNames, allowedColumns);
if (disallowedColumns.length) {
throw new errors.BadRequestError('Sorting not allowed on given attributes', disallowedColumns);
}
if (order.length)
options.order = order;
}
// all other query parameters are passed to search
var extraSearchCriteria = _.reduce(req.query, function(result, value, key) {
if (_.has(model.rawAttributes, key)) result[key] = self._safeishParse(value);
return result;
}, {});
if (Object.keys(extraSearchCriteria).length)
criteria = _.assign(criteria, extraSearchCriteria);
// do the actual lookup
if (Object.keys(criteria).length)
options.where = criteria;
return model
.findAndCountAll(options)
.then(function(result) {
context.instance = result.rows;
var start = offset;
var end = start + result.rows.length - 1;
end = end === -1 ? 0 : end;
if (self.resource.associationOptions.removeForeignKeys) {
_.each(context.instance, function(instance) {
_.each(includeAttributes, function(attr) {
delete instance[attr];
delete instance.dataValues[attr];
});
});
}
if (!!self.resource.pagination)
res.header('Content-Range', 'items ' + [[start, end].join('-'), result.count].join('/'));
return context.continue;
});
};
module.exports = List;