UNPKG

@naturalcycles/nodejs-lib

Version:
140 lines (139 loc) 4.95 kB
"use strict"; /* * Does 2 things: * 1. Validates the value according to Schema passed. * 2. Converts the value (also according to Schema). * * "Converts" mean e.g trims all strings from leading/trailing spaces. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.convert = exports.undefinedIfInvalid = exports.isValid = exports.getValidationResult = exports.validate = void 0; const js_lib_1 = require("@naturalcycles/js-lib"); const joi_validation_error_1 = require("./joi.validation.error"); // Strip colors in production (for e.g Sentry reporting) // const stripColors = process.env.NODE_ENV === 'production' || !!process.env.GAE_INSTANCE // Currently colors do more bad than good, so let's strip them always for now const stripColors = true; const defaultOptions = { abortEarly: false, convert: true, allowUnknown: true, stripUnknown: { objects: true, // true: it will SILENTLY strip invalid values from arrays. Very dangerous! Can lead to data loss! // false: it will THROW validation error if any of array items is invalid // Q: is it invalid if it has unknown properties? // A: no, unknown properties are just stripped (in both 'false' and 'true' states), array is still valid // Q: will it strip or keep unknown properties in array items?.. // A: strip arrays: false, // let's be very careful with that! https://github.com/hapijs/joi/issues/658 }, presence: 'required', // errors: { // stack: true, // } }; /** * Validates with Joi. * Throws JoiValidationError if invalid. * Returns *converted* value. * * If `schema` is undefined - returns value as is. */ function validate(value, schema, objectName, options = {}) { const { value: returnValue, error } = getValidationResult(value, schema, objectName, options); if (error) { throw error; } return returnValue; } exports.validate = validate; /** * Validates with Joi. * Returns JoiValidationResult with converted value and error (if any). * Does not throw. * * If `schema` is undefined - returns value as is. */ function getValidationResult(value, schema, objectName, options = {}) { if (!schema) return { value }; const { value: returnValue, error } = schema.validate(value, { ...defaultOptions, ...options, }); const vr = { value: returnValue, }; if (error) { vr.error = createError(value, error, objectName); } return vr; } exports.getValidationResult = getValidationResult; /** * Convenience function that returns true if !error. */ function isValid(value, schema) { if (!schema) return { value }; const { error } = schema.validate(value, defaultOptions); return !error; } exports.isValid = isValid; function undefinedIfInvalid(value, schema) { if (!schema) return { value }; const { value: returnValue, error } = schema.validate(value, defaultOptions); return error ? undefined : returnValue; } exports.undefinedIfInvalid = undefinedIfInvalid; /** * Will do joi-convertation, regardless of error/validity of value. * * @returns converted value */ function convert(value, schema) { if (!schema) return value; const { value: returnValue } = schema.validate(value, defaultOptions); return returnValue; } exports.convert = convert; function createError(value, err, objectName) { if (!err) return undefined; const tokens = []; const objectId = (0, js_lib_1._isObject)(value) ? value['id'] : undefined; if (objectId || objectName) { objectName = objectName || value?.constructor?.name; tokens.push('Invalid ' + [objectName, objectId].filter(Boolean).join('.')); } const annotation = err.annotate(stripColors); if (annotation.length > 4000) { // Annotation message is too big and will be replaced by stringified `error.details` instead tokens.push((0, js_lib_1._truncateMiddle)(annotation, 4000, `\n... ${(0, js_lib_1._hb)(annotation.length)} message truncated ...\n`)); // Up to 5 `details` tokens.push(...err.details.slice(0, 5).map(i => `${i.message} @ .${i.path.join('.')}`)); if (err.details.length > 5) tokens.push(`... ${err.details.length} errors`); } else { tokens.push(annotation); } const msg = tokens.join('\n'); const data = { joiValidationErrorItems: err.details, ...(objectName && { joiValidationObjectName: objectName }), ...(objectId && { joiValidationObjectId: objectId }), }; // Make annotation non-enumerable, to not get it automatically printed, // but still accessible Object.defineProperty(data, 'annotation', { writable: true, configurable: true, enumerable: false, value: annotation, }); return new joi_validation_error_1.JoiValidationError(msg, data); }