Skip to content

Commit ccbd40d

Browse files
committed
Resolved might be null
1 parent b5d9e22 commit ccbd40d

File tree

5 files changed

+79
-21
lines changed

5 files changed

+79
-21
lines changed

Diff for: lib/pointer.js

+4
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ function resolveIf$Ref (pointer, options) {
227227
}
228228
else {
229229
let resolved = pointer.$ref.$refs._resolve($refPath, url.getHash(pointer.path), options);
230+
if (resolved === null) {
231+
return false;
232+
}
233+
230234
pointer.indirections += resolved.indirections + 1;
231235

232236
if ($Ref.isExtended$Ref(pointer.value)) {

Diff for: lib/ref.js

+11-6
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,30 @@ function $Ref () {
5353
/**
5454
* Pushes an error to errors array.
5555
*
56-
* @param {Array<JSONParserError | JSONParserErrorGroup>} error - The error to be pushed
56+
* @param {Array<JSONParserError | JSONParserErrorGroup>} err - The error to be pushed
5757
* @returns {void}
5858
*/
5959
$Ref.prototype.addError = function (err) {
6060
if (this.errors === undefined) {
6161
this.errors = [];
6262
}
6363

64+
const existingErrors = this.errors.map(({ footprint }) => footprint);
65+
6466
// the path has been almost certainly set at this point,
65-
// but just in case something went wrong, let's inject path if necessary
67+
// but just in case something went wrong, normalizeError injects path if necessary
68+
// moreover, certain errors might point at the same spot, so filter them out to reduce noise
6669
if (Array.isArray(err.errors)) {
67-
this.errors.push(...err.errors.map(normalizeError));
70+
this.errors.push(...err.errors
71+
.map(normalizeError)
72+
.filter(({ footprint }) => !existingErrors.includes(footprint)),
73+
);
6874
}
69-
else {
75+
else if (!existingErrors.includes(err.footprint)) {
7076
this.errors.push(normalizeError(err));
7177
}
7278
};
7379

74-
7580
/**
7681
* Determines whether the given JSON reference exists within this {@link $Ref#value}.
7782
*
@@ -107,7 +112,7 @@ $Ref.prototype.get = function (path, options) {
107112
* @param {$RefParserOptions} options
108113
* @param {string} friendlyPath - The original user-specified path (used for error messages)
109114
* @param {string} pathFromRoot - The path of `obj` from the schema root
110-
* @returns {Pointer}
115+
* @returns {Pointer | null}
111116
*/
112117
$Ref.prototype.resolve = function (path, options, friendlyPath, pathFromRoot) {
113118
let pointer = new Pointer(this, path, friendlyPath);

Diff for: lib/util/errors.js

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ const JSONParserError = exports.JSONParserError = class JSONParserError extends
1515

1616
Ono.extend(this);
1717
}
18+
19+
get footprint () {
20+
return `${this.path}+${this.source}+${this.code}+${this.message}`;
21+
}
1822
};
1923

2024
setErrorName(JSONParserError);
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
internal1:
2+
$ref: '#/internal2'
3+
internal2:
4+
$ref: '#/external'
5+

Diff for: test/specs/missing-pointers/missing-pointers.spec.js

+55-15
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,65 @@ describe("Schema with missing pointers", () => {
2121
}
2222
});
2323

24-
it("should throw a grouped error for missing pointer if continueOnError is true", async () => {
25-
const parser = new $RefParser();
24+
it("should throw an error for missing pointer in external file", async () => {
2625
try {
27-
await parser.dereference({ foo: { $ref: "#/baz" }}, { continueOnError: true });
26+
await $RefParser.dereference({ foo: { $ref: path.abs("specs/missing-pointers/external-from-internal.yaml") }});
2827
helper.shouldNotGetCalled();
2928
}
3029
catch (err) {
31-
expect(err).to.be.instanceof(JSONParserErrorGroup);
32-
expect(err.files).to.equal(parser);
33-
expect(err.files.$refs._root$Ref.value).to.deep.equal({ foo: null });
34-
expect(err.message).to.have.string("1 error occurred while reading '");
35-
expect(err.errors).to.containSubset([
36-
{
37-
name: MissingPointerError.name,
38-
message: "Token \"baz\" does not exist.",
39-
path: ["foo"],
40-
source: message => message.endsWith("/test/") || message.startsWith("http://localhost"),
41-
}
42-
]);
30+
expect(err).to.be.an.instanceOf(MissingPointerError);
31+
expect(err.message).to.contain("Token \"external\" does not exist.");
4332
}
4433
});
34+
35+
context("when continueOnError is true", () => {
36+
it("should throw a grouped error for missing pointer", async () => {
37+
const parser = new $RefParser();
38+
try {
39+
await parser.dereference({ foo: { $ref: "#/baz" }}, { continueOnError: true });
40+
helper.shouldNotGetCalled();
41+
}
42+
catch (err) {
43+
expect(err).to.be.instanceof(JSONParserErrorGroup);
44+
expect(err.files).to.equal(parser);
45+
expect(err.files.$refs._root$Ref.value).to.deep.equal({ foo: null });
46+
expect(err.message).to.have.string("1 error occurred while reading '");
47+
expect(err.errors).to.containSubset([
48+
{
49+
name: MissingPointerError.name,
50+
message: "Token \"baz\" does not exist.",
51+
path: ["foo"],
52+
source: message => message.endsWith("/test/") || message.startsWith("http://localhost"),
53+
}
54+
]);
55+
}
56+
});
57+
58+
it("should throw an error for missing pointer in external file", async () => {
59+
const parser = new $RefParser();
60+
try {
61+
await parser.dereference({ foo: { $ref: path.abs("specs/missing-pointers/external-from-internal.yaml") }}, { continueOnError: true });
62+
helper.shouldNotGetCalled();
63+
}
64+
catch (err) {
65+
expect(err).to.be.instanceof(JSONParserErrorGroup);
66+
expect(err.files).to.equal(parser);
67+
expect(err.files.$refs._root$Ref.value).to.deep.equal({
68+
foo: {
69+
internal1: null,
70+
internal2: null,
71+
}
72+
});
73+
expect(err.message).to.have.string("1 error occurred while reading '");
74+
expect(err.errors).to.containSubset([
75+
{
76+
name: MissingPointerError.name,
77+
message: "Token \"external\" does not exist.",
78+
path: ["internal2"],
79+
source: message => message.endsWith("missing-pointers/external-from-internal.yaml") || message.startsWith("http://localhost"),
80+
}
81+
]);
82+
}
83+
});
84+
});
4585
});

0 commit comments

Comments
 (0)