Merge pull request #23 from mdibaiee/fix-joi-concat
Fix (validation) use joi's concat
This commit is contained in:
commit
469aaec66f
@ -10,8 +10,8 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint src",
|
"lint": "eslint src",
|
||||||
"test": "ava --require babel-register --source='*.test.js' --tap=${CI-false} | $(if [ -z ${CI:-} ]; then echo 'tail'; else tap-xunit > $CIRCLE_TEST_REPORTS/ava/ava.xml; fi;)",
|
"test": "ava --require babel-register --source='src/**/*.js' --source='!build/**/*' --tap=${CI-false} src/**/*.test.js | $(if [ -z ${CI:-} ]; then echo 'tail'; else tap-xunit > $CIRCLE_TEST_REPORTS/ava/ava.xml; fi;)",
|
||||||
"tdd": "ava --require babel-register --source='*.test.js' --watch",
|
"tdd": "ava --require babel-register --source='src/**/*.js' --source='!build/**/*' --watch src/**/*.test.js",
|
||||||
"build": "scripty",
|
"build": "scripty",
|
||||||
"watch": "scripty"
|
"watch": "scripty"
|
||||||
},
|
},
|
||||||
@ -31,6 +31,7 @@
|
|||||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.14.0",
|
"babel-plugin-transform-es2015-modules-commonjs": "^6.14.0",
|
||||||
"babel-preset-stage-1": "^6.13.0",
|
"babel-preset-stage-1": "^6.13.0",
|
||||||
|
"babel-register": "^6.14.0",
|
||||||
"eslint": "^3.4.0",
|
"eslint": "^3.4.0",
|
||||||
"eslint-config-pichak": "^1.1.2",
|
"eslint-config-pichak": "^1.1.2",
|
||||||
"eslint-plugin-ava": "^3.0.0",
|
"eslint-plugin-ava": "^3.0.0",
|
||||||
|
45
src/crud.js
45
src/crud.js
@ -14,6 +14,7 @@ const createAll = ({
|
|||||||
config,
|
config,
|
||||||
attributeValidation,
|
attributeValidation,
|
||||||
associationValidation,
|
associationValidation,
|
||||||
|
scopes,
|
||||||
}) => {
|
}) => {
|
||||||
Object.keys(methods).forEach((method) => {
|
Object.keys(methods).forEach((method) => {
|
||||||
methods[method]({
|
methods[method]({
|
||||||
@ -25,6 +26,7 @@ const createAll = ({
|
|||||||
attributeValidation,
|
attributeValidation,
|
||||||
associationValidation,
|
associationValidation,
|
||||||
config,
|
config,
|
||||||
|
scopes,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -64,6 +66,8 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
|
|||||||
include: joi.array().items(joi.string().valid(...modelAssociations)),
|
include: joi.array().items(joi.string().valid(...modelAssociations)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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
|
||||||
if (!permissions) {
|
if (!permissions) {
|
||||||
createAll({
|
createAll({
|
||||||
@ -73,6 +77,7 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
|
|||||||
config,
|
config,
|
||||||
attributeValidation,
|
attributeValidation,
|
||||||
associationValidation,
|
associationValidation,
|
||||||
|
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
|
||||||
} else if (!Array.isArray(permissions)) {
|
} else if (!Array.isArray(permissions)) {
|
||||||
@ -87,6 +92,7 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
|
|||||||
config,
|
config,
|
||||||
attributeValidation,
|
attributeValidation,
|
||||||
associationValidation,
|
associationValidation,
|
||||||
|
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
|
||||||
} else {
|
} else {
|
||||||
@ -108,6 +114,7 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
|
|||||||
method,
|
method,
|
||||||
attributeValidation,
|
attributeValidation,
|
||||||
associationValidation,
|
associationValidation,
|
||||||
|
scopes,
|
||||||
config: permissionConfig,
|
config: permissionConfig,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
@ -119,6 +126,7 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
|
|||||||
prefix,
|
prefix,
|
||||||
attributeValidation,
|
attributeValidation,
|
||||||
associationValidation,
|
associationValidation,
|
||||||
|
scopes,
|
||||||
config: permissionConfig,
|
config: permissionConfig,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -170,19 +178,11 @@ export const get = ({ server, model, prefix = '/', config }) => {
|
|||||||
|
|
||||||
reply(instance.toJSON());
|
reply(instance.toJSON());
|
||||||
},
|
},
|
||||||
config: _.defaultsDeep(config, {
|
config,
|
||||||
validate: {
|
|
||||||
params: {
|
|
||||||
id: joi.any(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const scope = ({ server, model, prefix = '/', config }) => {
|
export const scope = ({ server, model, prefix = '/', config }) => {
|
||||||
const scopes = Object.keys(model.options.scopes);
|
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: path.join(prefix, model._plural, '{scope}'),
|
path: path.join(prefix, model._plural, '{scope}'),
|
||||||
@ -198,13 +198,7 @@ export const scope = ({ server, model, prefix = '/', config }) => {
|
|||||||
|
|
||||||
reply(list.map((item) => item.toJSON()));
|
reply(list.map((item) => item.toJSON()));
|
||||||
},
|
},
|
||||||
config: _.defaultsDeep(config, {
|
config,
|
||||||
validate: {
|
|
||||||
params: {
|
|
||||||
scope: joi.string().valid(...scopes),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -268,8 +262,6 @@ export const destroyAll = ({ server, model, prefix = '/', config }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const destroyScope = ({ server, model, prefix = '/', config }) => {
|
export const destroyScope = ({ server, model, prefix = '/', config }) => {
|
||||||
const scopes = Object.keys(model.options.scopes);
|
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
path: path.join(prefix, model._plural, '{scope}'),
|
path: path.join(prefix, model._plural, '{scope}'),
|
||||||
@ -288,13 +280,7 @@ export const destroyScope = ({ server, model, prefix = '/', config }) => {
|
|||||||
const listAsJSON = list.map((item) => item.toJSON());
|
const listAsJSON = list.map((item) => item.toJSON());
|
||||||
reply(listAsJSON.length === 1 ? listAsJSON[0] : listAsJSON);
|
reply(listAsJSON.length === 1 ? listAsJSON[0] : listAsJSON);
|
||||||
},
|
},
|
||||||
config: _.defaultsDeep(config, {
|
config,
|
||||||
validate: {
|
|
||||||
params: {
|
|
||||||
scope: joi.string().valid(...scopes),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -315,14 +301,7 @@ export const update = ({ server, model, prefix = '/', config }) => {
|
|||||||
reply(instance.toJSON());
|
reply(instance.toJSON());
|
||||||
},
|
},
|
||||||
|
|
||||||
config: _.defaultsDeep(config, {
|
config,
|
||||||
validate: {
|
|
||||||
payload: joi.object().required(),
|
|
||||||
params: {
|
|
||||||
id: joi.any(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
import { defaultsDeep } from 'lodash';
|
import { set, get } from 'lodash';
|
||||||
import joi from 'joi';
|
import joi from 'joi';
|
||||||
|
|
||||||
|
// if the custom validation is a joi object we need to concat
|
||||||
|
// else, assume it's an plain object and we can just add it in with .keys
|
||||||
|
const concatToJoiObject = (joi, candidate) => {
|
||||||
|
if (!candidate) return joi;
|
||||||
|
else if (candidate.isJoi) return joi.concat(candidate);
|
||||||
|
else return joi.keys(candidate);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export const sequelizeOperators = {
|
export const sequelizeOperators = {
|
||||||
$and: joi.any(),
|
$and: joi.any(),
|
||||||
$or: joi.any(),
|
$or: joi.any(),
|
||||||
@ -47,41 +56,81 @@ export const payloadMethods = [
|
|||||||
'update',
|
'update',
|
||||||
];
|
];
|
||||||
|
|
||||||
export default ({ method, attributeValidation, associationValidation, config = {} }) => {
|
export const scopeParamsMethods = [
|
||||||
|
'destroyScope',
|
||||||
|
'scope',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const idParamsMethods = [
|
||||||
|
'get',
|
||||||
|
'update',
|
||||||
|
];
|
||||||
|
|
||||||
|
export default ({
|
||||||
|
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);
|
||||||
const hasPayload = payloadMethods.includes(method);
|
const hasPayload = payloadMethods.includes(method);
|
||||||
const methodConfig = { ...config };
|
const hasScopeParams = scopeParamsMethods.includes(method);
|
||||||
|
const hasIdParams = idParamsMethods.includes(method);
|
||||||
|
// clone the config so we don't modify it on multiple passes.
|
||||||
|
let methodConfig = { ...config, validate: { ...config.validate } };
|
||||||
|
|
||||||
if (hasWhere) {
|
if (hasWhere) {
|
||||||
defaultsDeep(methodConfig, {
|
const query = concatToJoiObject(joi.object()
|
||||||
validate: {
|
.keys({
|
||||||
query: {
|
|
||||||
...attributeValidation,
|
...attributeValidation,
|
||||||
...sequelizeOperators,
|
...sequelizeOperators,
|
||||||
},
|
}),
|
||||||
},
|
get(methodConfig, 'validate.query')
|
||||||
});
|
);
|
||||||
|
|
||||||
|
methodConfig = set(methodConfig, 'validate.query', query);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasInclude) {
|
if (hasInclude) {
|
||||||
defaultsDeep(methodConfig, {
|
const query = concatToJoiObject(joi.object()
|
||||||
validate: {
|
.keys({
|
||||||
query: {
|
|
||||||
...associationValidation,
|
...associationValidation,
|
||||||
},
|
}),
|
||||||
},
|
get(methodConfig, 'validate.query')
|
||||||
});
|
);
|
||||||
|
|
||||||
|
methodConfig = set(methodConfig, 'validate.query', query);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasPayload) {
|
if (hasPayload) {
|
||||||
defaultsDeep(methodConfig, {
|
const payload = concatToJoiObject(joi.object()
|
||||||
validate: {
|
.keys({
|
||||||
payload: {
|
|
||||||
...attributeValidation,
|
...attributeValidation,
|
||||||
},
|
}),
|
||||||
},
|
get(methodConfig, 'validate.payload')
|
||||||
});
|
);
|
||||||
|
|
||||||
|
methodConfig = set(methodConfig, 'validate.payload', payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasScopeParams) {
|
||||||
|
const params = concatToJoiObject(joi.object()
|
||||||
|
.keys({
|
||||||
|
scope: joi.string().valid(...scopes),
|
||||||
|
}),
|
||||||
|
get(methodConfig, 'validate.params')
|
||||||
|
);
|
||||||
|
|
||||||
|
methodConfig = set(methodConfig, 'validate.params', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasIdParams) {
|
||||||
|
const params = concatToJoiObject(joi.object()
|
||||||
|
.keys({
|
||||||
|
id: joi.any(),
|
||||||
|
}),
|
||||||
|
get(methodConfig, 'validate.params')
|
||||||
|
);
|
||||||
|
|
||||||
|
methodConfig = set(methodConfig, 'validate.params', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
return methodConfig;
|
return methodConfig;
|
||||||
|
@ -5,16 +5,22 @@ import
|
|||||||
whereMethods,
|
whereMethods,
|
||||||
includeMethods,
|
includeMethods,
|
||||||
payloadMethods,
|
payloadMethods,
|
||||||
|
scopeParamsMethods,
|
||||||
|
idParamsMethods,
|
||||||
sequelizeOperators,
|
sequelizeOperators,
|
||||||
} from './get-config-for-method.js';
|
} from './get-config-for-method.js';
|
||||||
|
|
||||||
test.beforeEach((t) => {
|
test.beforeEach((t) => {
|
||||||
|
t.context.models = ['MyModel'];
|
||||||
|
|
||||||
|
t.context.scopes = ['aScope'];
|
||||||
|
|
||||||
t.context.attributeValidation = {
|
t.context.attributeValidation = {
|
||||||
myKey: joi.any(),
|
myKey: joi.any(),
|
||||||
};
|
};
|
||||||
|
|
||||||
t.context.associationValidation = {
|
t.context.associationValidation = {
|
||||||
include: ['MyModel'],
|
include: joi.array().items(joi.string().valid(t.context.models)),
|
||||||
};
|
};
|
||||||
|
|
||||||
t.context.config = {
|
t.context.config = {
|
||||||
@ -22,11 +28,10 @@ test.beforeEach((t) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
test('get-config-for-method validate.query seqeulizeOperators', (t) => {
|
test('validate.query seqeulizeOperators', (t) => {
|
||||||
whereMethods.forEach((method) => {
|
whereMethods.forEach((method) => {
|
||||||
const configForMethod = getConfigForMethod({ method });
|
const configForMethod = getConfigForMethod({ method });
|
||||||
const { query } = configForMethod.validate;
|
const { query } = configForMethod.validate;
|
||||||
const configForMethodValidateQueryKeys = Object.keys(query);
|
|
||||||
|
|
||||||
t.truthy(
|
t.truthy(
|
||||||
query,
|
query,
|
||||||
@ -34,15 +39,20 @@ test('get-config-for-method validate.query seqeulizeOperators', (t) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Object.keys(sequelizeOperators).forEach((operator) => {
|
Object.keys(sequelizeOperators).forEach((operator) => {
|
||||||
|
t.ifError(
|
||||||
|
query.validate({ [operator]: true }).error
|
||||||
|
, `applies sequelize operator "${operator}" in validate.where for ${method}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
t.truthy(
|
t.truthy(
|
||||||
configForMethodValidateQueryKeys.includes(operator),
|
query.validate({ notAThing: true }).error
|
||||||
`applies sequelize operator "${operator}" in validate.where for ${method}`
|
, 'errors on a non-valid key'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
test('get-config-for-method validate.query attributeValidation', (t) => {
|
test('validate.query attributeValidation', (t) => {
|
||||||
const { attributeValidation } = t.context;
|
const { attributeValidation } = t.context;
|
||||||
|
|
||||||
whereMethods.forEach((method) => {
|
whereMethods.forEach((method) => {
|
||||||
@ -50,16 +60,96 @@ test('get-config-for-method validate.query attributeValidation', (t) => {
|
|||||||
const { query } = configForMethod.validate;
|
const { query } = configForMethod.validate;
|
||||||
|
|
||||||
Object.keys(attributeValidation).forEach((key) => {
|
Object.keys(attributeValidation).forEach((key) => {
|
||||||
t.truthy(
|
t.ifError(
|
||||||
query[key]
|
query.validate({ [key]: true }).error
|
||||||
, `applies attributeValidation (${key}) to validate.query`
|
, `applies attributeValidation (${key}) to validate.query`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.truthy(
|
||||||
|
query.validate({ notAThing: true }).error
|
||||||
|
, 'errors on a non-valid key'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('get-config-for-method validate.query associationValidation', (t) => {
|
test('query attributeValidation w/ config as plain object', (t) => {
|
||||||
const { attributeValidation, associationValidation } = t.context;
|
const { attributeValidation } = t.context;
|
||||||
|
const config = {
|
||||||
|
validate: {
|
||||||
|
query: {
|
||||||
|
aKey: joi.boolean(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
whereMethods.forEach((method) => {
|
||||||
|
const configForMethod = getConfigForMethod({
|
||||||
|
method,
|
||||||
|
attributeValidation,
|
||||||
|
config,
|
||||||
|
});
|
||||||
|
const { query } = configForMethod.validate;
|
||||||
|
|
||||||
|
const keys = [
|
||||||
|
...Object.keys(attributeValidation),
|
||||||
|
...Object.keys(config.validate.query),
|
||||||
|
];
|
||||||
|
|
||||||
|
keys.forEach((key) => {
|
||||||
|
t.ifError(
|
||||||
|
query.validate({ [key]: true }).error
|
||||||
|
, `applies ${key} to validate.query`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.truthy(
|
||||||
|
query.validate({ notAThing: true }).error
|
||||||
|
, 'errors on a non-valid key'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('query attributeValidation w/ config as joi object', (t) => {
|
||||||
|
const { attributeValidation } = t.context;
|
||||||
|
const queryKeys = {
|
||||||
|
aKey: joi.boolean(),
|
||||||
|
};
|
||||||
|
const config = {
|
||||||
|
validate: {
|
||||||
|
query: joi.object().keys(queryKeys),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
whereMethods.forEach((method) => {
|
||||||
|
const configForMethod = getConfigForMethod({
|
||||||
|
method,
|
||||||
|
attributeValidation,
|
||||||
|
config,
|
||||||
|
});
|
||||||
|
const { query } = configForMethod.validate;
|
||||||
|
|
||||||
|
const keys = [
|
||||||
|
...Object.keys(attributeValidation),
|
||||||
|
...Object.keys(queryKeys),
|
||||||
|
];
|
||||||
|
|
||||||
|
keys.forEach((key) => {
|
||||||
|
t.ifError(
|
||||||
|
query.validate({ [key]: true }).error
|
||||||
|
, `applies ${key} to validate.query`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.truthy(
|
||||||
|
query.validate({ notAThing: true }).error
|
||||||
|
, 'errors on a non-valid key'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('validate.query associationValidation', (t) => {
|
||||||
|
const { attributeValidation, associationValidation, models } = t.context;
|
||||||
|
|
||||||
includeMethods.forEach((method) => {
|
includeMethods.forEach((method) => {
|
||||||
const configForMethod = getConfigForMethod({
|
const configForMethod = getConfigForMethod({
|
||||||
@ -70,22 +160,106 @@ test('get-config-for-method validate.query associationValidation', (t) => {
|
|||||||
const { query } = configForMethod.validate;
|
const { query } = configForMethod.validate;
|
||||||
|
|
||||||
Object.keys(attributeValidation).forEach((key) => {
|
Object.keys(attributeValidation).forEach((key) => {
|
||||||
t.truthy(
|
t.ifError(
|
||||||
query[key]
|
query.validate({ [key]: true }).error
|
||||||
, `applies attributeValidation (${key}) to validate.query when include should be applied`
|
, `applies attributeValidation (${key}) to validate.query when include should be applied`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.keys(associationValidation).forEach((key) => {
|
Object.keys(associationValidation).forEach((key) => {
|
||||||
t.truthy(
|
t.ifError(
|
||||||
query[key]
|
query.validate({ [key]: models }).error
|
||||||
, `applies associationValidation (${key}) to validate.query when include should be applied`
|
, `applies associationValidation (${key}) to validate.query when include should be applied`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.truthy(
|
||||||
|
query.validate({ notAThing: true }).error
|
||||||
|
, 'errors on a non-valid key'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('get-config-for-method validate.payload associationValidation', (t) => {
|
test('query associationValidation w/ config as plain object', (t) => {
|
||||||
|
const { associationValidation, models } = t.context;
|
||||||
|
const config = {
|
||||||
|
validate: {
|
||||||
|
query: {
|
||||||
|
aKey: joi.boolean(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
includeMethods.forEach((method) => {
|
||||||
|
const configForMethod = getConfigForMethod({
|
||||||
|
method,
|
||||||
|
associationValidation,
|
||||||
|
config,
|
||||||
|
});
|
||||||
|
const { query } = configForMethod.validate;
|
||||||
|
|
||||||
|
Object.keys(associationValidation).forEach((key) => {
|
||||||
|
t.ifError(
|
||||||
|
query.validate({ [key]: models }).error
|
||||||
|
, `applies ${key} to validate.query`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(config.validate.query).forEach((key) => {
|
||||||
|
t.ifError(
|
||||||
|
query.validate({ [key]: true }).error
|
||||||
|
, `applies ${key} to validate.query`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.truthy(
|
||||||
|
query.validate({ notAThing: true }).error
|
||||||
|
, 'errors on a non-valid key'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('query associationValidation w/ config as joi object', (t) => {
|
||||||
|
const { associationValidation, models } = t.context;
|
||||||
|
const queryKeys = {
|
||||||
|
aKey: joi.boolean(),
|
||||||
|
};
|
||||||
|
const config = {
|
||||||
|
validate: {
|
||||||
|
query: joi.object().keys(queryKeys),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
includeMethods.forEach((method) => {
|
||||||
|
const configForMethod = getConfigForMethod({
|
||||||
|
method,
|
||||||
|
associationValidation,
|
||||||
|
config,
|
||||||
|
});
|
||||||
|
const { query } = configForMethod.validate;
|
||||||
|
|
||||||
|
Object.keys(associationValidation).forEach((key) => {
|
||||||
|
t.ifError(
|
||||||
|
query.validate({ [key]: models }).error
|
||||||
|
, `applies ${key} to validate.query`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(queryKeys).forEach((key) => {
|
||||||
|
t.ifError(
|
||||||
|
query.validate({ [key]: true }).error
|
||||||
|
, `applies ${key} to validate.query`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.truthy(
|
||||||
|
query.validate({ notAThing: true }).error
|
||||||
|
, 'errors on a non-valid key'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('validate.payload associationValidation', (t) => {
|
||||||
const { attributeValidation } = t.context;
|
const { attributeValidation } = t.context;
|
||||||
|
|
||||||
payloadMethods.forEach((method) => {
|
payloadMethods.forEach((method) => {
|
||||||
@ -93,20 +267,213 @@ test('get-config-for-method validate.payload associationValidation', (t) => {
|
|||||||
const { payload } = configForMethod.validate;
|
const { payload } = configForMethod.validate;
|
||||||
|
|
||||||
Object.keys(attributeValidation).forEach((key) => {
|
Object.keys(attributeValidation).forEach((key) => {
|
||||||
t.truthy(
|
t.ifError(
|
||||||
payload[key]
|
payload.validate({ [key]: true }).error
|
||||||
, `applies attributeValidation (${key}) to validate.payload`
|
, `applies attributeValidation (${key}) to validate.payload`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.truthy(
|
||||||
|
payload.validate({ notAThing: true }).error
|
||||||
|
, 'errors on a non-valid key'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('get-config-for-method does not modify initial config on multiple passes', (t) => {
|
test('payload attributeValidation w/ config as plain object', (t) => {
|
||||||
|
const { attributeValidation } = t.context;
|
||||||
|
const config = {
|
||||||
|
validate: {
|
||||||
|
payload: {
|
||||||
|
aKey: joi.boolean(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
payloadMethods.forEach((method) => {
|
||||||
|
const configForMethod = getConfigForMethod({
|
||||||
|
method,
|
||||||
|
attributeValidation,
|
||||||
|
config,
|
||||||
|
});
|
||||||
|
const { payload } = configForMethod.validate;
|
||||||
|
|
||||||
|
const keys = [
|
||||||
|
...Object.keys(attributeValidation),
|
||||||
|
...Object.keys(config.validate.payload),
|
||||||
|
];
|
||||||
|
|
||||||
|
keys.forEach((key) => {
|
||||||
|
t.ifError(
|
||||||
|
payload.validate({ [key]: true }).error
|
||||||
|
, `applies ${key} to validate.payload`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.truthy(
|
||||||
|
payload.validate({ notAThing: true }).error
|
||||||
|
, 'errors on a non-valid key'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('payload attributeValidation w/ config as joi object', (t) => {
|
||||||
|
const { attributeValidation } = t.context;
|
||||||
|
const payloadKeys = {
|
||||||
|
aKey: joi.boolean(),
|
||||||
|
};
|
||||||
|
const config = {
|
||||||
|
validate: {
|
||||||
|
payload: joi.object().keys(payloadKeys),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
payloadMethods.forEach((method) => {
|
||||||
|
const configForMethod = getConfigForMethod({
|
||||||
|
method,
|
||||||
|
attributeValidation,
|
||||||
|
config,
|
||||||
|
});
|
||||||
|
const { payload } = configForMethod.validate;
|
||||||
|
|
||||||
|
const keys = [
|
||||||
|
...Object.keys(attributeValidation),
|
||||||
|
...Object.keys(payloadKeys),
|
||||||
|
];
|
||||||
|
|
||||||
|
keys.forEach((key) => {
|
||||||
|
t.ifError(
|
||||||
|
payload.validate({ [key]: true }).error
|
||||||
|
, `applies ${key} to validate.payload`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.truthy(
|
||||||
|
payload.validate({ notAThing: true }).error
|
||||||
|
, 'errors on a non-valid key'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('validate.params scopeParamsMethods', (t) => {
|
||||||
|
const { scopes } = t.context;
|
||||||
|
|
||||||
|
scopeParamsMethods.forEach((method) => {
|
||||||
|
const configForMethod = getConfigForMethod({ method, scopes });
|
||||||
|
const { params } = configForMethod.validate;
|
||||||
|
|
||||||
|
scopes.forEach((key) => {
|
||||||
|
t.ifError(
|
||||||
|
params.validate({ scope: key }).error
|
||||||
|
, `applies "scope: ${key}" to validate.params`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.truthy(
|
||||||
|
params.validate({ scope: 'notAthing' }).error
|
||||||
|
, 'errors on a non-valid key'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('params scopeParamsMethods w/ config as plain object', (t) => {
|
||||||
|
const { scopes } = t.context;
|
||||||
|
const config = {
|
||||||
|
validate: {
|
||||||
|
params: {
|
||||||
|
aKey: joi.boolean(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
scopeParamsMethods.forEach((method) => {
|
||||||
|
const configForMethod = getConfigForMethod({
|
||||||
|
method,
|
||||||
|
scopes,
|
||||||
|
config,
|
||||||
|
});
|
||||||
|
const { params } = configForMethod.validate;
|
||||||
|
|
||||||
|
scopes.forEach((key) => {
|
||||||
|
t.ifError(
|
||||||
|
params.validate({ scope: key }).error
|
||||||
|
, `applies "scope: ${key}" to validate.params`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(config.validate.params).forEach((key) => {
|
||||||
|
t.ifError(
|
||||||
|
params.validate({ [key]: true }).error
|
||||||
|
, `applies ${key} to validate.params`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.truthy(
|
||||||
|
params.validate({ notAThing: true }).error
|
||||||
|
, 'errors on a non-valid key'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('params scopeParamsMethods w/ config as joi object', (t) => {
|
||||||
|
const { scopes } = t.context;
|
||||||
|
const paramsKeys = {
|
||||||
|
aKey: joi.boolean(),
|
||||||
|
};
|
||||||
|
const config = {
|
||||||
|
validate: {
|
||||||
|
params: joi.object().keys(paramsKeys),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
scopeParamsMethods.forEach((method) => {
|
||||||
|
const configForMethod = getConfigForMethod({
|
||||||
|
method,
|
||||||
|
scopes,
|
||||||
|
config,
|
||||||
|
});
|
||||||
|
const { params } = configForMethod.validate;
|
||||||
|
|
||||||
|
scopes.forEach((key) => {
|
||||||
|
t.ifError(
|
||||||
|
params.validate({ scope: key }).error
|
||||||
|
, `applies "scope: ${key}" to validate.params`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(paramsKeys).forEach((key) => {
|
||||||
|
t.ifError(
|
||||||
|
params.validate({ [key]: true }).error
|
||||||
|
, `applies ${key} to validate.params`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
t.truthy(
|
||||||
|
params.validate({ notAThing: true }).error
|
||||||
|
, 'errors on a non-valid key'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('validate.payload idParamsMethods', (t) => {
|
||||||
|
idParamsMethods.forEach((method) => {
|
||||||
|
const configForMethod = getConfigForMethod({ method });
|
||||||
|
const { params } = configForMethod.validate;
|
||||||
|
|
||||||
|
t.ifError(
|
||||||
|
params.validate({ id: 'aThing' }).error
|
||||||
|
, 'applies id to validate.params'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not modify initial config on multiple passes', (t) => {
|
||||||
const { config } = t.context;
|
const { config } = t.context;
|
||||||
const originalConfig = { ...config };
|
const originalConfig = { ...config };
|
||||||
|
|
||||||
whereMethods.forEach((method) => {
|
whereMethods.forEach((method) => {
|
||||||
getConfigForMethod({ method, config });
|
getConfigForMethod({ method, ...t.context });
|
||||||
});
|
});
|
||||||
|
|
||||||
t.deepEqual(
|
t.deepEqual(
|
||||||
|
Loading…
Reference in New Issue
Block a user