json-schema-library 11.0.1 → 11.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/launch.json +37 -0
- package/README.md +4 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +27 -3
- package/dist/index.d.mts +27 -3
- package/dist/index.mjs +1 -1
- package/dist/jlib.js +2 -2
- package/package.json +2 -2
- package/src/SchemaNode.ts +26 -4
- package/src/draftEditor.ts +4 -4
- package/src/errors/errors.ts +3 -2
- package/src/getNode.oneOfProperty.test.ts +43 -0
- package/src/{compileSchema.getNode.test.ts → getNode.test.ts} +1 -1
- package/src/getNode.ts +16 -3
- package/src/getNodeChild.ts +34 -28
- package/src/keywords/oneOf.test.ts +40 -17
- package/src/keywords/oneOf.ts +23 -8
- package/src/methods/getChildSelection.test.ts +18 -0
- package/src/methods/getChildSelection.ts +1 -1
- package/src/{compileSchema.reduceSchema.test.ts → reduceNode.test.ts} +0 -1
- /package/src/{compileSchema.getChild.test.ts → getChildNode.test.ts} +0 -0
- /package/src/{compileSchema.validate.test.ts → validate.test.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "json-schema-library",
|
|
3
|
-
"version": "11.0.
|
|
3
|
+
"version": "11.0.2",
|
|
4
4
|
"description": "Customizable and hackable json-validator and json-schema utilities for traversal, data generation and validation",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"test:6:ci": "DISABLE_LOG=true mocha -R json 'src/tests/spec/draft06.spec.ts' > test-result-spec6.json; exit 0",
|
|
34
34
|
"test:7": "mocha 'src/tests/spec/draft07.spec.ts'",
|
|
35
35
|
"test:7:ci": "DISABLE_LOG=true mocha -R json 'src/tests/spec/draft07.spec.ts' > test-result-spec7.json; exit 0",
|
|
36
|
-
"test:inspect": "
|
|
36
|
+
"test:inspect": "NODE_OPTIONS='--inspect-brk' mocha 'src/**/*.test.ts'",
|
|
37
37
|
"test:spec": "mocha 'src/tests/spec/*.spec.ts'",
|
|
38
38
|
"test:unit": "mocha 'src/**/*.test.ts'",
|
|
39
39
|
"test:unit:ci": "DISABLE_LOG=true mocha -R json 'src/**/*.test.ts' -R json > test-result-unit.json; exit 0"
|
package/src/SchemaNode.ts
CHANGED
|
@@ -38,7 +38,7 @@ import { getNode } from "./getNode";
|
|
|
38
38
|
import { getNodeChild } from "./getNodeChild";
|
|
39
39
|
import { DataNode } from "./methods/toDataNodes";
|
|
40
40
|
|
|
41
|
-
const { DYNAMIC_PROPERTIES, REGEX_FLAGS } = settings;
|
|
41
|
+
const { DYNAMIC_PROPERTIES, REGEX_FLAGS, DECLARATOR_ONEOF } = settings;
|
|
42
42
|
|
|
43
43
|
export function isSchemaNode(value: unknown): value is SchemaNode {
|
|
44
44
|
return isObject(value) && Array.isArray(value?.reducers) && Array.isArray(value?.resolvers);
|
|
@@ -194,12 +194,34 @@ interface SchemaNodeMethodsType {
|
|
|
194
194
|
createAnnotation<T extends string = DefaultErrors>(code: T, data: AnnotationData, message?: string): JsonAnnotation;
|
|
195
195
|
createSchema(data?: unknown): JsonSchema;
|
|
196
196
|
|
|
197
|
-
|
|
197
|
+
/**
|
|
198
|
+
* Returns a node matching the given location (pointer) in data
|
|
199
|
+
*
|
|
200
|
+
* - the returned node will have a **reduced schema** based on given input data
|
|
201
|
+
* - return returned node $ref is resolved
|
|
202
|
+
*
|
|
203
|
+
* To resolve dynamic schema where the type of JSON Schema is evaluated by
|
|
204
|
+
* its value, a data object has to be passed in options.
|
|
205
|
+
*
|
|
206
|
+
* Per default this function will return `undefined` schema for valid properties
|
|
207
|
+
* that do not have a defined schema. Use the option `withSchemaWarning: true` to
|
|
208
|
+
* receive an error with `code: schema-warning` containing the location of its
|
|
209
|
+
* last evaluated json-schema.
|
|
210
|
+
*
|
|
211
|
+
* @returns { node } or { error } where node can also be undefined (valid but undefined)
|
|
212
|
+
*/
|
|
198
213
|
getNode(pointer: string, data: unknown, options: { withSchemaWarning: true } & GetNodeOptions): NodeOrError;
|
|
199
214
|
getNode(pointer: string, data: unknown, options: { createSchema: true } & GetNodeOptions): NodeOrError;
|
|
200
215
|
getNode(pointer: string, data?: unknown, options?: GetNodeOptions): OptionalNodeOrError;
|
|
201
216
|
|
|
202
|
-
|
|
217
|
+
/**
|
|
218
|
+
* Returns the child for the given property-name or array-index
|
|
219
|
+
*
|
|
220
|
+
* - the returned child node is **not reduced**
|
|
221
|
+
* - a child node $ref is resolved
|
|
222
|
+
*
|
|
223
|
+
* @returns { node } or { error } where node can also be undefined (valid but undefined)
|
|
224
|
+
*/
|
|
203
225
|
getNodeChild(
|
|
204
226
|
key: string | number,
|
|
205
227
|
data: unknown,
|
|
@@ -431,7 +453,7 @@ export const SchemaNodeMethods = {
|
|
|
431
453
|
}
|
|
432
454
|
|
|
433
455
|
// remove dynamic properties of node
|
|
434
|
-
workingNode.schema = omit(workingNode.schema, ...DYNAMIC_PROPERTIES);
|
|
456
|
+
workingNode.schema = omit(workingNode.schema, DECLARATOR_ONEOF, ...DYNAMIC_PROPERTIES);
|
|
435
457
|
// @ts-expect-error string accessing schema props
|
|
436
458
|
DYNAMIC_PROPERTIES.forEach((prop) => (workingNode[prop] = undefined));
|
|
437
459
|
return { node: workingNode, error: undefined };
|
package/src/draftEditor.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { extendDraft } from "./Draft";
|
|
2
|
-
import {
|
|
2
|
+
import { draft2020 } from "./draft2020";
|
|
3
3
|
import { oneOfFuzzyKeyword } from "./keywords/oneOf";
|
|
4
4
|
import { render } from "./errors/render";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* @draft-editor https://json-schema.org/draft/
|
|
7
|
+
* @draft-editor https://json-schema.org/draft/2020-12/release-notes
|
|
8
8
|
*
|
|
9
|
-
* Uses Draft
|
|
9
|
+
* Uses Draft 2020-12 and changes resolveOneOf to be fuzzy
|
|
10
10
|
*/
|
|
11
|
-
export const draftEditor = extendDraft(
|
|
11
|
+
export const draftEditor = extendDraft(draft2020, {
|
|
12
12
|
$schemaRegEx: ".",
|
|
13
13
|
keywords: [oneOfFuzzyKeyword],
|
|
14
14
|
errors: {
|
package/src/errors/errors.ts
CHANGED
|
@@ -45,12 +45,13 @@ export const errors = {
|
|
|
45
45
|
"min-items-one-error": "At least one item is required in `{{pointer}}`",
|
|
46
46
|
"min-length-error": "Value `{{pointer}}` should have a minimum length of `{{minLength}}`, but got `{{length}}`.",
|
|
47
47
|
"min-length-one-error": "A value is required in `{{pointer}}`",
|
|
48
|
-
"missing-one-of-declarator-error": "Missing oneOf declarator `{{declarator}}` in `{{
|
|
48
|
+
"missing-one-of-declarator-error": "Missing oneOf declarator `{{declarator}}` in schema `{{schemaLocation}}`",
|
|
49
49
|
"min-properties-error":
|
|
50
50
|
"Too few properties in `{{pointer}}`, should be at least `{{minProperties}}`, but got `{{length}}`",
|
|
51
51
|
"missing-array-item-error": "Array at '{{pointer}}' has a missing item at '{{key}}'",
|
|
52
52
|
"missing-dependency-error": "The required propery '{{missingProperty}}' in `{{pointer}}` is missing",
|
|
53
|
-
"missing-one-of-property-error":
|
|
53
|
+
"missing-one-of-property-error":
|
|
54
|
+
"Value at `{{pointer}}` must be object or array and have a property ${oneOfProperty}: ${value}",
|
|
54
55
|
"multiple-of-error": "Expected `{{value}}` in `{{pointer}}` to be multiple of `{{multipleOf}}`",
|
|
55
56
|
"multiple-one-of-error": "Value `{{value}}` should not match multiple schemas in oneOf `{{matches}}`",
|
|
56
57
|
"no-additional-properties-error": "Additional property `{{property}}` in `{{pointer}}` is not allowed",
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { compileSchema } from "./compileSchema";
|
|
2
|
+
import { strict as assert } from "assert";
|
|
3
|
+
import { isSchemaNode } from "./types";
|
|
4
|
+
import settings from "./settings";
|
|
5
|
+
const DECLARATOR_ONEOF = settings.DECLARATOR_ONEOF;
|
|
6
|
+
|
|
7
|
+
describe("getNodeChild.oneOfProperty", () => {
|
|
8
|
+
describe("", () => {
|
|
9
|
+
it("should return resolved reference (reduced oneOfNode)", () => {
|
|
10
|
+
const node = compileSchema({
|
|
11
|
+
items: {
|
|
12
|
+
[DECLARATOR_ONEOF]: "name",
|
|
13
|
+
oneOf: [{ $ref: "#/$defs/first" }, { $ref: "#/$defs/second" }]
|
|
14
|
+
},
|
|
15
|
+
$defs: {
|
|
16
|
+
first: {
|
|
17
|
+
properties: { name: { type: "string", const: "first" }, title: { type: "number" } }
|
|
18
|
+
},
|
|
19
|
+
second: {
|
|
20
|
+
properties: { name: { type: "string", const: "second" }, title: { type: "number" } }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// precondition
|
|
26
|
+
const reducedItems = node.items?.reduceNode({ name: "second" });
|
|
27
|
+
assert(reducedItems && reducedItems.node, "should have successfully resolved items schema directly");
|
|
28
|
+
assert.deepEqual(
|
|
29
|
+
reducedItems.node?.schema,
|
|
30
|
+
{
|
|
31
|
+
properties: { name: { type: "string", const: "second" }, title: { type: "number" } }
|
|
32
|
+
},
|
|
33
|
+
"should have correctly resolved items-schema directly"
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const { node: res } = node.getNode("#/0", [{ name: "second" }]);
|
|
37
|
+
assert(isSchemaNode(res), "expected result to a node");
|
|
38
|
+
assert.deepEqual(res.schema, {
|
|
39
|
+
properties: { name: { type: "string", const: "second" }, title: { type: "number" } }
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -520,7 +520,7 @@ describe("compileSchema : getNode", () => {
|
|
|
520
520
|
assert.deepEqual(node.schema, { type: "array", minItems: 2 });
|
|
521
521
|
});
|
|
522
522
|
|
|
523
|
-
it("should
|
|
523
|
+
it("should merge title from local schema", () => {
|
|
524
524
|
const { node } = compileSchema({
|
|
525
525
|
type: "array",
|
|
526
526
|
prefixItems: [{ title: "from ref", $ref: "/$defs/target" }],
|
package/src/getNode.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GetNodeOptions, SchemaNode } from "./SchemaNode";
|
|
1
|
+
import { GetNodeOptions, isSchemaNode, SchemaNode } from "./SchemaNode";
|
|
2
2
|
import { isJsonError, NodeOrError, OptionalNodeOrError } from "./types";
|
|
3
3
|
import { split } from "@sagold/json-pointer";
|
|
4
4
|
import { getValue } from "./utils/getValue";
|
|
@@ -7,9 +7,13 @@ import { getValue } from "./utils/getValue";
|
|
|
7
7
|
export function getNode(pointer: string, data: unknown, options: { withSchemaWarning: true } & GetNodeOptions): NodeOrError;
|
|
8
8
|
export function getNode(pointer: string, data: unknown, options: { createSchema: true } & GetNodeOptions): NodeOrError;
|
|
9
9
|
export function getNode(pointer: string, data?: unknown, options?: GetNodeOptions): OptionalNodeOrError;
|
|
10
|
+
|
|
10
11
|
/**
|
|
11
12
|
* Returns a node containing JSON Schema of a data JSON Pointer.
|
|
12
13
|
*
|
|
14
|
+
* - the returned node will have a reduced schema based on given input data
|
|
15
|
+
* - the returned node $ref is resolved
|
|
16
|
+
*
|
|
13
17
|
* To resolve dynamic schema where the type of JSON Schema is evaluated by
|
|
14
18
|
* its value, a data object has to be passed in options.
|
|
15
19
|
*
|
|
@@ -49,6 +53,15 @@ export function getNode(
|
|
|
49
53
|
currentNode = result.node;
|
|
50
54
|
data = getValue(data, keys[i]);
|
|
51
55
|
}
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
|
|
57
|
+
const { node: reducedNode, error: reduceError } = currentNode.resolveRef(options).reduceNode(data);
|
|
58
|
+
|
|
59
|
+
if (isJsonError(reduceError)) {
|
|
60
|
+
return { node: undefined, error: reduceError };
|
|
61
|
+
}
|
|
62
|
+
if (isSchemaNode(reducedNode)) {
|
|
63
|
+
return { node: reducedNode, error: undefined };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { error: undefined };
|
|
54
67
|
}
|
package/src/getNodeChild.ts
CHANGED
|
@@ -2,67 +2,73 @@ import { GetNodeOptions, isSchemaNode, SchemaNode } from "./SchemaNode";
|
|
|
2
2
|
import { isJsonError, NodeOrError, OptionalNodeOrError } from "./types";
|
|
3
3
|
import { getValue } from "./utils/getValue";
|
|
4
4
|
|
|
5
|
-
// prettier-ignore
|
|
6
|
-
export function getNodeChild(key: string | number, data: unknown, options: {
|
|
7
|
-
// prettier-ignore
|
|
8
|
-
export function getNodeChild(key: string | number, data: unknown, options: { createSchema: true } & GetNodeOptions): NodeOrError;
|
|
5
|
+
export function getNodeChild(key: string | number, data: unknown, options: { withSchemaWarning: true } & GetNodeOptions): NodeOrError; // prettier-ignore
|
|
6
|
+
export function getNodeChild(key: string | number, data: unknown, options: { createSchema: true } & GetNodeOptions): NodeOrError; // prettier-ignore
|
|
9
7
|
export function getNodeChild(key: string | number, data?: unknown, options?: GetNodeOptions): OptionalNodeOrError;
|
|
10
8
|
|
|
11
9
|
/**
|
|
12
|
-
*
|
|
10
|
+
* Returns the child for the given property-name or array-index
|
|
11
|
+
*
|
|
12
|
+
* - the returned child node is **not reduced**
|
|
13
|
+
* - a child node $ref is resolved
|
|
14
|
+
*
|
|
15
|
+
* @returns { node } or { error } where node can also be undefined (valid but undefined)
|
|
13
16
|
*/
|
|
14
17
|
export function getNodeChild(
|
|
15
18
|
key: string | number,
|
|
16
19
|
data?: unknown,
|
|
17
20
|
options: GetNodeOptions = {}
|
|
18
|
-
): OptionalNodeOrError | NodeOrError
|
|
21
|
+
): OptionalNodeOrError | NodeOrError {
|
|
19
22
|
options.path = options.path ?? [];
|
|
20
|
-
|
|
21
23
|
options.withSchemaWarning = options.withSchemaWarning ?? false;
|
|
22
24
|
options.pointer = options.pointer ?? "#";
|
|
23
25
|
const { path, pointer } = options;
|
|
24
26
|
|
|
27
|
+
// reduce parent
|
|
25
28
|
// @ts-expect-error implicitely any
|
|
26
|
-
let
|
|
27
|
-
if (
|
|
28
|
-
const result =
|
|
29
|
+
let parentNode = this as SchemaNode;
|
|
30
|
+
if (parentNode.reducers.length) {
|
|
31
|
+
const result = parentNode.reduceNode(data, { key, path, pointer });
|
|
29
32
|
if (result.error) {
|
|
30
33
|
return result;
|
|
31
34
|
}
|
|
32
35
|
if (isSchemaNode(result.node)) {
|
|
33
|
-
|
|
36
|
+
parentNode = result.node;
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
40
|
+
// find child node
|
|
41
|
+
for (const resolver of parentNode.resolvers) {
|
|
42
|
+
const schemaNode = resolver({ data, key, node: parentNode });
|
|
43
|
+
// a matching resolver found an error, return
|
|
42
44
|
if (isJsonError(schemaNode)) {
|
|
43
45
|
return { node: undefined, error: schemaNode };
|
|
44
46
|
}
|
|
47
|
+
// a matching resolver found a child node, return
|
|
48
|
+
if (isSchemaNode(schemaNode)) {
|
|
49
|
+
return { node: schemaNode.resolveRef({ pointer, path }), error: undefined };
|
|
50
|
+
}
|
|
45
51
|
}
|
|
46
52
|
|
|
47
|
-
|
|
48
|
-
if (referencedNode !== node) {
|
|
49
|
-
return referencedNode.getNodeChild(key, data, options);
|
|
50
|
-
}
|
|
51
|
-
|
|
53
|
+
// no child node was found, but the child node is valid
|
|
52
54
|
if (options.createSchema === true) {
|
|
53
|
-
const newNode =
|
|
54
|
-
|
|
55
|
-
`${
|
|
56
|
-
`${
|
|
55
|
+
const newNode = parentNode.compileSchema(
|
|
56
|
+
parentNode.createSchema(getValue(data, key)),
|
|
57
|
+
`${parentNode.evaluationPath}/additional`,
|
|
58
|
+
`${parentNode.schemaLocation}/additional`
|
|
57
59
|
);
|
|
58
60
|
return { node: newNode, error: undefined };
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
if (options.withSchemaWarning === true) {
|
|
62
|
-
const error =
|
|
64
|
+
const error = parentNode.createError("schema-warning", {
|
|
65
|
+
pointer,
|
|
66
|
+
value: data,
|
|
67
|
+
schema: parentNode.schema,
|
|
68
|
+
key
|
|
69
|
+
});
|
|
63
70
|
return { node: undefined, error };
|
|
64
71
|
}
|
|
65
72
|
|
|
66
|
-
|
|
67
|
-
return {};
|
|
73
|
+
return { node: undefined };
|
|
68
74
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { strict as assert } from "assert";
|
|
2
2
|
import { compileSchema } from "../compileSchema";
|
|
3
|
-
import { isJsonError } from "../types";
|
|
3
|
+
import { isJsonError, isSchemaNode } from "../types";
|
|
4
4
|
import { reduceOneOfDeclarator, reduceOneOfFuzzy } from "./oneOf";
|
|
5
5
|
import settings from "../settings";
|
|
6
6
|
import { draftEditor } from "../draftEditor";
|
|
@@ -350,33 +350,56 @@ describe("keyword : oneof-property : reduce", () => {
|
|
|
350
350
|
assert(isJsonError(res), "expected result to be an error");
|
|
351
351
|
assert.deepEqual(res.code, "missing-one-of-property-error");
|
|
352
352
|
});
|
|
353
|
-
});
|
|
354
353
|
|
|
355
|
-
|
|
356
|
-
it("should return an error if no oneOfProperty could be matched", () => {
|
|
354
|
+
it("should return correct reference", () => {
|
|
357
355
|
const node = compileSchema({
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
properties: {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
356
|
+
[DECLARATOR_ONEOF]: "name",
|
|
357
|
+
oneOf: [{ $ref: "#/$defs/first" }, { $ref: "#/$defs/second" }],
|
|
358
|
+
$defs: {
|
|
359
|
+
first: {
|
|
360
|
+
properties: { name: { type: "string", const: "first" }, title: { type: "number" } }
|
|
361
|
+
},
|
|
362
|
+
second: {
|
|
363
|
+
properties: { name: { type: "string", const: "second" }, title: { type: "number" } }
|
|
367
364
|
}
|
|
368
|
-
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
const res = reduceOneOfDeclarator({ node, data: { name: "second" }, pointer: "#", path: [] });
|
|
368
|
+
assert(isSchemaNode(res), "expected result to be a node");
|
|
369
|
+
assert.deepEqual(res.schema, {
|
|
370
|
+
properties: { name: { type: "string", const: "second" }, title: { type: "number" } }
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it("should return correct reference even if data is not fully valid", () => {
|
|
375
|
+
const node = compileSchema({
|
|
376
|
+
[DECLARATOR_ONEOF]: "name",
|
|
377
|
+
oneOf: [{ $ref: "#/$defs/first" }, { $ref: "#/$defs/second" }],
|
|
378
|
+
$defs: {
|
|
379
|
+
first: {
|
|
380
|
+
properties: { name: { type: "string", const: "first" }, title: { type: "number" } }
|
|
381
|
+
},
|
|
382
|
+
second: {
|
|
383
|
+
properties: { name: { type: "string", const: "second" }, title: { type: "number" } }
|
|
384
|
+
}
|
|
385
|
+
}
|
|
369
386
|
});
|
|
370
387
|
const res = reduceOneOfDeclarator({
|
|
371
388
|
node,
|
|
372
|
-
data: { name: "
|
|
389
|
+
data: { name: "first", title: "not a number" },
|
|
373
390
|
pointer: "#",
|
|
374
391
|
path: []
|
|
375
392
|
});
|
|
376
|
-
assert(
|
|
377
|
-
assert.deepEqual(res.
|
|
393
|
+
assert(isSchemaNode(res), "expected result to be a node");
|
|
394
|
+
assert.deepEqual(res.schema, {
|
|
395
|
+
properties: { name: { type: "string", const: "first" }, title: { type: "number" } }
|
|
396
|
+
});
|
|
378
397
|
});
|
|
379
398
|
});
|
|
399
|
+
|
|
400
|
+
describe("array", () => {
|
|
401
|
+
// TODO test access on array-item as oneOfProperty id (oneOfProperty: "0")
|
|
402
|
+
});
|
|
380
403
|
});
|
|
381
404
|
|
|
382
405
|
describe("keyword : oneof-fuzzy : validate", () => {
|
package/src/keywords/oneOf.ts
CHANGED
|
@@ -101,42 +101,57 @@ function reduceOneOf({ node, data, pointer, path }: Omit<JsonSchemaReducerParams
|
|
|
101
101
|
});
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Returns matching oneOf schema identified by matching schema for oneOfProperty
|
|
106
|
+
*/
|
|
104
107
|
export function reduceOneOfDeclarator({ node, data, pointer, path }: Omit<JsonSchemaReducerParams, "key">) {
|
|
105
108
|
if (node.oneOf == null) {
|
|
106
109
|
return;
|
|
107
110
|
}
|
|
108
111
|
|
|
109
|
-
const errors: ValidationReturnType = [];
|
|
110
112
|
const oneOfProperty = node.schema[DECLARATOR_ONEOF];
|
|
111
|
-
const
|
|
113
|
+
const oneOfPropertyValue = getValue(data, oneOfProperty);
|
|
112
114
|
|
|
113
|
-
|
|
115
|
+
// in this case, we also fail when data undefined as this always is valid,
|
|
116
|
+
// but not in context on an expected oneOfProperty
|
|
117
|
+
if (data === undefined || oneOfPropertyValue === undefined) {
|
|
114
118
|
return node.createError("missing-one-of-property-error", {
|
|
115
|
-
|
|
119
|
+
oneOfProperty,
|
|
116
120
|
pointer,
|
|
117
121
|
schema: node.schema,
|
|
118
122
|
value: data
|
|
119
123
|
});
|
|
120
124
|
}
|
|
121
125
|
|
|
126
|
+
// find oneOf schema that has a matching oneOfProperty to the current input data
|
|
127
|
+
// TODO throw an error if multiple matches were found
|
|
128
|
+
const errors: ValidationReturnType = [];
|
|
122
129
|
for (let i = 0; i < node.oneOf.length; i += 1) {
|
|
123
130
|
const { node: resultNode } = node.oneOf[i].getNodeChild(oneOfProperty, data);
|
|
124
131
|
if (!isSchemaNode(resultNode)) {
|
|
132
|
+
// one of the oneOf schemas has a missing oneOfTypeProperty
|
|
133
|
+
// TODO this still might succeed
|
|
134
|
+
// TODO there is a possibility this throws an invalid error as we use input data
|
|
125
135
|
return node.createError("missing-one-of-declarator-error", {
|
|
126
136
|
declarator: DECLARATOR_ONEOF,
|
|
127
137
|
oneOfProperty,
|
|
128
|
-
|
|
138
|
+
schemaLocation: node.oneOf[i].schemaLocation,
|
|
129
139
|
pointer: `${pointer}/oneOf/${i}`,
|
|
130
140
|
schema: node.schema,
|
|
131
141
|
value: data
|
|
132
142
|
});
|
|
133
143
|
}
|
|
134
144
|
|
|
135
|
-
|
|
136
|
-
|
|
145
|
+
// collect errors in case we fail finding a matching schema
|
|
146
|
+
const result = sanitizeErrors(
|
|
147
|
+
validateNode(resultNode, oneOfPropertyValue, `${pointer}/${oneOfProperty}`, path)
|
|
148
|
+
);
|
|
149
|
+
|
|
137
150
|
if (result.length > 0) {
|
|
138
151
|
errors.push(...result);
|
|
139
152
|
} else {
|
|
153
|
+
// return at once when we found a schema
|
|
154
|
+
// TODO should check all oneOf-schema
|
|
140
155
|
const { node: reducedNode } = node.oneOf[i].reduceNode(data, { pointer, path });
|
|
141
156
|
if (reducedNode) {
|
|
142
157
|
reducedNode.oneOfIndex = i; // @evaluation-info
|
|
@@ -147,7 +162,7 @@ export function reduceOneOfDeclarator({ node, data, pointer, path }: Omit<JsonSc
|
|
|
147
162
|
|
|
148
163
|
return node.createError("one-of-property-error", {
|
|
149
164
|
property: oneOfProperty,
|
|
150
|
-
value:
|
|
165
|
+
value: data,
|
|
151
166
|
pointer,
|
|
152
167
|
schema: node.schema,
|
|
153
168
|
errors
|
|
@@ -34,6 +34,24 @@ describe("getChildSelection", () => {
|
|
|
34
34
|
);
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
+
it("should return resolved $ref", () => {
|
|
38
|
+
const result = compileSchema({
|
|
39
|
+
type: "array",
|
|
40
|
+
prefixItems: [{ $ref: "#/$defs/first" }, { $ref: "#/$defs/second" }],
|
|
41
|
+
$defs: {
|
|
42
|
+
first: { type: "string" },
|
|
43
|
+
second: { type: "number" }
|
|
44
|
+
}
|
|
45
|
+
}).getChildSelection(1);
|
|
46
|
+
|
|
47
|
+
assert(!isJsonError(result));
|
|
48
|
+
assert.deepEqual(result.length, 1);
|
|
49
|
+
assert.deepEqual(
|
|
50
|
+
result.map((n) => n.schema),
|
|
51
|
+
[{ type: "number" }]
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
37
55
|
it("should return an empty array if items schema is undefined", () => {
|
|
38
56
|
const result = compileSchema({
|
|
39
57
|
type: "array",
|
|
@@ -5,7 +5,7 @@ import { isSchemaNode, JsonError, SchemaNode } from "../types";
|
|
|
5
5
|
* could be added at the given property (e.g. item-index), thus an array of options is returned. In all other cases
|
|
6
6
|
* a list with a single item will be returned
|
|
7
7
|
*/
|
|
8
|
-
export function getChildSelection(node: SchemaNode, property: string | number)
|
|
8
|
+
export function getChildSelection(node: SchemaNode, property: string | number) {
|
|
9
9
|
if (node.oneOf) {
|
|
10
10
|
return node.oneOf.map((childNode: SchemaNode) => childNode.resolveRef());
|
|
11
11
|
}
|
|
File without changes
|
|
File without changes
|