source: frontend/node_modules/schema-utils/dist/ValidationError.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 36.9 KB
RevLine 
[9af201e]1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = void 0;
7var _memorize = _interopRequireDefault(require("./util/memorize"));
8function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9/** @typedef {import("json-schema").JSONSchema6} JSONSchema6 */
10/** @typedef {import("json-schema").JSONSchema7} JSONSchema7 */
11
12/** @typedef {import("./validate").Schema} Schema */
13/** @typedef {import("./validate").ValidationErrorConfiguration} ValidationErrorConfiguration */
14/** @typedef {import("./validate").PostFormatter} PostFormatter */
15/** @typedef {import("./validate").SchemaUtilErrorObject} SchemaUtilErrorObject */
16
17/** @enum {number} */
18const SPECIFICITY = {
19 type: 1,
20 not: 1,
21 oneOf: 1,
22 anyOf: 1,
23 if: 1,
24 enum: 1,
25 const: 1,
26 instanceof: 1,
27 required: 2,
28 pattern: 2,
29 patternRequired: 2,
30 format: 2,
31 formatMinimum: 2,
32 formatMaximum: 2,
33 minimum: 2,
34 exclusiveMinimum: 2,
35 maximum: 2,
36 exclusiveMaximum: 2,
37 multipleOf: 2,
38 uniqueItems: 2,
39 contains: 2,
40 minLength: 2,
41 maxLength: 2,
42 minItems: 2,
43 maxItems: 2,
44 minProperties: 2,
45 maxProperties: 2,
46 dependencies: 2,
47 propertyNames: 2,
48 additionalItems: 2,
49 additionalProperties: 2,
50 absolutePath: 2
51};
52
53/**
54 * @param {string} value value
55 * @returns {value is number} true when is number, otherwise false
56 */
57function isNumeric(value) {
58 return /^-?\d+$/.test(value);
59}
60
61/**
62 * @param {Array<SchemaUtilErrorObject>} array array of error objects
63 * @param {(item: SchemaUtilErrorObject) => number} fn function
64 * @returns {Array<SchemaUtilErrorObject>} filtered max
65 */
66function filterMax(array, fn) {
67 const evaluatedMax = array.reduce((max, item) => Math.max(max, fn(item)), 0);
68 return array.filter(item => fn(item) === evaluatedMax);
69}
70
71/**
72 * @param {Array<SchemaUtilErrorObject>} children children
73 * @returns {Array<SchemaUtilErrorObject>} filtered children
74 */
75function filterChildren(children) {
76 let newChildren = children;
77 newChildren = filterMax(newChildren,
78 /**
79 * @param {SchemaUtilErrorObject} error error object
80 * @returns {number} result
81 */
82 error => error.instancePath ? error.instancePath.length : 0);
83 newChildren = filterMax(newChildren,
84 /**
85 * @param {SchemaUtilErrorObject} error error object
86 * @returns {number} result
87 */
88 error => SPECIFICITY[(/** @type {keyof typeof SPECIFICITY} */error.keyword)] || 2);
89 return newChildren;
90}
91
92/**
93 * Extracts all refs from schema
94 * @param {SchemaUtilErrorObject} error error object
95 * @returns {Array<string>} extracted refs
96 */
97function extractRefs(error) {
98 const {
99 schema
100 } = error;
101 if (!Array.isArray(schema)) {
102 return [];
103 }
104 return schema.map(({
105 $ref
106 }) => $ref).filter(Boolean);
107}
108
109/**
110 * Find all children errors
111 * @param {Array<SchemaUtilErrorObject>} children children
112 * @param {Array<string>} schemaPaths schema paths
113 * @returns {number} returns index of first child
114 */
115function findAllChildren(children, schemaPaths) {
116 let i = children.length - 1;
117 const predicate =
118 /**
119 * @param {string} schemaPath schema path
120 * @returns {boolean} predicate
121 */
122 schemaPath => children[i].schemaPath.indexOf(schemaPath) !== 0;
123 while (i > -1 && !schemaPaths.every(predicate)) {
124 if (children[i].keyword === "anyOf" || children[i].keyword === "oneOf") {
125 const refs = extractRefs(children[i]);
126 const childrenStart = findAllChildren(children.slice(0, i), [...refs, children[i].schemaPath]);
127 i = childrenStart - 1;
128 } else {
129 i -= 1;
130 }
131 }
132 return i + 1;
133}
134
135/**
136 * Groups children by their first level parent (assuming that error is root)
137 * @param {Array<SchemaUtilErrorObject>} children children
138 * @returns {Array<SchemaUtilErrorObject>} grouped children
139 */
140function groupChildrenByFirstChild(children) {
141 const result = [];
142 let i = children.length - 1;
143 while (i > 0) {
144 const child = children[i];
145 if (child.keyword === "anyOf" || child.keyword === "oneOf") {
146 const refs = extractRefs(child);
147 const childrenStart = findAllChildren(children.slice(0, i), [...refs, child.schemaPath]);
148 if (childrenStart !== i) {
149 result.push({
150 ...child,
151 children: children.slice(childrenStart, i)
152 });
153 i = childrenStart;
154 } else {
155 result.push(child);
156 }
157 } else {
158 result.push(child);
159 }
160 i -= 1;
161 }
162 if (i === 0) {
163 result.push(children[i]);
164 }
165 return result.reverse();
166}
167
168/**
169 * @param {string} str string
170 * @param {string} prefix prefix
171 * @returns {string} string with indent and prefix
172 */
173function indent(str, prefix) {
174 return str.replace(/\n(?!$)/g, `\n${prefix}`);
175}
176
177/**
178 * @param {Schema} schema schema
179 * @returns {schema is (Schema & {not: Schema})} true when `not` in schema, otherwise false
180 */
181function hasNotInSchema(schema) {
182 return Boolean(schema.not);
183}
184
185/**
186 * @param {Schema} schema schema
187 * @returns {Schema} first typed schema
188 */
189function findFirstTypedSchema(schema) {
190 if (hasNotInSchema(schema)) {
191 return findFirstTypedSchema(schema.not);
192 }
193 return schema;
194}
195
196/**
197 * @param {Schema} schema schema
198 * @returns {boolean} true when schema type is number, otherwise false
199 */
200function likeNumber(schema) {
201 return schema.type === "number" || typeof schema.minimum !== "undefined" || typeof schema.exclusiveMinimum !== "undefined" || typeof schema.maximum !== "undefined" || typeof schema.exclusiveMaximum !== "undefined" || typeof schema.multipleOf !== "undefined";
202}
203
204/**
205 * @param {Schema} schema schema
206 * @returns {boolean} true when schema type is integer, otherwise false
207 */
208function likeInteger(schema) {
209 return schema.type === "integer" || typeof schema.minimum !== "undefined" || typeof schema.exclusiveMinimum !== "undefined" || typeof schema.maximum !== "undefined" || typeof schema.exclusiveMaximum !== "undefined" || typeof schema.multipleOf !== "undefined";
210}
211
212/**
213 * @param {Schema} schema schema
214 * @returns {boolean} true when schema type is string, otherwise false
215 */
216function likeString(schema) {
217 return schema.type === "string" || typeof schema.minLength !== "undefined" || typeof schema.maxLength !== "undefined" || typeof schema.pattern !== "undefined" || typeof schema.format !== "undefined" || typeof schema.formatMinimum !== "undefined" || typeof schema.formatMaximum !== "undefined";
218}
219
220/**
221 * @param {Schema} schema schema
222 * @returns {boolean} true when null, otherwise false
223 */
224function likeNull(schema) {
225 return schema.type === "null";
226}
227
228/**
229 * @param {Schema} schema schema
230 * @returns {boolean} true when schema type is boolean, otherwise false
231 */
232function likeBoolean(schema) {
233 return schema.type === "boolean";
234}
235
236/**
237 * @param {Schema} schema schema
238 * @returns {boolean} true when can apply not, otherwise false
239 */
240function canApplyNot(schema) {
241 const typedSchema = findFirstTypedSchema(schema);
242 return likeNumber(typedSchema) || likeInteger(typedSchema) || likeString(typedSchema) || likeNull(typedSchema) || likeBoolean(typedSchema);
243}
244
245// eslint-disable-next-line jsdoc/no-restricted-syntax
246/**
247 * @param {any} maybeObj maybe obj
248 * @returns {boolean} true when value is object, otherwise false
249 */
250function isObject(maybeObj) {
251 return typeof maybeObj === "object" && !Array.isArray(maybeObj) && maybeObj !== null;
252}
253
254/**
255 * @param {Schema} schema schema
256 * @returns {boolean} true when schema type is array, otherwise false
257 */
258function likeArray(schema) {
259 return schema.type === "array" || typeof schema.minItems === "number" || typeof schema.maxItems === "number" || typeof schema.uniqueItems !== "undefined" || typeof schema.items !== "undefined" || typeof schema.additionalItems !== "undefined" || typeof schema.contains !== "undefined";
260}
261
262/**
263 * @param {Schema & {patternRequired?: Array<string>}} schema schema
264 * @returns {boolean} true when schema type is object, otherwise false
265 */
266function likeObject(schema) {
267 return schema.type === "object" || typeof schema.minProperties !== "undefined" || typeof schema.maxProperties !== "undefined" || typeof schema.required !== "undefined" || typeof schema.properties !== "undefined" || typeof schema.patternProperties !== "undefined" || typeof schema.additionalProperties !== "undefined" || typeof schema.dependencies !== "undefined" || typeof schema.propertyNames !== "undefined" || typeof schema.patternRequired !== "undefined";
268}
269
270/**
271 * @param {string} type type
272 * @returns {string} article
273 */
274function getArticle(type) {
275 if (/^[aeiou]/i.test(type)) {
276 return "an";
277 }
278 return "a";
279}
280
281/**
282 * @param {Schema=} schema schema
283 * @returns {string} schema non types
284 */
285function getSchemaNonTypes(schema) {
286 if (!schema) {
287 return "";
288 }
289 if (!schema.type) {
290 if (likeNumber(schema) || likeInteger(schema)) {
291 return " | should be any non-number";
292 }
293 if (likeString(schema)) {
294 return " | should be any non-string";
295 }
296 if (likeArray(schema)) {
297 return " | should be any non-array";
298 }
299 if (likeObject(schema)) {
300 return " | should be any non-object";
301 }
302 }
303 return "";
304}
305
306/**
307 * @param {Array<string>} hints hints
308 * @returns {string} formatted hints
309 */
310function formatHints(hints) {
311 return hints.length > 0 ? `(${hints.join(", ")})` : "";
312}
313const getUtilHints = (0, _memorize.default)(() => require("./util/hints"));
314
315/**
316 * @param {Schema} schema schema
317 * @param {boolean} logic logic
318 * @returns {string[]} array of hints
319 */
320function getHints(schema, logic) {
321 if (likeNumber(schema) || likeInteger(schema)) {
322 const util = getUtilHints();
323 return util.numberHints(schema, logic);
324 } else if (likeString(schema)) {
325 const util = getUtilHints();
326 return util.stringHints(schema, logic);
327 }
328 return [];
329}
330class ValidationError extends Error {
331 /**
332 * @param {Array<SchemaUtilErrorObject>} errors array of error objects
333 * @param {Schema} schema schema
334 * @param {ValidationErrorConfiguration} configuration configuration
335 */
336 constructor(errors, schema, configuration = {}) {
337 super();
338
339 /** @type {string} */
340 this.name = "ValidationError";
341 /** @type {Array<SchemaUtilErrorObject>} */
342 this.errors = errors;
343 /** @type {Schema} */
344 this.schema = schema;
345 let headerNameFromSchema;
346 let baseDataPathFromSchema;
347 if (schema.title && (!configuration.name || !configuration.baseDataPath)) {
348 const splittedTitleFromSchema = schema.title.match(/^(.+) (.+)$/);
349 if (splittedTitleFromSchema) {
350 if (!configuration.name) {
351 [, headerNameFromSchema] = splittedTitleFromSchema;
352 }
353 if (!configuration.baseDataPath) {
354 [,, baseDataPathFromSchema] = splittedTitleFromSchema;
355 }
356 }
357 }
358
359 /** @type {string} */
360 this.headerName = configuration.name || headerNameFromSchema || "Object";
361 /** @type {string} */
362 this.baseDataPath = configuration.baseDataPath || baseDataPathFromSchema || "configuration";
363
364 /** @type {PostFormatter | null} */
365 this.postFormatter = configuration.postFormatter || null;
366 const header = `Invalid ${this.baseDataPath} object. ${this.headerName} has been initialized using ${getArticle(this.baseDataPath)} ${this.baseDataPath} object that does not match the API schema.\n`;
367
368 /** @type {string} */
369 this.message = `${header}${this.formatValidationErrors(errors)}`;
370 Error.captureStackTrace(this, this.constructor);
371 }
372
373 /**
374 * @param {string} path path
375 * @returns {Schema} schema
376 */
377 getSchemaPart(path) {
378 const newPath = path.split("/");
379 let schemaPart = this.schema;
380 for (let i = 1; i < newPath.length; i++) {
381 const inner = schemaPart[(/** @type {keyof Schema} */newPath[i])];
382 if (!inner) {
383 break;
384 }
385 schemaPart = inner;
386 }
387 return schemaPart;
388 }
389
390 /**
391 * @param {Schema} schema schema
392 * @param {boolean} logic logic
393 * @param {Array<object>} prevSchemas prev schemas
394 * @returns {string} formatted schema
395 */
396 formatSchema(schema, logic = true, prevSchemas = []) {
397 let newLogic = logic;
398 const formatInnerSchema =
399 /**
400 * @param {Schema} innerSchema inner schema
401 * @param {boolean=} addSelf true when need to add self
402 * @returns {string} formatted schema
403 */
404 (innerSchema, addSelf) => {
405 if (!addSelf) {
406 return this.formatSchema(innerSchema, newLogic, prevSchemas);
407 }
408 if (prevSchemas.includes(innerSchema)) {
409 return "(recursive)";
410 }
411 return this.formatSchema(innerSchema, newLogic, [...prevSchemas, schema]);
412 };
413 if (hasNotInSchema(schema) && !likeObject(schema)) {
414 if (canApplyNot(schema.not)) {
415 newLogic = !logic;
416 return formatInnerSchema(schema.not);
417 }
418 const needApplyLogicHere = !schema.not.not;
419 const prefix = logic ? "" : "non ";
420 newLogic = !logic;
421 return needApplyLogicHere ? prefix + formatInnerSchema(schema.not) : formatInnerSchema(schema.not);
422 }
423 if (/** @type {Schema & {instanceof: string | Array<string>}} */
424 schema.instanceof) {
425 const {
426 instanceof: value
427 } = /** @type {Schema & {instanceof: string | Array<string>}} */schema;
428 const values = !Array.isArray(value) ? [value] : value;
429 return values.map(
430 /**
431 * @param {string} item item
432 * @returns {string} result
433 */
434 item => item === "Function" ? "function" : item).join(" | ");
435 }
436 if (schema.enum) {
437 // eslint-disable-next-line jsdoc/no-restricted-syntax
438 const enumValues = /** @type {Array<any>} */schema.enum.map(item => {
439 if (item === null && schema.undefinedAsNull) {
440 return `${JSON.stringify(item)} | undefined`;
441 }
442 return JSON.stringify(item);
443 }).join(" | ");
444 return `${enumValues}`;
445 }
446 if (typeof schema.const !== "undefined") {
447 return JSON.stringify(schema.const);
448 }
449 if (schema.oneOf) {
450 return /** @type {Array<Schema>} */schema.oneOf.map(item => formatInnerSchema(item, true)).join(" | ");
451 }
452 if (schema.anyOf) {
453 return /** @type {Array<Schema>} */schema.anyOf.map(item => formatInnerSchema(item, true)).join(" | ");
454 }
455 if (schema.allOf) {
456 return /** @type {Array<Schema>} */schema.allOf.map(item => formatInnerSchema(item, true)).join(" & ");
457 }
458 if (/** @type {JSONSchema7} */schema.if) {
459 const {
460 if: ifValue,
461 then: thenValue,
462 else: elseValue
463 } = /** @type {JSONSchema7} */schema;
464 return `${ifValue ? `if ${ifValue === true ? "true" : formatInnerSchema(ifValue)}` : ""}${thenValue ? ` then ${thenValue === true ? "true" : formatInnerSchema(thenValue)}` : ""}${elseValue ? ` else ${elseValue === true ? "true" : formatInnerSchema(elseValue)}` : ""}`;
465 }
466 if (schema.$ref) {
467 return formatInnerSchema(this.getSchemaPart(schema.$ref), true);
468 }
469 if (likeNumber(schema) || likeInteger(schema)) {
470 const [type, ...hints] = getHints(schema, logic);
471 const str = `${type}${hints.length > 0 ? ` ${formatHints(hints)}` : ""}`;
472 return logic ? str : hints.length > 0 ? `non-${type} | ${str}` : `non-${type}`;
473 }
474 if (likeString(schema)) {
475 const [type, ...hints] = getHints(schema, logic);
476 const str = `${type}${hints.length > 0 ? ` ${formatHints(hints)}` : ""}`;
477 return logic ? str : str === "string" ? "non-string" : `non-string | ${str}`;
478 }
479 if (likeBoolean(schema)) {
480 return `${logic ? "" : "non-"}boolean`;
481 }
482 if (likeArray(schema)) {
483 // not logic already applied in formatValidationError
484 newLogic = true;
485 const hints = [];
486 if (typeof schema.minItems === "number") {
487 hints.push(`should not have fewer than ${schema.minItems} item${schema.minItems > 1 ? "s" : ""}`);
488 }
489 if (typeof schema.maxItems === "number") {
490 hints.push(`should not have more than ${schema.maxItems} item${schema.maxItems > 1 ? "s" : ""}`);
491 }
492 if (schema.uniqueItems) {
493 hints.push("should not have duplicate items");
494 }
495 const hasAdditionalItems = typeof schema.additionalItems === "undefined" || Boolean(schema.additionalItems);
496 let items = "";
497 if (schema.items) {
498 if (Array.isArray(schema.items) && schema.items.length > 0) {
499 items = `${/** @type {Array<Schema>} */schema.items.map(item => formatInnerSchema(item)).join(", ")}`;
500 if (hasAdditionalItems && schema.additionalItems && isObject(schema.additionalItems) && Object.keys(schema.additionalItems).length > 0) {
501 hints.push(`additional items should be ${schema.additionalItems === true ? "added" : formatInnerSchema(schema.additionalItems)}`);
502 }
503 } else if (schema.items && Object.keys(schema.items).length > 0 && schema.items !== true) {
504 // "additionalItems" is ignored
505 items = `${formatInnerSchema(schema.items)}`;
506 } else {
507 // Fallback for empty `items` value
508 items = "any";
509 }
510 } else {
511 // "additionalItems" is ignored
512 items = "any";
513 }
514 if (schema.contains && Object.keys(schema.contains).length > 0) {
515 hints.push(`should contains at least one ${this.formatSchema(schema.contains)} item`);
516 }
517 return `[${items}${hasAdditionalItems ? ", ..." : ""}]${hints.length > 0 ? ` (${hints.join(", ")})` : ""}`;
518 }
519 if (likeObject(schema)) {
520 // not logic already applied in formatValidationError
521 newLogic = true;
522 const hints = [];
523 if (typeof schema.minProperties === "number") {
524 hints.push(`should not have fewer than ${schema.minProperties} ${schema.minProperties > 1 ? "properties" : "property"}`);
525 }
526 if (typeof schema.maxProperties === "number") {
527 hints.push(`should not have more than ${schema.maxProperties} ${schema.minProperties && schema.minProperties > 1 ? "properties" : "property"}`);
528 }
529 if (schema.patternProperties && Object.keys(schema.patternProperties).length > 0) {
530 const patternProperties = Object.keys(schema.patternProperties);
531 hints.push(`additional property names should match pattern${patternProperties.length > 1 ? "s" : ""} ${patternProperties.map(pattern => JSON.stringify(pattern)).join(" | ")}`);
532 }
533 const properties = schema.properties ? Object.keys(schema.properties) : [];
534 const required = /** @type {string[]} */
535 schema.required ? schema.required : [];
536 const allProperties = [...new Set(/** @type {Array<string>} */[...required, ...properties])];
537 const objectStructure = [...allProperties.map(property => {
538 const isRequired = required.includes(property);
539
540 // Some properties need quotes, maybe we should add check
541 // Maybe we should output type of property (`foo: string`), but it is looks very unreadable
542 return `${property}${isRequired ? "" : "?"}`;
543 }), ...(typeof schema.additionalProperties === "undefined" || Boolean(schema.additionalProperties) ? schema.additionalProperties && isObject(schema.additionalProperties) && schema.additionalProperties !== true ? [`<key>: ${formatInnerSchema(schema.additionalProperties)}`] : ["…"] : [])].join(", ");
544 const {
545 dependencies,
546 propertyNames,
547 patternRequired
548 } = /** @type {Schema & {patternRequired?: Array<string>;}} */schema;
549 if (dependencies) {
550 for (const dependencyName of Object.keys(dependencies)) {
551 const dependency = dependencies[dependencyName];
552 if (Array.isArray(dependency)) {
553 hints.push(`should have ${dependency.length > 1 ? "properties" : "property"} ${dependency.map(dep => `'${dep}'`).join(", ")} when property '${dependencyName}' is present`);
554 } else {
555 hints.push(`should be valid according to the schema ${typeof dependency === "boolean" ? `${dependency}` : formatInnerSchema(dependency)} when property '${dependencyName}' is present`);
556 }
557 }
558 }
559 if (propertyNames && Object.keys(propertyNames).length > 0) {
560 hints.push(`each property name should match format ${JSON.stringify(schema.propertyNames.format)}`);
561 }
562 if (patternRequired && patternRequired.length > 0) {
563 hints.push(`should have property matching pattern ${patternRequired.map(
564 /**
565 * @param {string} item item
566 * @returns {string} stringified item
567 */
568 item => JSON.stringify(item))}`);
569 }
570 return `object {${objectStructure ? ` ${objectStructure} ` : ""}}${hints.length > 0 ? ` (${hints.join(", ")})` : ""}`;
571 }
572 if (likeNull(schema)) {
573 return `${logic ? "" : "non-"}null`;
574 }
575 if (Array.isArray(schema.type)) {
576 // not logic already applied in formatValidationError
577 return `${schema.type.join(" | ")}`;
578 }
579
580 // Fallback for unknown keywords
581 // not logic already applied in formatValidationError
582 /* istanbul ignore next */
583 return JSON.stringify(schema, null, 2);
584 }
585
586 /**
587 * @param {Schema=} schemaPart schema part
588 * @param {(boolean | Array<string>)=} additionalPath additional path
589 * @param {boolean=} needDot true when need dot
590 * @param {boolean=} logic logic
591 * @returns {string} schema part text
592 */
593 getSchemaPartText(schemaPart, additionalPath, needDot = false, logic = true) {
594 if (!schemaPart) {
595 return "";
596 }
597 if (Array.isArray(additionalPath)) {
598 for (let i = 0; i < additionalPath.length; i++) {
599 /** @type {Schema | undefined} */
600 const inner = schemaPart[(/** @type {keyof Schema} */additionalPath[i])];
601 if (inner) {
602 schemaPart = inner;
603 } else {
604 break;
605 }
606 }
607 }
608 while (schemaPart.$ref) {
609 schemaPart = this.getSchemaPart(schemaPart.$ref);
610 }
611 let schemaText = `${this.formatSchema(schemaPart, logic)}${needDot ? "." : ""}`;
612 if (schemaPart.description) {
613 schemaText += `\n-> ${schemaPart.description}`;
614 }
615 if (schemaPart.link) {
616 schemaText += `\n-> Read more at ${schemaPart.link}`;
617 }
618 return schemaText;
619 }
620
621 /**
622 * @param {Schema=} schemaPart schema part
623 * @returns {string} schema part description
624 */
625 getSchemaPartDescription(schemaPart) {
626 if (!schemaPart) {
627 return "";
628 }
629 while (schemaPart.$ref) {
630 schemaPart = this.getSchemaPart(schemaPart.$ref);
631 }
632 let schemaText = "";
633 if (schemaPart.description) {
634 schemaText += `\n-> ${schemaPart.description}`;
635 }
636 if (schemaPart.link) {
637 schemaText += `\n-> Read more at ${schemaPart.link}`;
638 }
639 return schemaText;
640 }
641
642 /**
643 * @param {SchemaUtilErrorObject} error error object
644 * @returns {string} formatted error object
645 */
646 formatValidationError(error) {
647 const {
648 keyword,
649 instancePath: errorInstancePath
650 } = error;
651 const splittedInstancePath = errorInstancePath.split("/");
652 /**
653 * @type {Array<string>}
654 */
655 const defaultValue = [];
656 const prettyInstancePath = splittedInstancePath.reduce((acc, val) => {
657 if (val.length > 0) {
658 if (isNumeric(val)) {
659 acc.push(`[${val}]`);
660 } else if (/^\[/.test(val)) {
661 acc.push(val);
662 } else {
663 acc.push(`.${val}`);
664 }
665 }
666 return acc;
667 }, defaultValue).join("");
668 const instancePath = `${this.baseDataPath}${prettyInstancePath}`;
669
670 // const { keyword, instancePath: errorInstancePath } = error;
671 // const instancePath = `${this.baseDataPath}${errorInstancePath.replace(/\//g, '.')}`;
672
673 switch (keyword) {
674 case "type":
675 {
676 const {
677 parentSchema,
678 params
679 } = error;
680 switch (params.type) {
681 case "number":
682 return `${instancePath} should be a ${this.getSchemaPartText(parentSchema, false, true)}`;
683 case "integer":
684 return `${instancePath} should be an ${this.getSchemaPartText(parentSchema, false, true)}`;
685 case "string":
686 return `${instancePath} should be a ${this.getSchemaPartText(parentSchema, false, true)}`;
687 case "boolean":
688 return `${instancePath} should be a ${this.getSchemaPartText(parentSchema, false, true)}`;
689 case "array":
690 return `${instancePath} should be an array:\n${this.getSchemaPartText(parentSchema)}`;
691 case "object":
692 return `${instancePath} should be an object:\n${this.getSchemaPartText(parentSchema)}`;
693 case "null":
694 return `${instancePath} should be a ${this.getSchemaPartText(parentSchema, false, true)}`;
695 default:
696 return `${instancePath} should be:\n${this.getSchemaPartText(parentSchema)}`;
697 }
698 }
699 case "instanceof":
700 {
701 const {
702 parentSchema
703 } = error;
704 return `${instancePath} should be an instance of ${this.getSchemaPartText(parentSchema, false, true)}`;
705 }
706 case "pattern":
707 {
708 const {
709 params,
710 parentSchema
711 } = error;
712 const {
713 pattern
714 } = params;
715 return `${instancePath} should match pattern ${JSON.stringify(pattern)}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
716 }
717 case "format":
718 {
719 const {
720 params,
721 parentSchema
722 } = error;
723 const {
724 format
725 } = params;
726 return `${instancePath} should match format ${JSON.stringify(format)}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
727 }
728 case "formatMinimum":
729 case "formatExclusiveMinimum":
730 case "formatMaximum":
731 case "formatExclusiveMaximum":
732 {
733 const {
734 params,
735 parentSchema
736 } = error;
737 const {
738 comparison,
739 limit
740 } = params;
741 return `${instancePath} should be ${comparison} ${JSON.stringify(limit)}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
742 }
743 case "minimum":
744 case "maximum":
745 case "exclusiveMinimum":
746 case "exclusiveMaximum":
747 {
748 const {
749 parentSchema,
750 params
751 } = error;
752 const {
753 comparison,
754 limit
755 } = params;
756 const [, ...hints] = getHints(/** @type {Schema} */parentSchema, true);
757 if (hints.length === 0) {
758 hints.push(`should be ${comparison} ${limit}`);
759 }
760 return `${instancePath} ${hints.join(" ")}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
761 }
762 case "multipleOf":
763 {
764 const {
765 params,
766 parentSchema
767 } = error;
768 const {
769 multipleOf
770 } = params;
771 return `${instancePath} should be multiple of ${multipleOf}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
772 }
773 case "patternRequired":
774 {
775 const {
776 params,
777 parentSchema
778 } = error;
779 const {
780 missingPattern
781 } = params;
782 return `${instancePath} should have property matching pattern ${JSON.stringify(missingPattern)}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
783 }
784 case "minLength":
785 {
786 const {
787 params,
788 parentSchema
789 } = error;
790 const {
791 limit
792 } = params;
793 if (limit === 1) {
794 return `${instancePath} should be a non-empty string${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
795 }
796 const length = limit - 1;
797 return `${instancePath} should be longer than ${length} character${length > 1 ? "s" : ""}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
798 }
799 case "minItems":
800 {
801 const {
802 params,
803 parentSchema
804 } = error;
805 const {
806 limit
807 } = params;
808 if (limit === 1) {
809 return `${instancePath} should be a non-empty array${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
810 }
811 return `${instancePath} should not have fewer than ${limit} items${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
812 }
813 case "minProperties":
814 {
815 const {
816 params,
817 parentSchema
818 } = error;
819 const {
820 limit
821 } = params;
822 if (limit === 1) {
823 return `${instancePath} should be a non-empty object${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
824 }
825 return `${instancePath} should not have fewer than ${limit} properties${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
826 }
827 case "maxLength":
828 {
829 const {
830 params,
831 parentSchema
832 } = error;
833 const {
834 limit
835 } = params;
836 const max = limit + 1;
837 return `${instancePath} should be shorter than ${max} character${max > 1 ? "s" : ""}${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
838 }
839 case "maxItems":
840 {
841 const {
842 params,
843 parentSchema
844 } = error;
845 const {
846 limit
847 } = params;
848 return `${instancePath} should not have more than ${limit} items${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
849 }
850 case "maxProperties":
851 {
852 const {
853 params,
854 parentSchema
855 } = error;
856 const {
857 limit
858 } = params;
859 return `${instancePath} should not have more than ${limit} properties${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
860 }
861 case "uniqueItems":
862 {
863 const {
864 params,
865 parentSchema
866 } = error;
867 const {
868 i
869 } = params;
870 return `${instancePath} should not contain the item '${
871 // eslint-disable-next-line jsdoc/no-restricted-syntax
872 /** @type {{ data: Array<any> }} * */
873 error.data[i]}' twice${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
874 }
875 case "additionalItems":
876 {
877 const {
878 params,
879 parentSchema
880 } = error;
881 const {
882 limit
883 } = params;
884 return `${instancePath} should not have more than ${limit} items${getSchemaNonTypes(parentSchema)}. These items are valid:\n${this.getSchemaPartText(parentSchema)}`;
885 }
886 case "contains":
887 {
888 const {
889 parentSchema
890 } = error;
891 return `${instancePath} should contains at least one ${this.getSchemaPartText(parentSchema, ["contains"])} item${getSchemaNonTypes(parentSchema)}.`;
892 }
893 case "required":
894 {
895 const {
896 parentSchema,
897 params
898 } = error;
899 const missingProperty = params.missingProperty.replace(/^\./, "");
900 const hasProperty = parentSchema && Boolean(/** @type {Schema} */
901 parentSchema.properties && /** @type {Schema} */
902 parentSchema.properties[missingProperty]);
903 return `${instancePath} misses the property '${missingProperty}'${getSchemaNonTypes(parentSchema)}.${hasProperty ? ` Should be:\n${this.getSchemaPartText(parentSchema, ["properties", missingProperty])}` : this.getSchemaPartDescription(parentSchema)}`;
904 }
905 case "additionalProperties":
906 {
907 const {
908 params,
909 parentSchema
910 } = error;
911 const {
912 additionalProperty
913 } = params;
914 return `${instancePath} has an unknown property '${additionalProperty}'${getSchemaNonTypes(parentSchema)}. These properties are valid:\n${this.getSchemaPartText(parentSchema)}`;
915 }
916 case "dependencies":
917 {
918 const {
919 params,
920 parentSchema
921 } = error;
922 const {
923 property,
924 deps
925 } = params;
926 const dependencies = deps.split(",").map(
927 /**
928 * @param {string} dep dependency
929 * @returns {string} normalized dependency
930 */
931 dep => `'${dep.trim()}'`).join(", ");
932 return `${instancePath} should have properties ${dependencies} when property '${property}' is present${getSchemaNonTypes(parentSchema)}.${this.getSchemaPartDescription(parentSchema)}`;
933 }
934 case "propertyNames":
935 {
936 const {
937 params,
938 parentSchema,
939 schema
940 } = error;
941 const {
942 propertyName
943 } = params;
944 return `${instancePath} property name '${propertyName}' is invalid${getSchemaNonTypes(parentSchema)}. Property names should be match format ${JSON.stringify(schema.format)}.${this.getSchemaPartDescription(parentSchema)}`;
945 }
946 case "enum":
947 {
948 const {
949 parentSchema
950 } = error;
951 if (parentSchema && /** @type {Schema} */
952 parentSchema.enum && /** @type {Schema} */
953 parentSchema.enum.length === 1) {
954 return `${instancePath} should be ${this.getSchemaPartText(parentSchema, false, true)}`;
955 }
956 return `${instancePath} should be one of these:\n${this.getSchemaPartText(parentSchema)}`;
957 }
958 case "const":
959 {
960 const {
961 parentSchema
962 } = error;
963 return `${instancePath} should be equal to constant ${this.getSchemaPartText(parentSchema, false, true)}`;
964 }
965 case "not":
966 {
967 const postfix = likeObject(/** @type {Schema} */error.parentSchema) ? `\n${this.getSchemaPartText(error.parentSchema)}` : "";
968 const schemaOutput = this.getSchemaPartText(error.schema, false, false, false);
969 if (canApplyNot(error.schema)) {
970 return `${instancePath} should be any ${schemaOutput}${postfix}.`;
971 }
972 const {
973 schema,
974 parentSchema
975 } = error;
976 return `${instancePath} should not be ${this.getSchemaPartText(schema, false, true)}${parentSchema && likeObject(parentSchema) ? `\n${this.getSchemaPartText(parentSchema)}` : ""}`;
977 }
978 case "oneOf":
979 case "anyOf":
980 {
981 const {
982 parentSchema,
983 children
984 } = error;
985 if (children && children.length > 0) {
986 if (error.schema.length === 1) {
987 const lastChild = children[children.length - 1];
988 const remainingChildren = children.slice(0, -1);
989 return this.formatValidationError({
990 ...lastChild,
991 children: remainingChildren,
992 parentSchema: {
993 ...parentSchema,
994 ...lastChild.parentSchema
995 }
996 });
997 }
998 let filteredChildren = filterChildren(children);
999 if (filteredChildren.length === 1) {
1000 return this.formatValidationError(filteredChildren[0]);
1001 }
1002 filteredChildren = groupChildrenByFirstChild(filteredChildren);
1003 return `${instancePath} should be one of these:\n${this.getSchemaPartText(parentSchema)}\nDetails:\n${filteredChildren.map(
1004 /**
1005 * @param {SchemaUtilErrorObject} nestedError nested error
1006 * @returns {string} formatted errors
1007 */
1008 nestedError => ` * ${indent(this.formatValidationError(nestedError), " ")}`).join("\n")}`;
1009 }
1010 return `${instancePath} should be one of these:\n${this.getSchemaPartText(parentSchema)}`;
1011 }
1012 case "if":
1013 {
1014 const {
1015 params,
1016 parentSchema
1017 } = error;
1018 const {
1019 failingKeyword
1020 } = params;
1021 return `${instancePath} should match "${failingKeyword}" schema:\n${this.getSchemaPartText(parentSchema, [failingKeyword])}`;
1022 }
1023 case "absolutePath":
1024 {
1025 const {
1026 message,
1027 parentSchema
1028 } = error;
1029 return `${instancePath}: ${message}${this.getSchemaPartDescription(parentSchema)}`;
1030 }
1031 /* istanbul ignore next */
1032 default:
1033 {
1034 const {
1035 message,
1036 parentSchema
1037 } = error;
1038 const ErrorInJSON = JSON.stringify(error, null, 2);
1039
1040 // For `custom`, `false schema`, `$ref` keywords
1041 // Fallback for unknown keywords
1042 return `${instancePath} ${message} (${ErrorInJSON}).\n${this.getSchemaPartText(parentSchema, false)}`;
1043 }
1044 }
1045 }
1046
1047 /**
1048 * @param {Array<SchemaUtilErrorObject>} errors errors
1049 * @returns {string} formatted errors
1050 */
1051 formatValidationErrors(errors) {
1052 return errors.map(error => {
1053 let formattedError = this.formatValidationError(error);
1054 if (this.postFormatter) {
1055 formattedError = this.postFormatter(formattedError, error);
1056 }
1057 return ` - ${indent(formattedError, " ")}`;
1058 }).join("\n");
1059 }
1060}
1061var _default = exports.default = ValidationError;
Note: See TracBrowser for help on using the repository browser.