Skip to content

Commit 142defa

Browse files
committed
Added handling if remoteRefResolver threw err
1 parent f8cb8aa commit 142defa

File tree

4 files changed

+278
-77
lines changed

4 files changed

+278
-77
lines changed

lib/bundle.js

Lines changed: 91 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ async function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilena
339339
/**
340340
* Converts contents received from remoteRefResolver into stringified JSON
341341
* @param {string | object} content - contents from remoteRefResolver
342-
* @returns Stringified JSON contents
342+
* @returns {string} Stringified JSON contents
343343
*/
344344
function convertToJSONString (content) {
345345
if (typeof content === 'object') {
@@ -351,17 +351,24 @@ async function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilena
351351
return JSON.stringify(parsedFile.oasObject);
352352
}
353353

354-
let contentFromRemote = await remoteRefResolver(property.$ref),
355-
nodeTemp = {
356-
fileName: tempRef,
357-
path: tempRef,
358-
content: convertToJSONString(contentFromRemote)
359-
};
354+
try {
355+
let contentFromRemote = await remoteRefResolver(property.$ref),
356+
nodeTemp = {
357+
fileName: tempRef,
358+
path: tempRef,
359+
content: convertToJSONString(contentFromRemote)
360+
};
360361

361-
remoteRefContentMap.set(tempRef, contentFromRemote);
362+
remoteRefContentMap.set(tempRef, contentFromRemote);
362363

363-
allData.push(nodeTemp);
364-
resolveInner();
364+
allData.push(nodeTemp);
365+
}
366+
catch (err) {
367+
// swallow the err
368+
}
369+
finally {
370+
resolveInner();
371+
}
365372
})
366373
);
367374

@@ -447,84 +454,91 @@ async function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilena
447454
}
448455

449456
const hasRemoteReferenceTypeKey = Object.keys(property)
450-
.find(
451-
(key) => {
452-
const isExternal = isExtURLRef(property, key),
453-
454-
// Only process URL refs if remoteRefResolver is provided and a valid function
455-
isReferenciable = isExternal && _.isFunction(remoteRefResolver);
457+
.find(
458+
(key) => {
459+
const isExternal = isExtURLRef(property, key),
456460

457-
return isReferenciable;
458-
}
459-
);
461+
// Only process URL refs if remoteRefResolver is provided and a valid function
462+
isReferenciable = isExternal && _.isFunction(remoteRefResolver);
460463

461-
if (hasRemoteReferenceTypeKey) {
462-
const tempRef = calculatePath(parentFilename, property.$ref),
463-
nodeTrace = handleLocalCollisions(
464-
getTraceFromParentKeyInComponents(this, tempRef, mainKeys, version, commonPathFromData),
465-
rootMainKeys
466-
),
467-
componentKey = nodeTrace[nodeTrace.length - 1],
468-
referenceInDocument = getJsonPointerRelationToRoot(
469-
tempRef,
470-
nodeTrace,
471-
version
464+
return isReferenciable;
465+
}
472466
),
473-
traceToParent = [...this.parents.map((item) => {
474-
return item.key;
475-
}).filter((item) => {
476-
return item !== undefined;
477-
}), this.key];
467+
handleRemoteURLReference = () => {
468+
const tempRef = calculatePath(parentFilename, property.$ref);
478469

479-
let newValue = Object.assign({}, this.node),
480-
[, local] = tempRef.split(localPointer),
481-
nodeFromData,
482-
refHasContent = false,
483-
parseResult,
484-
newRefInDoc,
485-
inline,
486-
contentFromRemote = remoteRefContentMap.get(tempRef),
487-
nodeTemp = {
488-
fileName: tempRef,
489-
path: tempRef,
490-
content: contentFromRemote
491-
};
470+
if (remoteRefContentMap.get(tempRef) === undefined) {
471+
return;
472+
}
492473

493-
nodeFromData = nodeTemp;
474+
let nodeTrace = handleLocalCollisions(
475+
getTraceFromParentKeyInComponents(this, tempRef, mainKeys, version, commonPathFromData),
476+
rootMainKeys
477+
),
478+
componentKey = nodeTrace[nodeTrace.length - 1],
479+
referenceInDocument = getJsonPointerRelationToRoot(
480+
tempRef,
481+
nodeTrace,
482+
version
483+
),
484+
traceToParent = [...this.parents.map((item) => {
485+
return item.key;
486+
}).filter((item) => {
487+
return item !== undefined;
488+
}), this.key],
489+
newValue = Object.assign({}, this.node),
490+
[, local] = tempRef.split(localPointer),
491+
nodeFromData,
492+
refHasContent = false,
493+
parseResult,
494+
newRefInDoc,
495+
inline,
496+
contentFromRemote = remoteRefContentMap.get(tempRef),
497+
nodeTemp = {
498+
fileName: tempRef,
499+
path: tempRef,
500+
content: contentFromRemote
501+
};
502+
503+
nodeFromData = nodeTemp;
504+
505+
if (nodeFromData && nodeFromData.content) {
506+
parseResult = parseFile(JSON.stringify(nodeFromData.content));
507+
if (parseResult.result) {
508+
newValue.$ref = referenceInDocument;
509+
refHasContent = true;
510+
nodeFromData.parsed = parseResult;
511+
}
512+
}
513+
this.update({ $ref: tempRef });
494514

495-
if (nodeFromData && nodeFromData.content) {
496-
parseResult = parseFile(JSON.stringify(nodeFromData.content));
497-
if (parseResult.result) {
498-
newValue.$ref = referenceInDocument;
499-
refHasContent = true;
500-
nodeFromData.parsed = parseResult;
515+
if (nodeTrace.length === 0) {
516+
inline = true;
501517
}
502-
}
503-
this.update({ $ref: tempRef });
504518

505-
if (nodeTrace.length === 0) {
506-
inline = true;
507-
}
519+
if (_.isNil(globalReferences[tempRef])) {
520+
nodeReferenceDirectory[tempRef] = {
521+
local,
522+
keyInComponents: nodeTrace,
523+
node: newValue,
524+
reference: inline ? newRefInDoc : referenceInDocument,
525+
traceToParent,
526+
parentNodeKey: parentFilename,
527+
mainKeyInTrace: nodeTrace[nodeTrace.length - 1],
528+
refHasContent,
529+
inline
530+
};
531+
}
508532

509-
if (_.isNil(globalReferences[tempRef])) {
510-
nodeReferenceDirectory[tempRef] = {
511-
local,
512-
keyInComponents: nodeTrace,
513-
node: newValue,
514-
reference: inline ? newRefInDoc : referenceInDocument,
515-
traceToParent,
516-
parentNodeKey: parentFilename,
517-
mainKeyInTrace: nodeTrace[nodeTrace.length - 1],
518-
refHasContent,
519-
inline
520-
};
521-
}
533+
mainKeys[componentKey] = tempRef;
522534

523-
mainKeys[componentKey] = tempRef;
535+
if (!added(property.$ref, referencesInNode)) {
536+
referencesInNode.push({ path: pathSolver(property), keyInComponents: nodeTrace, newValue: this.node });
537+
}
538+
};
524539

525-
if (!added(property.$ref, referencesInNode)) {
526-
referencesInNode.push({ path: pathSolver(property), keyInComponents: nodeTrace, newValue: this.node });
527-
}
540+
if (hasRemoteReferenceTypeKey) {
541+
handleRemoteURLReference();
528542
}
529543
}
530544
});
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
{
2+
"openapi": "3.0.0",
3+
"info": {
4+
"version": "1.0.0",
5+
"title": "Sample API",
6+
"description": "Buy or rent spacecrafts"
7+
},
8+
"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": "https://localhost:8080/SpacecraftId.json#/SpacecraftId"
18+
}
19+
}
20+
],
21+
"get": {
22+
"summary": "Read a spacecraft",
23+
"responses": {
24+
"200": {
25+
"description": "The spacecraft corresponding to the provided `spacecraftId`",
26+
"content": {
27+
"application/json": {
28+
"schema": {
29+
"$ref": "#/components/schemas/Spacecraft"
30+
}
31+
}
32+
}
33+
}
34+
}
35+
}
36+
}
37+
},
38+
"components": {
39+
"schemas": {
40+
"SpacecraftId": {
41+
"description": "The unique identifier of a spacecraft",
42+
"type": "string"
43+
},
44+
"Spacecraft": {
45+
"type": "object",
46+
"required": [
47+
"variant",
48+
"fuelFlowRate",
49+
"type"
50+
],
51+
"properties": {
52+
"id": {
53+
"description": "THE ID",
54+
"type": "string"
55+
},
56+
"variant": {
57+
"description": "The identifier of a spacecraft",
58+
"type": "string"
59+
}
60+
}
61+
}
62+
},
63+
"securitySchemes": {
64+
"ApiKey": {
65+
"type": "apiKey",
66+
"in": "header",
67+
"name": "X-Api-Key"
68+
}
69+
}
70+
},
71+
"security": [
72+
{
73+
"ApiKey": []
74+
}
75+
]
76+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
{
2+
"openapi": "3.0.0",
3+
"info": {
4+
"version": "1.0.0",
5+
"title": "Sample API",
6+
"description": "Buy or rent spacecrafts"
7+
},
8+
"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": "https://localhost:8080/SpacecraftId.json#/SpacecraftId"
18+
}
19+
}
20+
],
21+
"get": {
22+
"summary": "Read a spacecraft",
23+
"responses": {
24+
"200": {
25+
"description": "The spacecraft corresponding to the provided `spacecraftId`",
26+
"content": {
27+
"application/json": {
28+
"schema": {
29+
"$ref": "#/components/schemas/Spacecraft"
30+
}
31+
}
32+
}
33+
}
34+
}
35+
}
36+
}
37+
},
38+
"components": {
39+
"schemas": {
40+
"SpacecraftId": {
41+
"description": "The unique identifier of a spacecraft",
42+
"type": "string"
43+
},
44+
"Spacecraft": {
45+
"type": "object",
46+
"required": [
47+
"variant",
48+
"fuelFlowRate",
49+
"type"
50+
],
51+
"properties": {
52+
"id": {
53+
"description": "THE ID",
54+
"type": "string"
55+
},
56+
"variant": {
57+
"description": "The identifier of a spacecraft",
58+
"type": "string"
59+
}
60+
}
61+
}
62+
},
63+
"securitySchemes": {
64+
"ApiKey": {
65+
"type": "apiKey",
66+
"in": "header",
67+
"name": "X-Api-Key"
68+
}
69+
}
70+
},
71+
"security": [
72+
{
73+
"ApiKey": []
74+
}
75+
]
76+
}

test/unit/bundle.test.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2834,6 +2834,41 @@ describe('bundle files method - 3.0', function () {
28342834
expect(res.output.specification.version).to.equal('3.0');
28352835
expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected);
28362836
});
2837+
2838+
it('Should return bundled file as json without resolving the reference if resolver threw err - remote_url_refs',
2839+
async function () {
2840+
let contentRootFile = fs.readFileSync(remoteURLRefExamples + '/root_3.json', 'utf8'),
2841+
remoteRefResolver = async () => {
2842+
// eslint-disable-next-line no-throw-literal
2843+
throw { message: 'Something went wrong' };
2844+
},
2845+
expected = fs.readFileSync(remoteURLRefExamples + '/expected_3.json', 'utf8'),
2846+
input = {
2847+
type: 'multiFile',
2848+
specificationVersion: '3.0',
2849+
rootFiles: [
2850+
{
2851+
path: 'root.json'
2852+
}
2853+
],
2854+
data: [
2855+
{
2856+
path: 'root.json',
2857+
content: contentRootFile
2858+
}
2859+
],
2860+
options: {},
2861+
bundleFormat: 'JSON',
2862+
remoteRefResolver
2863+
};
2864+
2865+
const res = await Converter.bundle(input);
2866+
2867+
expect(res).to.not.be.empty;
2868+
expect(res.result).to.be.true;
2869+
expect(res.output.specification.version).to.equal('3.0');
2870+
expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected);
2871+
});
28372872
});
28382873

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

0 commit comments

Comments
 (0)