source: imaps-frontend/node_modules/schema-utils/dist/ValidationError.js@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 3 days ago

F4 Finalna Verzija

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