Skip to content

Commit 7828694

Browse files
committed
fix: issue #82
1 parent c4ae891 commit 7828694

File tree

11 files changed

+161
-101
lines changed

11 files changed

+161
-101
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,13 @@
8282
"watch": "^1.0.1"
8383
},
8484
"dependencies": {
85-
"@sagold/json-pointer": "^7.0.0",
85+
"@sagold/json-pointer": "^7.2.0",
8686
"@sagold/json-query": "^6.2.0",
8787
"deepmerge": "^4.3.1",
8888
"fast-copy": "^3.0.2",
8989
"fast-deep-equal": "^3.1.3",
9090
"smtp-address-parser": "1.0.10",
91+
"uri-js": "^4.4.1",
9192
"valid-url": "^1.0.9"
9293
},
9394
"resolutions": {

src/SchemaNode.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { toSchemaNodes } from "./methods/toSchemaNodes";
88
import { isJsonError, JsonSchema, JsonError, ErrorData, DefaultErrors, OptionalNodeOrError } from "./types";
99
import { isObject } from "./utils/isObject";
1010
import { join } from "@sagold/json-pointer";
11-
import { joinId } from "./utils/joinId";
11+
import { resolveUri } from "./utils/resolveUri";
1212
import { mergeNode } from "./mergeNode";
1313
import { omit } from "./utils/omit";
1414
import { pick } from "./utils/pick";
@@ -397,7 +397,7 @@ export const SchemaNodeMethods = {
397397
*/
398398
addRemoteSchema(url: string, schema: JsonSchema): SchemaNode {
399399
// @draft >= 6
400-
schema.$id = joinId(schema.$id || url);
400+
schema.$id = resolveUri(schema.$id || url);
401401
const { context } = this as SchemaNode;
402402
const draft = getDraft(context.drafts, schema?.$schema ?? this.context.rootNode.$schema);
403403

@@ -420,7 +420,7 @@ export const SchemaNodeMethods = {
420420
} as SchemaNode;
421421

422422
node.context.rootNode = node;
423-
node.context.remotes[joinId(url)] = node;
423+
node.context.remotes[resolveUri(url)] = node;
424424
addKeywords(node);
425425

426426
return this;

src/draft04/keywords/$ref.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Keyword, ValidationPath } from "../../Keyword";
2-
import { joinId } from "../../utils/joinId";
2+
import { resolveUri } from "../../utils/resolveUri";
33
import { isObject } from "../../utils/isObject";
44
import { omit } from "../../utils/omit";
55
import splitRef from "../../utils/splitRef";
@@ -24,7 +24,7 @@ function parseRef(node: SchemaNode) {
2424
// get and store current id of node - this may be the same as parent id
2525
let currentId = node.parent?.$id;
2626
if (node.schema?.$ref == null && node.schema?.id) {
27-
currentId = joinId(node.parent?.$id, node.schema.id);
27+
currentId = resolveUri(node.parent?.$id, node.schema.id);
2828
// console.log("create id", node.evaluationPath, ":", node.parent?.$id, node.schema?.id, "=>", currentId);
2929
}
3030
node.$id = currentId;
@@ -46,15 +46,15 @@ function parseRef(node: SchemaNode) {
4646
// store this node for retrieval by id + json-pointer from id
4747
if (node.lastIdPointer !== "#" && node.evaluationPath.startsWith(node.lastIdPointer)) {
4848
const localPointer = `#${node.evaluationPath.replace(node.lastIdPointer, "")}`;
49-
register(node, joinId(currentId, localPointer));
49+
register(node, resolveUri(currentId, localPointer));
5050
} else {
51-
register(node, joinId(currentId, node.evaluationPath));
51+
register(node, resolveUri(currentId, node.evaluationPath));
5252
}
53-
register(node, joinId(node.context.rootNode.$id, node.evaluationPath));
53+
register(node, resolveUri(node.context.rootNode.$id, node.evaluationPath));
5454

5555
// precompile reference
5656
if (node.schema.$ref) {
57-
node.$ref = joinId(currentId, node.schema.$ref);
57+
node.$ref = resolveUri(currentId, node.schema.$ref);
5858
}
5959
}
6060

src/draft06/keywords/$ref.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Keyword, JsonSchemaValidatorParams, ValidationPath } from "../../Keyword";
22
import { resolveRef } from "../../keywords/$ref";
33
import { SchemaNode } from "../../types";
4-
import { joinId } from "../../utils/joinId";
4+
import { resolveUri } from "../../utils/resolveUri";
55
import { validateNode } from "../../validateNode";
66

77
export const $refKeyword: Keyword = {
@@ -16,7 +16,7 @@ function parseRef(node: SchemaNode) {
1616
// get and store current $id of node - this may be the same as parent $id
1717
let currentId = node.parent?.$id;
1818
if (node.schema?.$ref == null) {
19-
currentId = joinId(node.parent?.$id, node.schema?.$id);
19+
currentId = resolveUri(node.parent?.$id, node.schema?.$id);
2020
}
2121
node.$id = currentId;
2222
node.lastIdPointer = node.parent?.lastIdPointer ?? "#";
@@ -37,15 +37,15 @@ function parseRef(node: SchemaNode) {
3737
// store this node for retrieval by $id + json-pointer from $id
3838
if (node.lastIdPointer !== "#" && node.evaluationPath.startsWith(node.lastIdPointer)) {
3939
const localPointer = `#${node.evaluationPath.replace(node.lastIdPointer, "")}`;
40-
node.context.refs[joinId(currentId, localPointer)] = node;
40+
node.context.refs[resolveUri(currentId, localPointer)] = node;
4141
} else {
42-
node.context.refs[joinId(currentId, node.evaluationPath)] = node;
42+
node.context.refs[resolveUri(currentId, node.evaluationPath)] = node;
4343
}
44-
node.context.refs[joinId(node.context.rootNode.$id, node.evaluationPath)] = node;
44+
node.context.refs[resolveUri(node.context.rootNode.$id, node.evaluationPath)] = node;
4545

4646
// precompile reference
4747
if (node.schema.$ref) {
48-
node.$ref = joinId(currentId, node.schema.$ref);
48+
node.$ref = resolveUri(currentId, node.schema.$ref);
4949
}
5050
}
5151

src/draft2019-09/keywords/$ref.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Keyword, JsonSchemaValidatorParams, ValidationPath } from "../../Keyword";
2-
import { joinId } from "../../utils/joinId";
2+
import { resolveUri } from "../../utils/resolveUri";
33
import splitRef from "../../utils/splitRef";
44
import { omit } from "../../utils/omit";
55
import { isObject } from "../../utils/isObject";
@@ -29,7 +29,7 @@ export function parseRef(node: SchemaNode) {
2929
node.resolveRef = resolveRef;
3030

3131
// get and store current $id of node - this may be the same as parent $id
32-
const currentId = joinId(node.parent?.$id, node.schema?.$id);
32+
const currentId = resolveUri(node.parent?.$id, node.schema?.$id);
3333
node.$id = currentId;
3434
node.lastIdPointer = node.parent?.lastIdPointer ?? "#";
3535
if (currentId !== node.parent?.$id && node.evaluationPath !== "#") {
@@ -39,18 +39,19 @@ export function parseRef(node: SchemaNode) {
3939
// store this node for retrieval by $id + json-pointer from $id
4040
if (node.lastIdPointer !== "#" && node.evaluationPath.startsWith(node.lastIdPointer)) {
4141
const localPointer = `#${node.evaluationPath.replace(node.lastIdPointer, "")}`;
42-
register(node, joinId(currentId, localPointer));
42+
register(node, resolveUri(currentId, localPointer));
4343
}
4444
// store $rootId + json-pointer to this node
45-
register(node, joinId(node.context.rootNode.$id, node.evaluationPath));
45+
register(node, resolveUri(node.context.rootNode.$id, node.evaluationPath));
46+
4647
// store this node for retrieval by $id + anchor
4748
if (node.schema.$anchor) {
4849
node.context.anchors[`${currentId.replace(/#$/, "")}#${node.schema.$anchor}`] = node;
4950
}
5051

5152
// precompile reference
5253
if (node.schema.$ref) {
53-
node.$ref = joinId(currentId, node.schema.$ref);
54+
node.$ref = resolveUri(currentId, node.schema.$ref);
5455
if (node.$ref.startsWith("/")) {
5556
node.$ref = `#${node.$ref}`;
5657
}
@@ -108,7 +109,7 @@ function resolveRecursiveRef(node: SchemaNode, path: ValidationPath): SchemaNode
108109
for (let i = history.length - 1; i >= 0; i--) {
109110
if (history[i].node.schema.$recursiveAnchor === false) {
110111
// $recursiveRef with $recursiveAnchor: false works like $ref
111-
const nextNode = getRef(node, joinId(node.$id, node.schema.$recursiveRef));
112+
const nextNode = getRef(node, resolveUri(node.$id, node.schema.$recursiveRef));
112113
return nextNode;
113114
}
114115
if (/^https?:\/\//.test(history[i].node.schema.$id ?? "") && history[i].node.schema.$recursiveAnchor !== true) {
@@ -124,7 +125,7 @@ function resolveRecursiveRef(node: SchemaNode, path: ValidationPath): SchemaNode
124125
}
125126

126127
// $recursiveRef with no $recursiveAnchor works like $ref?
127-
const nextNode = getRef(node, joinId(node.$id, node.schema.$recursiveRef));
128+
const nextNode = getRef(node, resolveUri(node.$id, node.schema.$recursiveRef));
128129
return nextNode;
129130
}
130131

src/keywords/$ref.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { SchemaNode } from "../types";
22
import { Keyword, JsonSchemaValidatorParams, ValidationPath, JsonSchemaReducerParams } from "../Keyword";
3-
import { joinId } from "../utils/joinId";
3+
import { resolveUri } from "../utils/resolveUri";
44
import splitRef from "../utils/splitRef";
55
import { omit } from "../utils/omit";
66
import { isObject } from "../utils/isObject";
@@ -31,7 +31,7 @@ export function parseRef(node: SchemaNode) {
3131
node.resolveRef = resolveRef;
3232

3333
// get and store current $id of node - this may be the same as parent $id
34-
const currentId = joinId(node.parent?.$id, node.schema?.$id);
34+
const currentId = resolveUri(node.parent?.$id, node.schema?.$id);
3535
node.$id = currentId;
3636
node.lastIdPointer = node.parent?.lastIdPointer ?? "#";
3737
if (currentId !== node.parent?.$id && node.evaluationPath !== "#") {
@@ -41,10 +41,10 @@ export function parseRef(node: SchemaNode) {
4141
// store this node for retrieval by $id + json-pointer from $id
4242
if (node.lastIdPointer !== "#" && node.evaluationPath.startsWith(node.lastIdPointer)) {
4343
const localPointer = `#${node.evaluationPath.replace(node.lastIdPointer, "")}`;
44-
register(node, joinId(currentId, localPointer));
44+
register(node, resolveUri(currentId, localPointer));
4545
}
4646
// store $rootId + json-pointer to this node
47-
register(node, joinId(node.context.rootNode.$id, node.evaluationPath));
47+
register(node, resolveUri(node.context.rootNode.$id, node.evaluationPath));
4848

4949
// @draft-2020: A $dynamicRef to a $dynamicAnchor in the same schema resource behaves like a normal $ref to an $anchor
5050
const anchor = node.schema.$anchor;
@@ -67,7 +67,7 @@ export function parseRef(node: SchemaNode) {
6767

6868
// precompile reference
6969
if (node.schema.$ref) {
70-
node.$ref = joinId(currentId, node.schema.$ref);
70+
node.$ref = resolveUri(currentId, node.schema.$ref);
7171
if (node.$ref.startsWith("/")) {
7272
node.$ref = `#${node.$ref}`;
7373
}
@@ -124,7 +124,7 @@ function validateRef({ node, data, pointer = "#", path }: JsonSchemaValidatorPar
124124
// 1. https://json-schema.org/draft/2019-09/json-schema-core#scopes
125125
function resolveRecursiveRef(node: SchemaNode, path: ValidationPath): SchemaNode {
126126
const history = path;
127-
const refInCurrentScope = joinId(node.$id, node.schema.$dynamicRef);
127+
const refInCurrentScope = resolveUri(node.$id, node.schema.$dynamicRef);
128128

129129
// A $dynamicRef with a non-matching $dynamicAnchor in the same schema resource behaves like a normal $ref to $anchor
130130
const nonMatchingDynamicAnchor = node.context.dynamicAnchors[refInCurrentScope] == null;
@@ -142,7 +142,7 @@ function resolveRecursiveRef(node: SchemaNode, path: ValidationPath): SchemaNode
142142

143143
// A $dynamicRef only stops at a $dynamicAnchor if it is in the same dynamic scope.
144144
const refWithoutScope = node.schema.$dynamicRef.split("#").pop();
145-
const ref = joinId(history[i].node.$id, `#${refWithoutScope}`);
145+
const ref = resolveUri(history[i].node.$id, `#${refWithoutScope}`);
146146
const anchorNode = node.context.dynamicAnchors[ref];
147147
if (anchorNode) {
148148
return compileNext(node.context.dynamicAnchors[ref], node);

src/tests/issues/issue82.test.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { compileSchema } from "../../compileSchema";
44
// @todo investigate different requirements on joining url and id
55
// here json-schemer://schema + stock-assembly is joined to json-schemer://stock-assembly
66
// in joinId: const trailingFragments = /\/\/*$/; will fix this issue but fail spec tests
7-
describe.skip("issue#82", () => {
7+
describe("issue#82", () => {
88
it("should return validation errors for number", () => {
99
const schema = compileSchema({
1010
$schema: "https://json-schema.org/draft/2020-12/schema",
@@ -15,7 +15,7 @@ describe.skip("issue#82", () => {
1515
$ref: "stock-assembly"
1616
}
1717
},
18-
$id: "json-schemer://schema", // this works only if a trailing slash
18+
$id: "json-schemer://schema", // this works only for a trailing slash
1919
$defs: {
2020
"json-schemer://schema/stock-assembly": {
2121
$schema: "http://json-schema.org/draft-07/schema#",
@@ -35,6 +35,44 @@ describe.skip("issue#82", () => {
3535
"stock-assembly": 1
3636
});
3737

38+
console.log(errors);
39+
40+
assert.equal(valid, false);
41+
});
42+
43+
it("should return validation error for initial example", () => {
44+
const schema = compileSchema({
45+
$schema: "https://json-schema.org/draft/2020-12/schema",
46+
type: "object",
47+
properties: {
48+
"1c_list_Document_СборкаЗапасов": {
49+
$ref: "1c_list_Document_%D0%A1%D0%B1%D0%BE%D1%80%D0%BA%D0%B0%D0%97%D0%B0%D0%BF%D0%B0%D1%81%D0%BE%D0%B2"
50+
}
51+
},
52+
required: ["1c_list_Document_СборкаЗапасов"],
53+
$id: "json-schemer://schema",
54+
$defs: {
55+
"json-schemer://schema/1c_list_Document_%D0%A1%D0%B1%D0%BE%D1%80%D0%BA%D0%B0%D0%97%D0%B0%D0%BF%D0%B0%D1%81%D0%BE%D0%B2":
56+
{
57+
$schema: "http://json-schema.org/draft-07/schema#",
58+
type: "object",
59+
properties: {
60+
$filter: {
61+
type: "string",
62+
title: "Фильтр"
63+
}
64+
},
65+
$id: "json-schemer://schema/1c_list_Document_%D0%A1%D0%B1%D0%BE%D1%80%D0%BA%D0%B0%D0%97%D0%B0%D0%BF%D0%B0%D1%81%D0%BE%D0%B2"
66+
}
67+
}
68+
});
69+
70+
const { valid, errors } = schema.validate({
71+
"1c_list_Document_СборкаЗапасов": 1
72+
});
73+
74+
console.log(errors);
75+
3876
assert.equal(valid, false);
3977
});
4078
});

src/utils/joinId.ts

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)