Fix (validation) use joi's concat

It turns out defaultsDeep doesn't ever correctly combine Joi objects.
So, the only option is to use Joi's concat method to combine Joi
schemas. This complicates `getConfigForMethod`, but simplifies actual
route creation.

I ran into this because I'm setting up [lout](https://github.com/hapijs/lout)
on a server, and it requires properly formatted Joi schemas. This leads
me to believe there was something already wrong and Lout just exposed
the problem.
This commit is contained in:
Joey Baker
2016-09-07 18:09:48 -07:00
parent 85111c7dc8
commit c59943a717
3 changed files with 473 additions and 78 deletions

View File

@ -1,6 +1,15 @@
import { defaultsDeep } from 'lodash';
import { set, get } from 'lodash';
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 = {
$and: joi.any(),
$or: joi.any(),
@ -47,41 +56,81 @@ export const payloadMethods = [
'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 hasInclude = includeMethods.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) {
defaultsDeep(methodConfig, {
validate: {
query: {
...attributeValidation,
...sequelizeOperators,
},
},
});
const query = concatToJoiObject(joi.object()
.keys({
...attributeValidation,
...sequelizeOperators,
}),
get(methodConfig, 'validate.query')
);
methodConfig = set(methodConfig, 'validate.query', query);
}
if (hasInclude) {
defaultsDeep(methodConfig, {
validate: {
query: {
...associationValidation,
},
},
});
const query = concatToJoiObject(joi.object()
.keys({
...associationValidation,
}),
get(methodConfig, 'validate.query')
);
methodConfig = set(methodConfig, 'validate.query', query);
}
if (hasPayload) {
defaultsDeep(methodConfig, {
validate: {
payload: {
...attributeValidation,
},
},
});
const payload = concatToJoiObject(joi.object()
.keys({
...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;