Skip to content

Commit e3b3562

Browse files
committed
Allow resolver errors
1 parent c0cc8bf commit e3b3562

5 files changed

Lines changed: 53 additions & 59 deletions

File tree

lib/parse.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,15 @@ function readFile (file, options, $refs) {
8080
function onError (err) {
8181
// Throw the original error, if it's one of our own (user-friendly) errors.
8282
// Otherwise, throw a generic, friendly error.
83-
if (err && !(err instanceof SyntaxError)) {
83+
if (!err || !(err instanceof SyntaxError)) {
84+
reject(ono.syntax(`Unable to resolve $ref pointer "${file.url}"`));
85+
}
86+
else if (err.error instanceof ResolverError) {
8487
reject(err);
8588
}
8689
else {
87-
reject(ono.syntax(`Unable to resolve $ref pointer "${file.url}"`));
90+
err.error = new ResolverError(err, file.url);
91+
reject(err);
8892
}
8993
}
9094
}));

lib/resolvers/file.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
const fs = require("fs");
33
const { ono } = require("ono");
44
const url = require("../util/url");
5+
const { ResolverError } = require("../util/errors");
56

67
module.exports = {
78
/**
@@ -40,23 +41,23 @@ module.exports = {
4041
path = url.toFileSystemPath(file.url);
4142
}
4243
catch (err) {
43-
reject(ono.uri(err, `Malformed URI: ${file.url}`));
44+
reject(new ResolverError(ono.uri(err, `Malformed URI: ${file.url}`), file.url));
4445
}
4546

4647
// console.log('Opening file: %s', path);
4748

4849
try {
4950
fs.readFile(path, (err, data) => {
5051
if (err) {
51-
reject(ono(err, `Error opening file "${path}"`));
52+
reject(new ResolverError(ono(err, `Error opening file "${path}"`), path));
5253
}
5354
else {
5455
resolve(data);
5556
}
5657
});
5758
}
5859
catch (err) {
59-
reject(ono(err, `Error opening file "${path}"`));
60+
reject(new ResolverError(ono(err, `Error opening file "${path}"`), path));
6061
}
6162
}));
6263
}

lib/resolvers/http.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const http = require("http");
44
const https = require("https");
55
const { ono } = require("ono");
66
const url = require("../util/url");
7+
const { ResolverError } = require("../util/errors");
78

89
module.exports = {
910
/**
@@ -106,8 +107,8 @@ function download (u, httpOptions, redirects) {
106107
}
107108
else if (res.statusCode >= 300) {
108109
if (redirects.length > httpOptions.redirects) {
109-
reject(ono({ status: res.statusCode },
110-
`Error downloading ${redirects[0]}. \nToo many redirects: \n ${redirects.join(" \n ")}`));
110+
reject(new ResolverError(ono({ status: res.statusCode },
111+
`Error downloading ${redirects[0]}. \nToo many redirects: \n ${redirects.join(" \n ")}`)));
111112
}
112113
else if (!res.headers.location) {
113114
throw ono({ status: res.statusCode }, `HTTP ${res.statusCode} redirect with no location header`);
@@ -123,7 +124,7 @@ function download (u, httpOptions, redirects) {
123124
}
124125
})
125126
.catch((err) => {
126-
reject(ono(err, `Error downloading ${u.href}`));
127+
reject(new ResolverError(ono(err, `Error downloading ${u.href}`), u.href));
127128
});
128129
}));
129130
}

test/specs/invalid/invalid.spec.js

Lines changed: 17 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const { expect } = chai;
88
const $RefParser = require("../../../lib");
99
const helper = require("../../utils/helper");
1010
const path = require("../../utils/path");
11-
const { StoplightParserError, ParserError } = require("../../../lib/util/errors");
11+
const { StoplightParserError, ParserError, ResolverError } = require("../../../lib/util/errors");
1212

1313
describe("Invalid syntax", () => {
1414
describe("in main file", () => {
@@ -18,7 +18,7 @@ describe("Invalid syntax", () => {
1818
helper.shouldNotGetCalled();
1919
}
2020
catch (err) {
21-
expect(err).to.be.an.instanceOf(Error);
21+
expect(err).to.be.an.instanceOf(ResolverError);
2222
if (host.node) {
2323
expect(err.code).to.equal("ENOENT");
2424
expect(err.message).to.contain("Error opening file ");
@@ -74,6 +74,21 @@ describe("Invalid syntax", () => {
7474
});
7575

7676
describe("when failFast is false", () => {
77+
it("should not throw an error for an invalid file path", async () => {
78+
const parser = new $RefParser();
79+
const result = await parser.dereference("this file does not exist", { failFast: false });
80+
expect(result).to.be.null;
81+
expect(parser.errors.length).to.equal(1);
82+
expect(parser.errors).to.containSubset([
83+
{
84+
name: ResolverError.name,
85+
message: expectedValue => expectedValue.startsWith("Error opening file"),
86+
path: [],
87+
source: expectedValue => expectedValue.endsWith("/test/this file does not exist"),
88+
}
89+
]);
90+
});
91+
7792
it("should not throw an error for an invalid YAML file", async () => {
7893
const parser = new $RefParser();
7994
const result = await parser.dereference(path.rel("specs/invalid/invalid.yaml"), { failFast: false });
@@ -178,54 +193,5 @@ describe("Invalid syntax", () => {
178193
foo: ":\n"
179194
});
180195
});
181-
182-
describe("when failFast is false", () => {
183-
it("should not throw an error for an invalid YAML file", async () => {
184-
const parser = new $RefParser();
185-
const result = await parser.dereference({ foo: { $ref: path.rel("specs/invalid/invalid.yaml") }}, { failFast: false });
186-
expect(parser.errors.length).to.equal(1);
187-
expect(parser.errors).to.containSubset([
188-
{
189-
name: ParserError.name,
190-
message: "incomplete explicit mapping pair; a key node is missed",
191-
path: ["foo"],
192-
source: expectedValue => expectedValue.endsWith("/test/"),
193-
},
194-
]);
195-
});
196-
197-
it("should not throw an error for an invalid JSON file", async () => {
198-
const parser = new $RefParser();
199-
const result = await parser.dereference({ foo: { $ref: path.rel("specs/invalid/invalid.json") }}, { failFast: false });
200-
expect(parser.errors).to.containSubset([
201-
{
202-
name: ParserError.name,
203-
message: "unexpected end of the stream within a flow collection",
204-
path: ["foo"],
205-
source: expectedValue => expectedValue.endsWith("/test/"),
206-
}
207-
]);
208-
});
209-
210-
it("should not throw an error for an invalid JSON file with YAML disabled", async () => {
211-
const parser = new $RefParser();
212-
const result = await parser.dereference({ foo: { $ref: path.rel("specs/invalid/invalid.json") }}, { failFast: false, parse: { yaml: false }});
213-
expect(parser.errors).to.containSubset([
214-
{
215-
name: ParserError.name,
216-
message: "CloseBraceExpected",
217-
path: ["foo"],
218-
source: expectedValue => expectedValue.endsWith("/test/"),
219-
}
220-
]);
221-
});
222-
223-
it("should not throw an error for an invalid YAML file with JSON and YAML disabled", async () => {
224-
const parser = new $RefParser();
225-
const result = await parser.dereference({ foo: { $ref: path.rel("specs/invalid/invalid.yaml") }}, { failFast: false, parse: { yaml: false, json: false }});
226-
expect(result).to.deep.equal({ foo: ":\n" });
227-
expect(parser.errors).to.deep.equal([]);
228-
});
229-
});
230196
});
231197
});

test/specs/resolvers/resolvers.spec.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const helper = require("../../utils/helper");
66
const path = require("../../utils/path");
77
const parsedSchema = require("./parsed");
88
const dereferencedSchema = require("./dereferenced");
9+
const { ResolverError } = require("../../../lib/util/errors");
910

1011
describe("options.resolve", () => {
1112
it('should not resolve external links if "resolve.external" is disabled', async () => {
@@ -111,4 +112,25 @@ describe("options.resolve", () => {
111112
expect(schema).to.deep.equal(dereferencedSchema);
112113
});
113114

115+
it("should normalize errors thrown by resolvers", async () => {
116+
try {
117+
await $RefParser.dereference({ $ref: path.abs("specs/resolvers/resolvers.yaml") }, {
118+
resolve: {
119+
// A custom resolver that always fails
120+
file: {
121+
order: 1,
122+
canRead: true,
123+
parse () {
124+
throw new Error("Woops");
125+
}
126+
}
127+
}
128+
});
129+
helper.shouldNotGetCalled();
130+
}
131+
catch (err) {
132+
expect(err).to.be.instanceof(ResolverError);
133+
expect(err.message).to.contain("Error opening file");
134+
}
135+
});
114136
});

0 commit comments

Comments
 (0)