Skip to content

Commit e856ca0

Browse files
committed
Allow excluding certain paths from dereferencing
1 parent f6886ab commit e856ca0

File tree

5 files changed

+207
-2
lines changed

5 files changed

+207
-2
lines changed

lib/dereference.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ function crawl (obj, path, pathFromRoot, parents, processedObjects, dereferenced
4141
circular: false
4242
};
4343

44+
let isExcludedPath = options.dereference.excludedPathMatcher;
45+
4446
if (options.dereference.circular === "ignore" || !processedObjects.has(obj)) {
45-
if (obj && typeof obj === "object" && !ArrayBuffer.isView(obj)) {
47+
if (obj && typeof obj === "object" && !ArrayBuffer.isView(obj) && !isExcludedPath(pathFromRoot)) {
4648
parents.add(obj);
4749
processedObjects.add(obj);
4850

@@ -55,6 +57,9 @@ function crawl (obj, path, pathFromRoot, parents, processedObjects, dereferenced
5557
for (const key of Object.keys(obj)) {
5658
let keyPath = Pointer.join(path, key);
5759
let keyPathFromRoot = Pointer.join(pathFromRoot, key);
60+
61+
if (isExcludedPath(keyPathFromRoot)) continue;
62+
5863
let value = obj[key];
5964
let circular = false;
6065

lib/options.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,16 @@ $RefParserOptions.defaults = {
7373
*
7474
* @type {boolean|string}
7575
*/
76-
circular: true
76+
circular: true,
77+
78+
/**
79+
* A function, called for each path, which can return true to stop this path and all
80+
* subpaths from being dereferenced further. This is useful in schemas where some
81+
* subpaths contain literal $ref keys that should not be dereferenced.
82+
*
83+
* @type {function}
84+
*/
85+
excludedPathMatcher: () => false
7786
},
7887
};
7988

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
"use strict";
2+
3+
module.exports = {
4+
"components": {
5+
"examples": {
6+
"confirmation-failure": {
7+
"value": {
8+
"$ref": "#/literal-component-example"
9+
}
10+
},
11+
"confirmation-success": {
12+
"value": {
13+
"abc": "def"
14+
}
15+
},
16+
"query-example": {
17+
"value": "abc"
18+
}
19+
},
20+
"parameters": {
21+
"a": {
22+
"example": {
23+
"$ref": "#/literal-param-component-example"
24+
}
25+
},
26+
"b": {
27+
"examples": {
28+
"example1": {
29+
"value": {
30+
"$ref": "#/literal-param-component-examples1"
31+
}
32+
}
33+
}
34+
}
35+
}
36+
},
37+
"paths": {
38+
"/x/{id}": {
39+
"parameters": [
40+
{
41+
"example": 123,
42+
"in": "path",
43+
"name": "id"
44+
},
45+
{
46+
"examples": {
47+
"e1": {
48+
"value": {
49+
"$ref": "#/literal-h1"
50+
}
51+
}
52+
},
53+
"in": "header",
54+
"name": "h1"
55+
},
56+
{
57+
"example": {
58+
"$ref": "#/literal-q1"
59+
},
60+
"in": "query",
61+
"name": "q1"
62+
},
63+
{
64+
"examples": {
65+
"q2": {
66+
"value": "abc"
67+
}
68+
},
69+
"in": "query",
70+
"name": "q2"
71+
}
72+
],
73+
"responses": {
74+
"200": {
75+
"content": {
76+
"application/json": {
77+
"examples": {
78+
"confirmation-failure": {
79+
"value": {
80+
"$ref": "#/literal-component-example"
81+
}
82+
},
83+
"confirmation-in-progress": {
84+
"summary": "In progress response",
85+
"value": {
86+
"$ref": "#/abc"
87+
}
88+
},
89+
"confirmation-success": {
90+
"value": {
91+
"abc": "def"
92+
}
93+
}
94+
}
95+
}
96+
}
97+
},
98+
"400": {
99+
"content": {
100+
"application/json": {
101+
"example": {
102+
"$ref": "#/literal-example"
103+
}
104+
}
105+
}
106+
}
107+
}
108+
}
109+
}
110+
};
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"use strict";
2+
3+
const { expect } = require("chai");
4+
const $RefParser = require("../../..");
5+
const path = require("../../utils/path");
6+
const dereferencedSchema = require("./dereferenced");
7+
8+
describe("Schema with literal $refs in examples", () => {
9+
it("should exclude the given paths from dereferencing", async () => {
10+
let parser = new $RefParser();
11+
const schema = await parser.dereference(path.rel("specs/ref-in-excluded-path/ref-in-excluded-path.yaml"), {
12+
dereference: {
13+
excludedPathMatcher: (path) => {
14+
return /\/example(\/|$|s\/[^\/]+\/value(\/|$))/.test(path);
15+
}
16+
}
17+
});
18+
expect(schema).to.equal(parser.schema);
19+
expect(schema).to.deep.equal(dereferencedSchema);
20+
});
21+
});
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Minimal parts of an OpenAPI 3.1.0 document, including various examples that should be literal:
2+
paths:
3+
'/x/{id}':
4+
parameters:
5+
- name: id
6+
in: path
7+
example:
8+
123
9+
- name: h1
10+
in: header
11+
examples:
12+
e1:
13+
value: # Literal value
14+
$ref: '#/literal-h1'
15+
- name: q1
16+
in: query
17+
example:
18+
$ref: '#/literal-q1'
19+
- name: q2
20+
in: query
21+
examples:
22+
q2:
23+
$ref: '#/components/examples/query-example'
24+
responses:
25+
'200':
26+
content:
27+
application/json:
28+
examples:
29+
confirmation-success: # Real ref
30+
$ref: '#/components/examples/confirmation-success'
31+
confirmation-in-progress:
32+
summary: In progress response
33+
value: # Literal ref! The $ref should not be dereferenced
34+
$ref: '#/abc'
35+
confirmation-failure: # Real ref to example with literal $ref
36+
$ref: '#/components/examples/confirmation-failure'
37+
'400':
38+
content:
39+
application/json:
40+
example:
41+
$ref: '#/literal-example'
42+
components:
43+
examples:
44+
query-example:
45+
value: abc
46+
confirmation-success:
47+
value:
48+
abc: def
49+
confirmation-failure:
50+
value: # Literal value! The $ref should not be dereferenced
51+
$ref: '#/literal-component-example'
52+
parameters:
53+
a:
54+
example:
55+
$ref: '#/literal-param-component-example'
56+
b:
57+
examples:
58+
example1:
59+
value:
60+
$ref: '#/literal-param-component-examples1'

0 commit comments

Comments
 (0)