155 lines
4.2 KiB
JavaScript
155 lines
4.2 KiB
JavaScript
import { omit, identity, toNumber, isString, isUndefined } from 'lodash';
|
|
import { notImplemented } from 'boom';
|
|
import joi from 'joi';
|
|
import Promise from 'bluebird';
|
|
|
|
const sequelizeKeys = ['include', 'order', 'limit', 'offset'];
|
|
|
|
const getModels = (request) => {
|
|
const noGetDb = typeof request.getDb !== 'function';
|
|
const noRequestModels = !request.models;
|
|
if (noGetDb && noRequestModels) {
|
|
return notImplemented('`request.getDb` or `request.models` are not defined.'
|
|
+ 'Be sure to load hapi-sequelize before hapi-sequelize-crud.');
|
|
}
|
|
|
|
const { models } = noGetDb ? request : request.getDb();
|
|
|
|
return models;
|
|
};
|
|
|
|
const getModelInstance = (models, includeItem) => {
|
|
return new Promise(async(resolve) => {
|
|
if (includeItem) {
|
|
if (typeof includeItem !== 'object') {
|
|
const singluarOrPluralMatch = Object.keys(models).find((modelName) => {
|
|
const { _singular, _plural } = models[modelName];
|
|
return _singular === includeItem || _plural === includeItem;
|
|
});
|
|
|
|
if (singluarOrPluralMatch) {
|
|
return resolve(models[singluarOrPluralMatch]);
|
|
}
|
|
}
|
|
|
|
if (typeof includeItem === 'string' && models.hasOwnProperty(includeItem)) {
|
|
return resolve(models[includeItem]);
|
|
} else if (typeof includeItem === 'object') {
|
|
if (
|
|
typeof includeItem.model === 'string' &&
|
|
includeItem.model.length &&
|
|
models.hasOwnProperty(includeItem.model)
|
|
) {
|
|
includeItem.model = models[includeItem.model];
|
|
}
|
|
if (includeItem.hasOwnProperty('include')) {
|
|
includeItem.include = await getModelInstance(models, includeItem.include);
|
|
return resolve(includeItem);
|
|
} else {
|
|
return resolve(includeItem);
|
|
}
|
|
}
|
|
}
|
|
return resolve(includeItem);
|
|
});
|
|
};
|
|
|
|
export const parseInclude = async(request) => {
|
|
if (typeof request.query.include === 'undefined') return [];
|
|
|
|
const include = Array.isArray(request.query.include)
|
|
? request.query.include
|
|
: [request.query.include]
|
|
;
|
|
|
|
const models = getModels(request);
|
|
if (models.isBoom) return models;
|
|
|
|
const jsonValidation = joi.string().regex(/^\{.*?"model":.*?\}$/);
|
|
const includes = include.map(async(b) => {
|
|
let a = b;
|
|
try {
|
|
if (!jsonValidation.validate(a).error) {
|
|
a = JSON.parse(b);
|
|
}
|
|
} catch (e) {
|
|
//
|
|
}
|
|
|
|
return getModelInstance(models, a);
|
|
}).filter(identity);
|
|
|
|
return await Promise.all(includes);
|
|
};
|
|
|
|
export const parseWhere = request => {
|
|
const where = omit(request.query, sequelizeKeys);
|
|
|
|
for (const key of Object.keys(where)) {
|
|
try {
|
|
where[key] = JSON.parse(where[key]);
|
|
} catch (e) {
|
|
//
|
|
}
|
|
}
|
|
|
|
return where;
|
|
};
|
|
|
|
export const parseLimitAndOffset = (request) => {
|
|
const { limit, offset } = request.query;
|
|
const out = {};
|
|
if (!isUndefined(limit)) {
|
|
out.limit = toNumber(limit);
|
|
}
|
|
if (!isUndefined(offset)) {
|
|
out.offset = toNumber(offset);
|
|
}
|
|
return out;
|
|
};
|
|
|
|
const parseOrderArray = (order, models) => {
|
|
return order.map((requestColumn) => {
|
|
if (Array.isArray(requestColumn)) {
|
|
return parseOrderArray(requestColumn, models);
|
|
}
|
|
|
|
let column;
|
|
try {
|
|
column = JSON.parse(requestColumn);
|
|
} catch (e) {
|
|
column = requestColumn;
|
|
}
|
|
|
|
if (column.model) column.model = models[column.model];
|
|
|
|
return column;
|
|
});
|
|
};
|
|
|
|
export const parseOrder = (request) => {
|
|
const { order } = request.query;
|
|
|
|
if (!order) return null;
|
|
|
|
const models = getModels(request);
|
|
if (models.isBoom) return models;
|
|
|
|
// transform to an array so sequelize will escape the input for us and
|
|
// maintain security. See http://docs.sequelizejs.com/en/latest/docs/querying/#ordering
|
|
const requestOrderColumns = isString(order) ? [order.split(' ')] : order;
|
|
|
|
const parsedOrder = parseOrderArray(requestOrderColumns, models);
|
|
|
|
return parsedOrder;
|
|
};
|
|
|
|
export const getMethod = (model, association, plural = true, method = 'get') => {
|
|
const a = plural ? association.original.plural : association.original.singular;
|
|
const b = plural ? association.original.singular : association.original.plural; // alternative
|
|
const fn = model[`${method}${a}`] || model[`${method}${b}`];
|
|
if (fn) return fn.bind(model);
|
|
|
|
return false;
|
|
};
|