Skip to content

convert relative refs when resolving an external schema #47

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ jobs:
node-version: << parameters.node-version >>.0
install-yarn: false
install-npm: true
npm-version: "9.7.1"
- install
- test-node

Expand Down
32 changes: 16 additions & 16 deletions .github/workflows/CI-CD.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ jobs:
- macos-latest
- windows-latest
node:
- 10
- 12
- 14
- 16

steps:
- name: Checkout source
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Install Node ${{ matrix.node }}
uses: actions/setup-node@v1
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}

Expand All @@ -53,30 +53,30 @@ jobs:
run: npm run coverage:node

- name: Send code coverage results to Coveralls
uses: coverallsapp/github-action@v1.0.1
uses: coverallsapp/github-action@v1.1.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel: true

browser_tests:
name: Browser Tests
runs-on: ${{ matrix.os }}
timeout-minutes: 10
timeout-minutes: 15
strategy:
fail-fast: true
matrix:
os:
- ubuntu-latest # Chrome, Firefox, Safari (via SauceLabs), Edge (via SauceLabs)
- windows-latest # Internet Explorer
- ubuntu-latest # Chrome, Firefox
- windows-latest # Internet Explorer

steps:
- name: Checkout source
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Install Node
uses: actions/setup-node@v1
uses: actions/setup-node@v3
with:
node-version: 12
node-version: 16

- name: Install dependencies
run: npm ci
Expand All @@ -94,21 +94,21 @@ jobs:
cat coverage/*/lcov.info > ./coverage/lcov.info
- name: Send code coverage results to Coveralls
uses: coverallsapp/github-action@v1.0.1
uses: coverallsapp/github-action@v1.1.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel: true

coverage:
name: Code Coverage
runs-on: ubuntu-latest
timeout-minutes: 10
timeout-minutes: 5
needs:
- node_tests
- browser_tests
steps:
- name: Let Coveralls know that all tests have finished
uses: coverallsapp/github-action@v1.0.1
uses: coverallsapp/github-action@v1.1.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true
Expand All @@ -124,10 +124,10 @@ jobs:

steps:
- name: Checkout source
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Install Node
uses: actions/setup-node@v1
uses: actions/setup-node@v3

- name: Install dependencies
run: npm ci
Expand Down
42 changes: 31 additions & 11 deletions lib/resolve-external.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ function resolveExternal (parser, options) {

try {
// console.log('Resolving $ref pointers in %s', parser.$refs._root$Ref.path);
let promises = crawl(parser.schema, parser.$refs._root$Ref.path + "#", parser.$refs, options);
let promises = crawl(
parser.schema,
parser.$refs._root$Ref.path + "#",
parser.$refs,
options
);
return Promise.all(promises);
}
catch (e) {
Expand All @@ -44,31 +49,46 @@ function resolveExternal (parser, options) {
* @param {string} path - The full path of `obj`, possibly with a JSON Pointer in the hash
* @param {$Refs} $refs
* @param {$RefParserOptions} options
* @param {Set} seen - Internal.
*
* @returns {Promise[]}
* Returns an array of promises. There will be one promise for each JSON reference in `obj`.
* If `obj` does not contain any JSON references, then the array will be empty.
* If any of the JSON references point to files that contain additional JSON references,
* then the corresponding promise will internally reference an array of promises.
*/
function crawl (obj, path, $refs, options) {
function crawl (obj, path, $refs, options, external, seen) {
seen = seen || new Set();
let promises = [];

if (obj && typeof obj === "object" && !ArrayBuffer.isView(obj)) {
if (
obj &&
typeof obj === "object" &&
!ArrayBuffer.isView(obj) &&
!seen.has(obj)
) {
seen.add(obj); // Track previously seen objects to avoid infinite recursion
if ($Ref.isExternal$Ref(obj)) {
promises.push(resolve$Ref(obj, path, $refs, options));
}
else {
if (external && $Ref.is$Ref(obj)) {
/* Correct the reference in the external document so we can resolve it */
let withoutHash = url.stripHash(path);
if (url.isFileSystemPath(withoutHash)) {
/* remove file:// from path */
withoutHash = url.toFileSystemPath(withoutHash);
}
obj.$ref = withoutHash + obj.$ref;
}

for (let key of Object.keys(obj)) {
let keyPath = Pointer.join(path, key);
let value = obj[key];

if ($Ref.isExternal$Ref(value)) {
promises.push(resolve$Ref(value, keyPath, $refs, options));
}
else {
promises = promises.concat(crawl(value, keyPath, $refs, options));
}
promises = promises.concat(
crawl(value, keyPath, $refs, options, external, seen)
);
}
}
}
Expand Down Expand Up @@ -107,7 +127,7 @@ async function resolve$Ref ($ref, path, $refs, options) {

// Crawl the parsed value
// console.log('Resolving $ref pointers in %s', withoutHash);
let promises = crawl(result, withoutHash + "#", $refs, options);
let promises = crawl(result, withoutHash + "#", $refs, options, true);

return Promise.all(promises);
}
Expand All @@ -117,7 +137,7 @@ async function resolve$Ref ($ref, path, $refs, options) {
}

if ($refs._$refs[withoutHash]) {
err.source = url.stripHash(path);
err.source = decodeURI(url.stripHash(path));
err.path = url.safePointerToPath(url.getHash(path));
}

Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 44 additions & 21 deletions test/specs/absolute-root/absolute-root.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,44 +47,65 @@ describe("When executed in the context of root directory", () => {
process.cwd = originalProcessCwd; // already restored by the finally block above, but just in case
});


it("should parse successfully from an absolute path", async () => {
let parser = new $RefParser();
const schema = await parser.parse(path.abs("specs/absolute-root/absolute-root.yaml"));
const schema = await parser.parse(
path.abs("specs/absolute-root/absolute-root.yaml")
);
expect(schema).to.equal(parser.schema);
expect(schema).to.deep.equal(parsedSchema.schema);
expect(parser.$refs.paths()).to.deep.equal([
path.abs("specs/absolute-root/absolute-root.yaml")
path.abs("specs/absolute-root/absolute-root.yaml"),
]);
});

it("should parse successfully from a url", async () => {
let parser = new $RefParser();
const schema = await parser.parse(path.url("specs/absolute-root/absolute-root.yaml"));
const schema = await parser.parse(
path.url("specs/absolute-root/absolute-root.yaml")
);
expect(schema).to.equal(parser.schema);
expect(schema).to.deep.equal(parsedSchema.schema);
expect(parser.$refs.paths()).to.deep.equal([path.url("specs/absolute-root/absolute-root.yaml")]);
expect(parser.$refs.paths()).to.deep.equal([
path.url("specs/absolute-root/absolute-root.yaml"),
]);
});

it("should resolve successfully from an absolute path", helper.testResolve(
path.abs("specs/absolute-root/absolute-root.yaml"),
path.abs("specs/absolute-root/absolute-root.yaml"), parsedSchema.schema,
path.abs("specs/absolute-root/definitions/definitions.json"), parsedSchema.definitions,
path.abs("specs/absolute-root/definitions/name.yaml"), parsedSchema.name,
path.abs("specs/absolute-root/definitions/required-string.yaml"), parsedSchema.requiredString
));
it(
"should resolve successfully from an absolute path",
helper.testResolve(
path.abs("specs/absolute-root/absolute-root.yaml"),
path.abs("specs/absolute-root/absolute-root.yaml"),
parsedSchema.schema,
path.abs("specs/absolute-root/definitions/definitions.json"),
parsedSchema.definitions,
path.abs("specs/absolute-root/definitions/name.yaml"),
parsedSchema.name,
path.abs("specs/absolute-root/definitions/required-string.yaml"),
parsedSchema.requiredString
)
);

it("should resolve successfully from a url", helper.testResolve(
path.url("specs/absolute-root/absolute-root.yaml"),
path.url("specs/absolute-root/absolute-root.yaml"), parsedSchema.schema,
path.url("specs/absolute-root/definitions/definitions.json"), parsedSchema.definitions,
path.url("specs/absolute-root/definitions/name.yaml"), parsedSchema.name,
path.url("specs/absolute-root/definitions/required-string.yaml"), parsedSchema.requiredString
));
it(
"should resolve successfully from a url",
helper.testResolve(
path.url("specs/absolute-root/absolute-root.yaml"),
path.url("specs/absolute-root/absolute-root.yaml"),
parsedSchema.schema,
path.url("specs/absolute-root/definitions/definitions.json"),
parsedSchema.definitions,
path.url("specs/absolute-root/definitions/name.yaml"),
parsedSchema.name,
path.url("specs/absolute-root/definitions/required-string.yaml"),
parsedSchema.requiredString
)
);

it("should dereference successfully", async () => {
let parser = new $RefParser();
const schema = await parser.dereference(path.abs("specs/absolute-root/absolute-root.yaml"));
const schema = await parser.dereference(
path.abs("specs/absolute-root/absolute-root.yaml")
);
expect(schema).to.equal(parser.schema);
expect(schema).to.deep.equal(dereferencedSchema);
// Reference equality
Expand All @@ -100,7 +121,9 @@ describe("When executed in the context of root directory", () => {

it("should bundle successfully", async () => {
let parser = new $RefParser();
const schema = await parser.bundle(path.abs("specs/absolute-root/absolute-root.yaml"));
const schema = await parser.bundle(
path.abs("specs/absolute-root/absolute-root.yaml")
);
expect(schema).to.equal(parser.schema);
expect(schema).to.deep.equal(bundledSchema);
});
Expand Down
Loading