From 7eece2bb00d8dc45c0e5e573a4f8cec57a96df68 Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Fri, 14 Feb 2020 11:42:03 +0100 Subject: [PATCH] Init entity serivce validators and refactor boom middleware to render errors Signed-off-by: Alexandre Bodin --- packages/strapi-utils/lib/validators.js | 5 ++ packages/strapi/lib/middlewares/boom/index.js | 52 ++++++++++--------- .../strapi/lib/services/entity-service.js | 42 +++++++++++++++ 3 files changed, 74 insertions(+), 25 deletions(-) diff --git a/packages/strapi-utils/lib/validators.js b/packages/strapi-utils/lib/validators.js index 7d0ebe198d..1cd03bd313 100644 --- a/packages/strapi-utils/lib/validators.js +++ b/packages/strapi-utils/lib/validators.js @@ -1,6 +1,11 @@ 'use strict'; 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 diff --git a/packages/strapi/lib/middlewares/boom/index.js b/packages/strapi/lib/middlewares/boom/index.js index f88446966d..f9c6e8b417 100644 --- a/packages/strapi/lib/middlewares/boom/index.js +++ b/packages/strapi/lib/middlewares/boom/index.js @@ -42,6 +42,22 @@ const boomMethods = [ '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 => { return { /** @@ -68,35 +84,19 @@ module.exports = strapi => { // Log error. strapi.log.error(error); - // if the error is a boom error (e.g throw strapi.errors.badRequest) - if (error.isBoom) { - ctx.status = error.output.statusCode; - 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 }); - } + const { status, body } = formatBoomPayload(error); + ctx.body = body; + ctx.status = status; } + }); - if (ctx.response.headers.location) { - return; - } + strapi.app.use(async (ctx, next) => { + await next(); // Empty body is considered as `notFound` response. if (!ctx.body && ctx.body !== 0) { 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() { boomMethods.forEach(method => { 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; - this.body = error; + const { status, body } = formatBoomPayload(boomError); + + this.body = body; + this.status = status; }; this.delegator.method(method); diff --git a/packages/strapi/lib/services/entity-service.js b/packages/strapi/lib/services/entity-service.js index 38abe08da1..abc450abde 100644 --- a/packages/strapi/lib/services/entity-service.js +++ b/packages/strapi/lib/services/entity-service.js @@ -2,6 +2,39 @@ const _ = require('lodash'); 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 }) => ({ /** @@ -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); if (files) {