UNPKG

4.95 kBJavaScriptView Raw
1"use strict";
2/*
3 * Does 2 things:
4 * 1. Validates the value according to Schema passed.
5 * 2. Converts the value (also according to Schema).
6 *
7 * "Converts" mean e.g trims all strings from leading/trailing spaces.
8 */
9Object.defineProperty(exports, "__esModule", { value: true });
10exports.convert = exports.undefinedIfInvalid = exports.isValid = exports.getValidationResult = exports.validate = void 0;
11const js_lib_1 = require("@naturalcycles/js-lib");
12const joi_validation_error_1 = require("./joi.validation.error");
13// Strip colors in production (for e.g Sentry reporting)
14// const stripColors = process.env.NODE_ENV === 'production' || !!process.env.GAE_INSTANCE
15// Currently colors do more bad than good, so let's strip them always for now
16const stripColors = true;
17const defaultOptions = {
18 abortEarly: false,
19 convert: true,
20 allowUnknown: true,
21 stripUnknown: {
22 objects: true,
23 // true: it will SILENTLY strip invalid values from arrays. Very dangerous! Can lead to data loss!
24 // false: it will THROW validation error if any of array items is invalid
25 // Q: is it invalid if it has unknown properties?
26 // A: no, unknown properties are just stripped (in both 'false' and 'true' states), array is still valid
27 // Q: will it strip or keep unknown properties in array items?..
28 // A: strip
29 arrays: false, // let's be very careful with that! https://github.com/hapijs/joi/issues/658
30 },
31 presence: 'required',
32 // errors: {
33 // stack: true,
34 // }
35};
36/**
37 * Validates with Joi.
38 * Throws JoiValidationError if invalid.
39 * Returns *converted* value.
40 *
41 * If `schema` is undefined - returns value as is.
42 */
43function validate(value, schema, objectName, options = {}) {
44 const { value: returnValue, error } = getValidationResult(value, schema, objectName, options);
45 if (error) {
46 throw error;
47 }
48 return returnValue;
49}
50exports.validate = validate;
51/**
52 * Validates with Joi.
53 * Returns JoiValidationResult with converted value and error (if any).
54 * Does not throw.
55 *
56 * If `schema` is undefined - returns value as is.
57 */
58function getValidationResult(value, schema, objectName, options = {}) {
59 if (!schema)
60 return { value };
61 const { value: returnValue, error } = schema.validate(value, {
62 ...defaultOptions,
63 ...options,
64 });
65 const vr = {
66 value: returnValue,
67 };
68 if (error) {
69 vr.error = createError(value, error, objectName);
70 }
71 return vr;
72}
73exports.getValidationResult = getValidationResult;
74/**
75 * Convenience function that returns true if !error.
76 */
77function isValid(value, schema) {
78 if (!schema)
79 return { value };
80 const { error } = schema.validate(value, defaultOptions);
81 return !error;
82}
83exports.isValid = isValid;
84function undefinedIfInvalid(value, schema) {
85 if (!schema)
86 return { value };
87 const { value: returnValue, error } = schema.validate(value, defaultOptions);
88 return error ? undefined : returnValue;
89}
90exports.undefinedIfInvalid = undefinedIfInvalid;
91/**
92 * Will do joi-convertation, regardless of error/validity of value.
93 *
94 * @returns converted value
95 */
96function convert(value, schema) {
97 if (!schema)
98 return value;
99 const { value: returnValue } = schema.validate(value, defaultOptions);
100 return returnValue;
101}
102exports.convert = convert;
103function createError(value, err, objectName) {
104 if (!err)
105 return undefined;
106 const tokens = [];
107 const objectId = (0, js_lib_1._isObject)(value) ? value['id'] : undefined;
108 if (objectId || objectName) {
109 objectName = objectName || value?.constructor?.name;
110 tokens.push('Invalid ' + [objectName, objectId].filter(Boolean).join('.'));
111 }
112 const annotation = err.annotate(stripColors);
113 if (annotation.length > 4000) {
114 // Annotation message is too big and will be replaced by stringified `error.details` instead
115 tokens.push((0, js_lib_1._truncateMiddle)(annotation, 4000, `\n... ${(0, js_lib_1._hb)(annotation.length)} message truncated ...\n`));
116 // Up to 5 `details`
117 tokens.push(...err.details.slice(0, 5).map(i => `${i.message} @ .${i.path.join('.')}`));
118 if (err.details.length > 5)
119 tokens.push(`... ${err.details.length} errors`);
120 }
121 else {
122 tokens.push(annotation);
123 }
124 const msg = tokens.join('\n');
125 const data = {
126 joiValidationErrorItems: err.details,
127 ...(objectName && { joiValidationObjectName: objectName }),
128 ...(objectId && { joiValidationObjectId: objectId }),
129 };
130 // Make annotation non-enumerable, to not get it automatically printed,
131 // but still accessible
132 Object.defineProperty(data, 'annotation', {
133 writable: true,
134 configurable: true,
135 enumerable: false,
136 value: annotation,
137 });
138 return new joi_validation_error_1.JoiValidationError(msg, data);
139}