Skip to content

Commit 37003c1

Browse files
committed
Added handling for circular refs
1 parent b0ffbb9 commit 37003c1

File tree

6 files changed

+114
-83
lines changed

6 files changed

+114
-83
lines changed

lib/bundle.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,8 @@ async function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilena
358358
nodeTemp = {
359359
fileName: tempRef,
360360
path: tempRef,
361-
content: convertToJSONString(contentFromRemote)
361+
content: convertToJSONString(contentFromRemote),
362+
href: property.$ref
362363
};
363364

364365
remoteRefContentMap.set(tempRef, contentFromRemote);

lib/dfs.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,24 @@ class DFS {
3535
missing = [],
3636
visited = new Set(),
3737
nodeContents = {},
38-
globalReferences = {};
38+
globalReferences = {},
39+
hrefsVisited = new Set();
40+
3941
stack.push(node);
4042
while (stack.length > 0) {
4143
node = stack.pop();
42-
if (!visited.has(node)) {
44+
if (!visited.has(node) &&
45+
46+
/**
47+
* For nodes that are fetched for remote URLs we ensure they
48+
* aren't visited more than once
49+
*/
50+
(!node.href || (!hrefsVisited.has(node.href)))
51+
) {
4352
traverseOrder.push(node);
4453
visited.add(node);
54+
hrefsVisited.add(node.href);
55+
4556
let {
4657
graphAdj,
4758
missingNodes,
Lines changed: 42 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,35 @@
11
{
2-
"openapi": "3.0.0",
2+
"openapi": "3.0.2",
33
"info": {
44
"version": "1.0.0",
5-
"title": "Sample API",
6-
"description": "Buy or rent spacecrafts"
5+
"title": "Swagger Petstore",
6+
"description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",
7+
"termsOfService": "http://swagger.io/terms/",
8+
"contact": {
9+
"name": "Swagger API Team",
10+
"email": "apiteam@swagger.io",
11+
"url": "http://swagger.io"
12+
},
13+
"license": {
14+
"name": "Apache 2.0",
15+
"url": "https://www.apache.org/licenses/LICENSE-2.0.html"
16+
}
717
},
818
"paths": {
9-
"/spacecrafts/{spacecraftId}": {
10-
"parameters": [
11-
{
12-
"name": "spacecraftId",
13-
"description": "The unique identifier of the spacecraft",
14-
"in": "path",
15-
"required": true,
16-
"schema": {
17-
"$ref": "#/components/schemas/SpacecraftId"
18-
}
19-
}
20-
],
19+
"/pets": {
2120
"get": {
22-
"summary": "Read a spacecraft",
21+
"description": "Returns all pets alesuada ac...",
22+
"operationId": "findPets",
2323
"responses": {
2424
"200": {
25-
"description": "The spacecraft corresponding to the provided `spacecraftId`",
25+
"description": "An paged array of pets",
2626
"content": {
2727
"application/json": {
2828
"schema": {
29-
"$ref": "#/components/schemas/https_localhost8080_Spacecraft.json-components_schemas_Spacecraft"
29+
"type": "array",
30+
"items": {
31+
"$ref": "#/components/schemas/https_localhost8080_schema.json-_components_schemas_ErrorDetail"
32+
}
3033
}
3134
}
3235
}
@@ -37,71 +40,36 @@
3740
},
3841
"components": {
3942
"schemas": {
40-
"SpacecraftId": {
41-
"description": "The unique identifier of a spacecraft",
43+
"details": {
44+
"description": "The detailed identifier of a spacecraft",
4245
"type": "string"
4346
},
44-
"Error": {
45-
"type": "object",
46-
"required": [
47-
"message"
48-
],
49-
"properties": {
50-
"message": {
51-
"description": "A human readable error message",
52-
"type": "string"
53-
}
54-
}
55-
},
56-
"https_localhost8080_Spacecraft.json-components_schemas_Spacecraft": {
47+
"https_localhost8080_schema.json-_components_schemas_ErrorDetail": {
5748
"type": "object",
58-
"required": [
59-
"variant",
60-
"fuelFlowRate",
61-
"type"
62-
],
49+
"description": "The error detail.",
6350
"properties": {
64-
"id": {
65-
"description": "THE ID",
66-
"type": "string"
67-
},
68-
"variant": {
69-
"description": "The identifier of a spacecraft",
70-
"type": "string"
71-
},
72-
"peakThrust": {
51+
"code": {
52+
"readOnly": true,
7353
"type": "string",
74-
"description": "PEAKTHRUST"
54+
"description": "The error code."
7555
},
76-
"fuelFlowRate": {
56+
"message": {
57+
"readOnly": true,
7758
"type": "string",
78-
"description": "Fuel injection capacity rate"
79-
},
80-
"maxImpulse": {
81-
"description": "maxImpulse maxImpulse",
82-
"type": "string"
59+
"description": "The error message."
8360
},
84-
"type": {
61+
"target": {
62+
"readOnly": true,
8563
"type": "string",
86-
"enum": [
87-
"capsule",
88-
"probe",
89-
"satellite",
90-
"spaceplane",
91-
"station"
92-
]
64+
"description": "The error target."
9365
},
94-
"peakThrustSecond": {
95-
"type": "object",
96-
"required": [
97-
"message"
98-
],
99-
"properties": {
100-
"message": {
101-
"description": "A human readable error message from nested deep ref",
102-
"type": "string"
103-
}
104-
}
66+
"details": {
67+
"readOnly": true,
68+
"type": "array",
69+
"items": {
70+
"$ref": "#/components/schemas/details"
71+
},
72+
"description": "The error details."
10573
}
10674
}
10775
}
@@ -113,10 +81,5 @@
11381
"name": "X-Api-Key"
11482
}
11583
}
116-
},
117-
"security": [
118-
{
119-
"ApiKey": []
120-
}
121-
]
84+
}
12285
}

test/data/toBundleExamples/remote_url_refs/circular/root.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,20 @@
3737
}
3838
}
3939
}
40+
},
41+
"components": {
42+
"schemas": {
43+
"details": {
44+
"description": "The detailed identifier of a spacecraft",
45+
"type": "string"
46+
}
47+
},
48+
"securitySchemes": {
49+
"ApiKey": {
50+
"type": "apiKey",
51+
"in": "header",
52+
"name": "X-Api-Key"
53+
}
54+
}
4055
}
4156
}

test/data/toBundleExamples/remote_url_refs/circular/schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"readOnly": true,
2525
"type": "array",
2626
"items": {
27-
"$ref": "https://localhost:8080/schema.json#/components/schemas/ErrorDetail"
27+
"$ref": "https://localhost:8080/root.json#/components/schemas/details"
2828
},
2929
"description": "The error details."
3030
}

test/unit/bundle.test.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2943,6 +2943,47 @@ describe('bundle files method - 3.0', function () {
29432943
expect(res.output.specification.version).to.equal('3.0');
29442944
expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected);
29452945
});
2946+
2947+
it('Should return bundled file as json with circular url refs - remote_url_refs', async function () {
2948+
let contentRootFile = fs.readFileSync(remoteURLRefExamples + '/circular/root.json', 'utf8'),
2949+
schema = fs.readFileSync(remoteURLRefExamples + '/circular/schema.json', 'utf8'),
2950+
2951+
remoteRefResolver = async (refURL) => {
2952+
if (refURL.includes('schema')) {
2953+
return JSON.parse(schema);
2954+
}
2955+
2956+
if (refURL.includes('root')) {
2957+
return JSON.parse(contentRootFile);
2958+
}
2959+
},
2960+
expected = fs.readFileSync(remoteURLRefExamples + '/circular/expected.json', 'utf8'),
2961+
input = {
2962+
type: 'multiFile',
2963+
specificationVersion: '3.0',
2964+
rootFiles: [
2965+
{
2966+
path: 'root.json'
2967+
}
2968+
],
2969+
data: [
2970+
{
2971+
path: 'root.json',
2972+
content: contentRootFile
2973+
}
2974+
],
2975+
options: {},
2976+
bundleFormat: 'JSON',
2977+
remoteRefResolver
2978+
};
2979+
2980+
const res = await Converter.bundle(input);
2981+
2982+
expect(res).to.not.be.empty;
2983+
expect(res.result).to.be.true;
2984+
expect(res.output.specification.version).to.equal('3.0');
2985+
expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected);
2986+
});
29462987
});
29472988

29482989
describe('getReferences method when node does not have any reference', function() {

0 commit comments

Comments
 (0)