Merge pull request #20 from Getable/error-on-invalid-where
Validate query and payload
This commit is contained in:
commit
196999a4c5
68
README.md
68
README.md
@ -11,6 +11,9 @@ npm install -S hapi-sequelize-crud
|
|||||||
|
|
||||||
##Configure
|
##Configure
|
||||||
|
|
||||||
|
Please note that you should register `hapi-sequelize-crud` after defining your
|
||||||
|
associations.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// First, register hapi-sequelize
|
// First, register hapi-sequelize
|
||||||
await register({
|
await register({
|
||||||
@ -35,6 +38,7 @@ await register({
|
|||||||
// `models` property. If you omit this property, all models will have
|
// `models` property. If you omit this property, all models will have
|
||||||
// models defined for them. e.g.
|
// models defined for them. e.g.
|
||||||
models: ['cat', 'dog'] // only the cat and dog models will have routes created
|
models: ['cat', 'dog'] // only the cat and dog models will have routes created
|
||||||
|
|
||||||
// or
|
// or
|
||||||
models: [
|
models: [
|
||||||
// possible methods: list, get, scope, create, destroy, destroyAll, destroyScope, update
|
// possible methods: list, get, scope, create, destroy, destroyAll, destroyScope, update
|
||||||
@ -54,20 +58,59 @@ await register({
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
* list: get all rows in a table
|
* **list**: get all rows in a table
|
||||||
* get: get a single row
|
* **get**: get a single row
|
||||||
* scope: reference a [sequelize scope](http://docs.sequelizejs.com/en/latest/api/model/#scopeoptions-model)
|
* **scope**: reference a [sequelize scope](http://docs.sequelizejs.com/en/latest/api/model/#scopeoptions-model)
|
||||||
* create: create a new row
|
* **create**: create a new row
|
||||||
* destroy: delete a row
|
* **destroy**: delete a row
|
||||||
* destroyAll: delete all models in the table
|
* **destroyAll**: delete all models in the table
|
||||||
* destroyScope: use a [sequelize scope](http://docs.sequelizejs.com/en/latest/api/model/#scopeoptions-model) to find rows, then delete them
|
* **destroyScope**: use a [sequelize scope](http://docs.sequelizejs.com/en/latest/api/model/#scopeoptions-model) to find rows, then delete them
|
||||||
* update: update a row
|
* **update**: update a row
|
||||||
|
|
||||||
|
## `where` queries
|
||||||
|
It's easy to restrict your requests using Sequelize's `where` query option. Just pass a query parameter.
|
||||||
|
|
||||||
Please note that you should register `hapi-sequelize-crud` after defining your
|
```js
|
||||||
associations.
|
// returns only teams that have a `city` property of "windsor"
|
||||||
|
// GET /team?city=windsor
|
||||||
|
|
||||||
##What do I get
|
// results in the Sequelize query:
|
||||||
|
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
|
||||||
|
Getting related models is easy, just use a query parameter `include`.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// returns all teams with their related City model
|
||||||
|
// GET /teams?include=City
|
||||||
|
|
||||||
|
// results in a Sequelize query:
|
||||||
|
Team.findAll({include: City})
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to get multiple related models, just pass multiple `include` parameters.
|
||||||
|
```js
|
||||||
|
// returns all teams with their related City and Uniform models
|
||||||
|
// GET /teams?include=City&include=Uniform
|
||||||
|
|
||||||
|
// results in a Sequelize query:
|
||||||
|
Team.findAll({include: [City, Uniform]})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Full list of methods
|
||||||
|
|
||||||
Let's say you have a `many-to-many` association like this:
|
Let's say you have a `many-to-many` association like this:
|
||||||
|
|
||||||
@ -82,8 +125,9 @@ You get these:
|
|||||||
# get an array of records
|
# get an array of records
|
||||||
GET /team/{id}/roles
|
GET /team/{id}/roles
|
||||||
GET /role/{id}/teams
|
GET /role/{id}/teams
|
||||||
# might also append query parameters to search for
|
# might also append `where` query parameters to search for
|
||||||
GET /role/{id}/teams?members=5
|
GET /role/{id}/teams?members=5
|
||||||
|
GET /role/{id}/teams?city=healdsburg
|
||||||
|
|
||||||
# you might also use scopes
|
# you might also use scopes
|
||||||
GET /teams/{scope}/roles/{scope}
|
GET /teams/{scope}/roles/{scope}
|
||||||
|
16
package.json
16
package.json
@ -25,25 +25,25 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ava": "^0.16.0",
|
"ava": "^0.16.0",
|
||||||
"babel-cli": "^6.10.1",
|
"babel-cli": "^6.14.0",
|
||||||
"babel-plugin-add-module-exports": "^0.2.1",
|
"babel-plugin-add-module-exports": "^0.2.1",
|
||||||
"babel-plugin-closure-elimination": "^1.0.6",
|
"babel-plugin-closure-elimination": "^1.0.6",
|
||||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.10.3",
|
"babel-plugin-transform-es2015-modules-commonjs": "^6.14.0",
|
||||||
"babel-preset-stage-1": "^6.5.0",
|
"babel-preset-stage-1": "^6.13.0",
|
||||||
"eslint": "^3.4.0",
|
"eslint": "^3.4.0",
|
||||||
"eslint-config-pichak": "1.1.0",
|
"eslint-config-pichak": "^1.1.2",
|
||||||
"eslint-plugin-ava": "^3.0.0",
|
"eslint-plugin-ava": "^3.0.0",
|
||||||
"ghooks": "1.0.3",
|
"ghooks": "^1.3.2",
|
||||||
"scripty": "^1.6.0",
|
"scripty": "^1.6.0",
|
||||||
"sinon": "^1.17.5",
|
"sinon": "^1.17.5",
|
||||||
"sinon-bluebird": "^3.0.2",
|
"sinon-bluebird": "^3.0.2",
|
||||||
"tap-xunit": "^1.4.0"
|
"tap-xunit": "^1.4.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"boom": "^3.2.2",
|
"boom": "^4.0.0",
|
||||||
"joi": "7.2.1",
|
"joi": "^9.0.4",
|
||||||
"lodash": "4.0.0"
|
"lodash": "^4.15.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"babel-polyfill": "^6.13.0"
|
"babel-polyfill": "^6.13.0"
|
||||||
|
115
src/crud.js
115
src/crud.js
@ -5,10 +5,28 @@ import _ from 'lodash';
|
|||||||
import { parseInclude, parseWhere } from './utils';
|
import { parseInclude, parseWhere } 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';
|
||||||
|
|
||||||
const createAll = ({ server, model, prefix, config }) => {
|
const createAll = ({
|
||||||
|
server,
|
||||||
|
model,
|
||||||
|
prefix,
|
||||||
|
config,
|
||||||
|
attributeValidation,
|
||||||
|
associationValidation,
|
||||||
|
}) => {
|
||||||
Object.keys(methods).forEach((method) => {
|
Object.keys(methods).forEach((method) => {
|
||||||
methods[method]({ server, model, prefix, config });
|
methods[method]({
|
||||||
|
server,
|
||||||
|
model,
|
||||||
|
prefix,
|
||||||
|
config: getConfigForMethod({
|
||||||
|
method,
|
||||||
|
attributeValidation,
|
||||||
|
associationValidation,
|
||||||
|
config,
|
||||||
|
}),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -34,13 +52,43 @@ models: {
|
|||||||
|
|
||||||
export default (server, model, { prefix, defaultConfig: config, models: permissions }) => {
|
export default (server, model, { prefix, defaultConfig: config, models: permissions }) => {
|
||||||
const modelName = model._singular;
|
const modelName = model._singular;
|
||||||
|
const modelAttributes = Object.keys(model.attributes);
|
||||||
|
const modelAssociations = Object.keys(model.associations);
|
||||||
|
|
||||||
|
const attributeValidation = modelAttributes.reduce((params, attribute) => {
|
||||||
|
params[attribute] = joi.any();
|
||||||
|
return params;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const associationValidation = {
|
||||||
|
include: joi.array().items(joi.string().valid(...modelAssociations)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// if we don't have any permissions set, just create all the methods
|
||||||
if (!permissions) {
|
if (!permissions) {
|
||||||
createAll({ server, model, prefix, config });
|
createAll({
|
||||||
|
server,
|
||||||
|
model,
|
||||||
|
prefix,
|
||||||
|
config,
|
||||||
|
attributeValidation,
|
||||||
|
associationValidation,
|
||||||
|
});
|
||||||
|
// if permissions are set, but we can't parse them, throw an error
|
||||||
} else if (!Array.isArray(permissions)) {
|
} else if (!Array.isArray(permissions)) {
|
||||||
throw new Error('hapi-sequelize-crud: `models` property must be an array');
|
throw new Error('hapi-sequelize-crud: `models` property must be an array');
|
||||||
|
// if permissions are set, but the only thing we've got is a model name, there
|
||||||
|
// are no permissions to be set, so just create all methods and move on
|
||||||
} else if (permissions.includes(modelName)) {
|
} else if (permissions.includes(modelName)) {
|
||||||
createAll({ server, model, prefix, config });
|
createAll({
|
||||||
|
server,
|
||||||
|
model,
|
||||||
|
prefix,
|
||||||
|
config,
|
||||||
|
attributeValidation,
|
||||||
|
associationValidation,
|
||||||
|
});
|
||||||
|
// if we've gotten here, we have complex permissions and need to set them
|
||||||
} else {
|
} else {
|
||||||
const permissionOptions = permissions.filter((permission) => {
|
const permissionOptions = permissions.filter((permission) => {
|
||||||
return permission.model === modelName;
|
return permission.model === modelName;
|
||||||
@ -56,11 +104,23 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
|
|||||||
server,
|
server,
|
||||||
model,
|
model,
|
||||||
prefix,
|
prefix,
|
||||||
config: permissionConfig,
|
config: getConfigForMethod({
|
||||||
|
method,
|
||||||
|
attributeValidation,
|
||||||
|
associationValidation,
|
||||||
|
config: permissionConfig,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
createAll({ server, model, prefix, config: permissionConfig });
|
createAll({
|
||||||
|
server,
|
||||||
|
model,
|
||||||
|
prefix,
|
||||||
|
attributeValidation,
|
||||||
|
associationValidation,
|
||||||
|
config: permissionConfig,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -102,19 +162,21 @@ export const get = ({ server, model, prefix = '/', config }) => {
|
|||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
if (id) where[model.primaryKeyField] = id;
|
if (id) where[model.primaryKeyField] = id;
|
||||||
|
|
||||||
|
if (include instanceof Error) return void reply(include);
|
||||||
|
|
||||||
const instance = await model.findOne({ where, include });
|
const instance = await model.findOne({ where, include });
|
||||||
|
|
||||||
if (!instance) return void reply(notFound(`${id} not found.`));
|
if (!instance) return void reply(notFound(`${id} not found.`));
|
||||||
|
|
||||||
reply(instance);
|
reply(instance);
|
||||||
},
|
},
|
||||||
config: _.defaultsDeep({
|
config: _.defaultsDeep(config, {
|
||||||
validate: {
|
validate: {
|
||||||
params: joi.object().keys({
|
params: {
|
||||||
id: joi.any(),
|
id: joi.any(),
|
||||||
}),
|
},
|
||||||
},
|
},
|
||||||
}, config),
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -130,17 +192,19 @@ export const scope = ({ server, model, prefix = '/', config }) => {
|
|||||||
const include = parseInclude(request);
|
const include = parseInclude(request);
|
||||||
const where = parseWhere(request);
|
const where = parseWhere(request);
|
||||||
|
|
||||||
|
if (include instanceof Error) return void reply(include);
|
||||||
|
|
||||||
const list = await model.scope(request.params.scope).findAll({ include, where });
|
const list = await model.scope(request.params.scope).findAll({ include, where });
|
||||||
|
|
||||||
reply(list);
|
reply(list);
|
||||||
},
|
},
|
||||||
config: _.defaultsDeep({
|
config: _.defaultsDeep(config, {
|
||||||
validate: {
|
validate: {
|
||||||
params: joi.object().keys({
|
params: {
|
||||||
scope: joi.string().valid(...scopes),
|
scope: joi.string().valid(...scopes),
|
||||||
}),
|
},
|
||||||
},
|
},
|
||||||
}, config),
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -213,19 +277,21 @@ export const destroyScope = ({ server, model, prefix = '/', config }) => {
|
|||||||
const include = parseInclude(request);
|
const include = parseInclude(request);
|
||||||
const where = parseWhere(request);
|
const where = parseWhere(request);
|
||||||
|
|
||||||
|
if (include instanceof Error) return void reply(include);
|
||||||
|
|
||||||
const list = await model.scope(request.params.scope).findAll({ include, where });
|
const list = await model.scope(request.params.scope).findAll({ include, where });
|
||||||
|
|
||||||
await Promise.all(list.map(instance => instance.destroy()));
|
await Promise.all(list.map(instance => instance.destroy()));
|
||||||
|
|
||||||
reply(list);
|
reply(list);
|
||||||
},
|
},
|
||||||
config: _.defaultsDeep({
|
config: _.defaultsDeep(config, {
|
||||||
validate: {
|
validate: {
|
||||||
params: joi.object().keys({
|
params: {
|
||||||
scope: joi.string().valid(...scopes),
|
scope: joi.string().valid(...scopes),
|
||||||
}),
|
},
|
||||||
},
|
},
|
||||||
}, config),
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -237,11 +303,7 @@ export const update = ({ server, model, prefix = '/', config }) => {
|
|||||||
@error
|
@error
|
||||||
async handler(request, reply) {
|
async handler(request, reply) {
|
||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
const instance = await model.findOne({
|
const instance = await model.findById(id);
|
||||||
where: {
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!instance) return void reply(notFound(`${id} not found.`));
|
if (!instance) return void reply(notFound(`${id} not found.`));
|
||||||
|
|
||||||
@ -250,11 +312,14 @@ export const update = ({ server, model, prefix = '/', config }) => {
|
|||||||
reply(instance);
|
reply(instance);
|
||||||
},
|
},
|
||||||
|
|
||||||
config: _.defaultsDeep({
|
config: _.defaultsDeep(config, {
|
||||||
validate: {
|
validate: {
|
||||||
payload: joi.object().required(),
|
payload: joi.object().required(),
|
||||||
|
params: {
|
||||||
|
id: joi.any(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}, config),
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
88
src/get-config-for-method.js
Normal file
88
src/get-config-for-method.js
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import { defaultsDeep } from 'lodash';
|
||||||
|
import joi from 'joi';
|
||||||
|
|
||||||
|
export const sequelizeOperators = {
|
||||||
|
$and: joi.any(),
|
||||||
|
$or: joi.any(),
|
||||||
|
$gt: joi.any(),
|
||||||
|
$gte: joi.any(),
|
||||||
|
$lt: joi.any(),
|
||||||
|
$lte: joi.any(),
|
||||||
|
$ne: joi.any(),
|
||||||
|
$eq: joi.any(),
|
||||||
|
$not: joi.any(),
|
||||||
|
$between: joi.any(),
|
||||||
|
$notBetween: joi.any(),
|
||||||
|
$in: joi.any(),
|
||||||
|
$notIn: joi.any(),
|
||||||
|
$like: joi.any(),
|
||||||
|
$notLike: joi.any(),
|
||||||
|
$iLike: joi.any(),
|
||||||
|
$notILike: joi.any(),
|
||||||
|
$overlap: joi.any(),
|
||||||
|
$contains: joi.any(),
|
||||||
|
$contained: joi.any(),
|
||||||
|
$any: joi.any(),
|
||||||
|
$col: joi.any(),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const whereMethods = [
|
||||||
|
'list',
|
||||||
|
'get',
|
||||||
|
'scope',
|
||||||
|
'destroy',
|
||||||
|
'destoryScope',
|
||||||
|
'destroyAll',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const includeMethods = [
|
||||||
|
'list',
|
||||||
|
'get',
|
||||||
|
'scope',
|
||||||
|
'destoryScope',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const payloadMethods = [
|
||||||
|
'create',
|
||||||
|
'update',
|
||||||
|
];
|
||||||
|
|
||||||
|
export default ({ method, attributeValidation, associationValidation, config = {} }) => {
|
||||||
|
const hasWhere = whereMethods.includes(method);
|
||||||
|
const hasInclude = includeMethods.includes(method);
|
||||||
|
const hasPayload = payloadMethods.includes(method);
|
||||||
|
const methodConfig = { ...config };
|
||||||
|
|
||||||
|
if (hasWhere) {
|
||||||
|
defaultsDeep(methodConfig, {
|
||||||
|
validate: {
|
||||||
|
query: {
|
||||||
|
...attributeValidation,
|
||||||
|
...sequelizeOperators,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasInclude) {
|
||||||
|
defaultsDeep(methodConfig, {
|
||||||
|
validate: {
|
||||||
|
query: {
|
||||||
|
...associationValidation,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasPayload) {
|
||||||
|
defaultsDeep(methodConfig, {
|
||||||
|
validate: {
|
||||||
|
payload: {
|
||||||
|
...attributeValidation,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return methodConfig;
|
||||||
|
};
|
117
src/get-config-for-method.test.js
Normal file
117
src/get-config-for-method.test.js
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import test from 'ava';
|
||||||
|
import joi from 'joi';
|
||||||
|
import
|
||||||
|
getConfigForMethod, {
|
||||||
|
whereMethods,
|
||||||
|
includeMethods,
|
||||||
|
payloadMethods,
|
||||||
|
sequelizeOperators,
|
||||||
|
} from './get-config-for-method.js';
|
||||||
|
|
||||||
|
test.beforeEach((t) => {
|
||||||
|
t.context.attributeValidation = {
|
||||||
|
myKey: joi.any(),
|
||||||
|
};
|
||||||
|
|
||||||
|
t.context.associationValidation = {
|
||||||
|
include: ['MyModel'],
|
||||||
|
};
|
||||||
|
|
||||||
|
t.context.config = {
|
||||||
|
cors: {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get-config-for-method validate.query seqeulizeOperators', (t) => {
|
||||||
|
whereMethods.forEach((method) => {
|
||||||
|
const configForMethod = getConfigForMethod({ method });
|
||||||
|
const { query } = configForMethod.validate;
|
||||||
|
const configForMethodValidateQueryKeys = Object.keys(query);
|
||||||
|
|
||||||
|
t.truthy(
|
||||||
|
query,
|
||||||
|
`applies query validation for ${method}`
|
||||||
|
);
|
||||||
|
|
||||||
|
Object.keys(sequelizeOperators).forEach((operator) => {
|
||||||
|
t.truthy(
|
||||||
|
configForMethodValidateQueryKeys.includes(operator),
|
||||||
|
`applies sequelize operator "${operator}" in validate.where for ${method}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get-config-for-method validate.query attributeValidation', (t) => {
|
||||||
|
const { attributeValidation } = t.context;
|
||||||
|
|
||||||
|
whereMethods.forEach((method) => {
|
||||||
|
const configForMethod = getConfigForMethod({ method, attributeValidation });
|
||||||
|
const { query } = configForMethod.validate;
|
||||||
|
|
||||||
|
Object.keys(attributeValidation).forEach((key) => {
|
||||||
|
t.truthy(
|
||||||
|
query[key]
|
||||||
|
, `applies attributeValidation (${key}) to validate.query`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get-config-for-method validate.query associationValidation', (t) => {
|
||||||
|
const { attributeValidation, associationValidation } = t.context;
|
||||||
|
|
||||||
|
includeMethods.forEach((method) => {
|
||||||
|
const configForMethod = getConfigForMethod({
|
||||||
|
method,
|
||||||
|
attributeValidation,
|
||||||
|
associationValidation,
|
||||||
|
});
|
||||||
|
const { query } = configForMethod.validate;
|
||||||
|
|
||||||
|
Object.keys(attributeValidation).forEach((key) => {
|
||||||
|
t.truthy(
|
||||||
|
query[key]
|
||||||
|
, `applies attributeValidation (${key}) to validate.query when include should be applied`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(associationValidation).forEach((key) => {
|
||||||
|
t.truthy(
|
||||||
|
query[key]
|
||||||
|
, `applies associationValidation (${key}) to validate.query when include should be applied`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get-config-for-method validate.payload associationValidation', (t) => {
|
||||||
|
const { attributeValidation } = t.context;
|
||||||
|
|
||||||
|
payloadMethods.forEach((method) => {
|
||||||
|
const configForMethod = getConfigForMethod({ method, attributeValidation });
|
||||||
|
const { payload } = configForMethod.validate;
|
||||||
|
|
||||||
|
Object.keys(attributeValidation).forEach((key) => {
|
||||||
|
t.truthy(
|
||||||
|
payload[key]
|
||||||
|
, `applies attributeValidation (${key}) to validate.payload`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get-config-for-method does not modify initial config on multiple passes', (t) => {
|
||||||
|
const { config } = t.context;
|
||||||
|
const originalConfig = { ...config };
|
||||||
|
|
||||||
|
whereMethods.forEach((method) => {
|
||||||
|
getConfigForMethod({ method, config });
|
||||||
|
});
|
||||||
|
|
||||||
|
t.deepEqual(
|
||||||
|
config
|
||||||
|
, originalConfig
|
||||||
|
, 'does not modify the original config object'
|
||||||
|
);
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user