Init entity serivce validators and refactor boom middleware to render errors

Signed-off-by: Alexandre Bodin <bodin.alex@gmail.com>
This commit is contained in:
Alexandre Bodin 2020-02-14 11:42:03 +01:00
parent 37b184d063
commit 7eece2bb00
3 changed files with 74 additions and 25 deletions

View File

@ -1,6 +1,11 @@
'use strict'; 'use strict';
const yup = require('yup'); const yup = require('yup');
const _ = require('lodash');
yup.addMethod(yup.mixed, 'defined', function(msg = '${path} must be defined') {
return this.test('defined', msg, value => !_.isNil(value));
});
/** /**
* Returns a formatted error for http responses * Returns a formatted error for http responses

View File

@ -42,6 +42,22 @@ const boomMethods = [
'gatewayTimeout', 'gatewayTimeout',
]; ];
const formatBoomPayload = boomError => {
if (!Boom.isBoom(boomError)) {
boomError = Boom.boomify(boomError, {
statusCode: boomError.status || 500,
});
}
const { output } = boomError;
if (output.statusCode < 500 && !_.isNil(boomError.data)) {
output.payload.data = boomError.data;
}
return { status: output.statusCode, body: output.payload };
};
module.exports = strapi => { module.exports = strapi => {
return { return {
/** /**
@ -68,35 +84,19 @@ module.exports = strapi => {
// Log error. // Log error.
strapi.log.error(error); strapi.log.error(error);
// if the error is a boom error (e.g throw strapi.errors.badRequest) const { status, body } = formatBoomPayload(error);
if (error.isBoom) { ctx.body = body;
ctx.status = error.output.statusCode; ctx.status = status;
ctx.body = error.output.payload;
} else {
// Wrap error into a Boom's response.
ctx.status = error.status || 500;
ctx.body = _.get(ctx.body, 'isBoom')
? ctx.body || (error && error.message)
: Boom.boomify(error, { statusCode: ctx.status });
}
} }
});
if (ctx.response.headers.location) { strapi.app.use(async (ctx, next) => {
return; await next();
}
// Empty body is considered as `notFound` response. // Empty body is considered as `notFound` response.
if (!ctx.body && ctx.body !== 0) { if (!ctx.body && ctx.body !== 0) {
ctx.notFound(); ctx.notFound();
} }
if (ctx.body.isBoom && ctx.body.data) {
ctx.body.output.payload.message = ctx.body.data;
}
// Format `ctx.body` and `ctx.status`.
ctx.status = ctx.body.isBoom ? ctx.body.output.statusCode : ctx.status;
ctx.body = ctx.body.isBoom ? ctx.body.output.payload : ctx.body;
}); });
}, },
@ -104,10 +104,12 @@ module.exports = strapi => {
createResponses() { createResponses() {
boomMethods.forEach(method => { boomMethods.forEach(method => {
strapi.app.response[method] = function(...rest) { strapi.app.response[method] = function(...rest) {
const error = Boom[method](...rest) || {}; const boomError = Boom[method](...rest) || {};
this.status = error.isBoom ? error.output.statusCode : this.status; const { status, body } = formatBoomPayload(boomError);
this.body = error;
this.body = body;
this.status = status;
}; };
this.delegator.method(method); this.delegator.method(method);

View File

@ -2,6 +2,39 @@
const _ = require('lodash'); const _ = require('lodash');
const uploadFiles = require('./utils/upload-files'); const uploadFiles = require('./utils/upload-files');
const { yup, formatYupErrors } = require('strapi-utils');
const createAttributeValidator = attr => {
switch (attr.type) {
case 'string': {
const { minLength, maxLength } = attr;
return yup
.string()
.nullable()
.min(minLength)
.max(maxLength);
}
default:
return yup.mixed();
}
};
const createValidator = model => {
return yup
.object(
_.mapValues(model.attributes, attr => {
const { required } = attr;
const validator = createAttributeValidator(attr);
if (required) {
return validator.defined();
}
return validator;
})
)
.required();
};
module.exports = ({ db, eventHub }) => ({ module.exports = ({ db, eventHub }) => ({
/** /**
@ -62,6 +95,15 @@ module.exports = ({ db, eventHub }) => ({
} }
} }
try {
await createValidator(db.getModel(model)).validate(data, {
strict: true,
abortEarly: false,
});
} catch (err) {
throw strapi.errors.badRequest('Validation error', formatYupErrors(err));
}
let entry = await db.query(model).create(data); let entry = await db.query(model).create(data);
if (files) { if (files) {