import process from "process"; import * as Hapi from "@hapi/hapi"; import * as Joi from "joi"; import Hoek from "@hapi/hoek"; import * as Boom from "@hapi/boom"; export interface HapiValidationError extends Joi.ValidationError { output: { statusCode: number; headers: Hapi.Utils.Dictionary; payload: { statusCode: number; error: string; message?: string; validation: { source: string; keys: string[]; }; }; }; } export function defaultValidationErrorHandler( request: Hapi.Request, h: Hapi.ResponseToolkit, err?: Error ): Hapi.Lifecycle.ReturnValue { // Newer versions of Joi don't format the key for missing params the same way. This shim // provides backwards compatibility. Unfortunately, Joi doesn't export it's own Error class // in JS so we have to rely on the `name` key before we can cast it. // // The Hapi code we're 'overwriting' can be found here: // https://github.com/hapijs/hapi/blob/master/lib/validation.js#L102 if (err && err.name === "ValidationError" && err.hasOwnProperty("output")) { const validationError: HapiValidationError = err as HapiValidationError; const validationKeys: string[] = []; validationError.details.forEach((detail) => { if (detail.path.length > 0) { validationKeys.push(Hoek.escapeHtml(detail.path.join("."))); } else { // If no path, use the value sigil to signal the entire value had an issue. validationKeys.push("value"); } }); validationError.output.payload.validation.keys = validationKeys; } throw err; } export const validatingFailAction = async ( request: Hapi.Request, h: Hapi.ResponseToolkit, err: Error ): Promise => { if (process.env.NODE_ENV === "production") { throw Boom.badRequest("Invalid request payload input"); } else { defaultValidationErrorHandler(request, h, err); } };