Refactored codes to not modify get-config-for-method.test.js, moved validationAssociations definition logic from get-config-for-method.js to crud.js, fixed README.md

This commit is contained in:
Muhammad Labib Ramadhan 2016-11-07 08:48:52 +07:00
parent 1977304287
commit 11306667d6
4 changed files with 59 additions and 57 deletions

View File

@ -92,13 +92,25 @@ It's easy to restrict your requests using Sequelize's `where` query option. Just
Team.findOne({ where: { city: 'windsor' }}) Team.findOne({ where: { city: 'windsor' }})
``` ```
You can also do more complex queries by setting the value of a key to JSON.
```js
// returns only teams that have a `address.city` property of "windsor"
// GET /team?city={"address": "windsor"}
// or
// GET /team?city[address]=windsor
// results in the Sequelize query:
Team.findOne({ where: { address: { city: 'windsor' }}})
```
## `include` queries ## `include` queries
Getting related models is easy, just use a query parameter `include`. Getting related models is easy, just use a query parameter `include`.
```js ```js
// returns all teams with their related City model // returns all teams with their related City model
// GET /teams?include=city or // GET /teams?include=city or
// GET /teams?include={"include": {"model": "City"}}} // GET /teams?include={"model": "City"}
// results in a Sequelize query: // results in a Sequelize query:
@ -126,7 +138,7 @@ Team.findAll({include: [Player]})
Filtering by related models property, you can pass **where** paremeter inside each **include** item(s) object. Filtering by related models property, you can pass **where** paremeter inside each **include** item(s) object.
```js ```js
// returns all team with their related City where City property name equals Healdsburg // returns all team with their related City where City property name equals Healdsburg
// GET /teams?include={"include": {"model": "City", "where": {"name": "Healdsburg"}}} // GET /teams?include={"model": "City", "where": {"name": "Healdsburg"}}
// results in a Sequelize query: // results in a Sequelize query:
Team.findAll({include: {model: City, where: {name: 'Healdsburg'}}}) Team.findAll({include: {model: City, where: {name: 'Healdsburg'}}})

View File

@ -5,7 +5,7 @@ import _ from 'lodash';
import { parseInclude, parseWhere, parseLimitAndOffset, parseOrder } from './utils'; import { parseInclude, parseWhere, parseLimitAndOffset, parseOrder } from './utils';
import { notFound } from 'boom'; import { notFound } from 'boom';
import * as associations from './associations/index'; import * as associations from './associations/index';
import getConfigForMethod from './get-config-for-method.js'; import getConfigForMethod, { sequelizeOperators } from './get-config-for-method.js';
const createAll = ({ const createAll = ({
server, server,
@ -13,7 +13,7 @@ const createAll = ({
prefix, prefix,
config, config,
attributeValidation, attributeValidation,
modelAssociations, associationValidation,
scopes, scopes,
}) => { }) => {
Object.keys(methods).forEach((method) => { Object.keys(methods).forEach((method) => {
@ -24,7 +24,7 @@ const createAll = ({
config: getConfigForMethod({ config: getConfigForMethod({
method, method,
attributeValidation, attributeValidation,
modelAssociations, associationValidation,
config, config,
scopes, scopes,
}), }),
@ -71,6 +71,27 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
return params; return params;
}, {}); }, {});
const modelsHasAssociations = modelAssociations && modelAssociations.length;
const validAssociationsString = modelsHasAssociations
? joi.string().valid(...modelAssociations)
: joi.valid(null);
const validAssociationsObject = modelsHasAssociations
? joi.object().keys({
model: joi.string().valid(...modelAssociations),
where: joi.object().keys({
...attributeValidation,
...sequelizeOperators,
}),
})
: joi.valid(null);
const associationValidation = {
include: [
joi.array().items(validAssociationsString),
joi.array().items(validAssociationsObject),
validAssociationsString,
validAssociationsObject,
],
};
const scopes = Object.keys(model.options.scopes); const scopes = Object.keys(model.options.scopes);
// if we don't have any permissions set, just create all the methods // if we don't have any permissions set, just create all the methods
@ -81,7 +102,7 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
prefix, prefix,
config, config,
attributeValidation, attributeValidation,
modelAssociations, associationValidation,
scopes, scopes,
}); });
// if permissions are set, but we can't parse them, throw an error // if permissions are set, but we can't parse them, throw an error
@ -96,7 +117,7 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
prefix, prefix,
config, config,
attributeValidation, attributeValidation,
modelAssociations, associationValidation,
scopes, scopes,
}); });
// if we've gotten here, we have complex permissions and need to set them // if we've gotten here, we have complex permissions and need to set them
@ -118,7 +139,7 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
config: getConfigForMethod({ config: getConfigForMethod({
method, method,
attributeValidation, attributeValidation,
modelAssociations, associationValidation,
scopes, scopes,
config: permissionConfig, config: permissionConfig,
}), }),
@ -130,7 +151,7 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
model, model,
prefix, prefix,
attributeValidation, attributeValidation,
modelAssociations, associationValidation,
scopes, scopes,
config: permissionConfig, config: permissionConfig,
}); });

View File

@ -72,7 +72,7 @@ export const restrictMethods = [
]; ];
export default ({ export default ({
method, attributeValidation, modelAssociations, scopes = [], config = {}, method, attributeValidation, associationValidation, scopes = [], config = {},
}) => { }) => {
const hasWhere = whereMethods.includes(method); const hasWhere = whereMethods.includes(method);
const hasInclude = includeMethods.includes(method); const hasInclude = includeMethods.includes(method);
@ -96,27 +96,9 @@ export default ({
} }
if (hasInclude) { if (hasInclude) {
const modelsHasAssociations = modelAssociations && modelAssociations.length;
const validAssociationsString = modelsHasAssociations
? joi.string().valid(...modelAssociations)
: joi.valid(null);
const validAssociationsObject = modelsHasAssociations
? joi.object().keys({
model: joi.string().valid(...modelAssociations),
where: joi.object().keys({
...attributeValidation,
...sequelizeOperators,
}),
})
: joi.valid(null);
const query = concatToJoiObject(joi.object() const query = concatToJoiObject(joi.object()
.keys({ .keys({
include: [ ...associationValidation,
joi.array().items(validAssociationsString),
joi.array().items(validAssociationsObject),
validAssociationsString,
validAssociationsObject,
],
}), }),
get(methodConfig, 'validate.query') get(methodConfig, 'validate.query')
); );

View File

@ -20,19 +20,8 @@ test.beforeEach((t) => {
myKey: joi.any(), myKey: joi.any(),
}; };
const validAssociationsString = joi.string().valid(...t.context.models);
const validAssociationsObject = joi.object().keys({
model: joi.string().valid(...t.context.models),
where: joi.object(),
});
t.context.associationValidation = { t.context.associationValidation = {
include: [ include: joi.array().items(joi.string().valid(t.context.models)),
joi.array().items(validAssociationsString),
joi.array().items(validAssociationsObject),
validAssociationsString,
validAssociationsObject,
],
}; };
t.context.config = { t.context.config = {
@ -162,12 +151,12 @@ test('query attributeValidation w/ config as joi object', (t) => {
test('validate.query associationValidation', (t) => { test('validate.query associationValidation', (t) => {
const { attributeValidation, associationValidation, models } = t.context; const { attributeValidation, associationValidation, models } = t.context;
const modelAssociations = models;
includeMethods.forEach((method) => { includeMethods.forEach((method) => {
const configForMethod = getConfigForMethod({ const configForMethod = getConfigForMethod({
method, method,
attributeValidation, attributeValidation,
modelAssociations, associationValidation,
}); });
const { query } = configForMethod.validate; const { query } = configForMethod.validate;
@ -194,7 +183,6 @@ test('validate.query associationValidation', (t) => {
test('query associationValidation w/ config as plain object', (t) => { test('query associationValidation w/ config as plain object', (t) => {
const { associationValidation, models } = t.context; const { associationValidation, models } = t.context;
const modelAssociations = models;
const config = { const config = {
validate: { validate: {
query: { query: {
@ -206,7 +194,7 @@ test('query associationValidation w/ config as plain object', (t) => {
includeMethods.forEach((method) => { includeMethods.forEach((method) => {
const configForMethod = getConfigForMethod({ const configForMethod = getConfigForMethod({
method, method,
modelAssociations, associationValidation,
config, config,
}); });
const { query } = configForMethod.validate; const { query } = configForMethod.validate;
@ -234,7 +222,6 @@ test('query associationValidation w/ config as plain object', (t) => {
test('query associationValidation w/ config as joi object', (t) => { test('query associationValidation w/ config as joi object', (t) => {
const { associationValidation, models } = t.context; const { associationValidation, models } = t.context;
const modelAssociations = models;
const queryKeys = { const queryKeys = {
aKey: joi.boolean(), aKey: joi.boolean(),
}; };
@ -247,7 +234,7 @@ test('query associationValidation w/ config as joi object', (t) => {
includeMethods.forEach((method) => { includeMethods.forEach((method) => {
const configForMethod = getConfigForMethod({ const configForMethod = getConfigForMethod({
method, method,
modelAssociations, associationValidation,
config, config,
}); });
const { query } = configForMethod.validate; const { query } = configForMethod.validate;