Fix circular references in bundle

Fix circular references in bundle
This commit is contained in:
Luis Tejeda
2022-07-08 17:26:27 -05:00
parent dcb78e30d7
commit 83e6819c66
7 changed files with 208 additions and 8 deletions

View File

@@ -550,6 +550,18 @@ function generateComponentsObject (documentContext, rootContent, refTypeResolver
}
});
});
return {
resRoot: traverseUtility(rootContent).map(function () {
if (this.circular) {
this.update({ [this.key]: '- Circular' });
}
}),
newComponents: traverseUtility(components).map(function () {
if (this.circular) {
this.update({ [this.key]: '- Circular' });
}
})
};
}
/**
@@ -634,6 +646,7 @@ module.exports = {
let algorithm = new DFS(),
components = {},
commonPathFromData = '',
finalElements = {},
rootContextData;
commonPathFromData = Utils.findCommonSubpath(allData.map((fileData) => {
return fileData.fileName;
@@ -653,16 +666,18 @@ module.exports = {
specRoot.parsed.oasObject,
rootContextData.nodeContents
);
generateComponentsObject(
finalElements = generateComponentsObject(
rootContextData,
rootContextData.nodeContents[specRoot.fileName],
isExtRef,
components,
version
);
finalElements.resRoot;
finalElements.newComponents;
return {
fileContent: rootContextData.nodeContents[specRoot.fileName],
components,
fileContent: finalElements.resRoot,
components: finalElements.newComponents,
fileName: specRoot.fileName,
referenceMap: getReferenceMap(rootContextData.globalReferences)
};

View File

@@ -1,4 +1,5 @@
const VERSION_30 = { key: 'openapi', version: '3.0' },
const _ = require('lodash'),
VERSION_30 = { key: 'openapi', version: '3.0' },
VERSION_31 = { key: 'openapi', version: '3.1' },
VERSION_20 = { key: 'swagger', version: '2.0' },
GENERIC_VERSION2 = { key: 'swagger', version: '2.' },
@@ -306,6 +307,20 @@ function getBundleRulesDataByVersion(version) {
}
}
/**
* Gets the version of a parsed Spec
* @param {object} spec The parsed openapi spec
* @returns {object} The bundling rules related with the spec
*/
function getVersionFromSpec(spec) {
if (!_.isNil(spec) && _.has(spec, 'swagger')) {
return spec.swagger;
}
if (!_.isNil(spec) && _.has(spec, 'openapi')) {
return spec.openapi;
}
}
module.exports = {
getSpecVersion,
getConcreteSchemaUtils,
@@ -316,5 +331,6 @@ module.exports = {
SWAGGER_VERSION,
VERSION_3_1,
validateSupportedVersion,
getBundleRulesDataByVersion
getBundleRulesDataByVersion,
getVersionFromSpec
};

View File

@@ -6,7 +6,7 @@ var yaml = require('js-yaml'),
pathBrowserify = require('path-browserify'),
resolver = require('oas-resolver-browser'),
yamlParse = require('yaml'),
{ compareVersion } = require('./common/versionUtils.js');
{ compareVersion, getVersionFromSpec } = require('./common/versionUtils.js');
const BROWSER = 'browser',
YAML_FORMAT = 'yaml',
JSON_FORMAT = 'json',
@@ -131,7 +131,7 @@ module.exports = {
}
if (inputValidation.validateSpec(oasObject, options).result) {
if (specificationVersion) {
if (compareVersion(specificationVersion, oasObject.openapi)) {
if (compareVersion(specificationVersion, getVersionFromSpec(oasObject))) {
rootFilesArray.push(filePath.fileName);
}
}

View File

@@ -0,0 +1,76 @@
{
"openapi": "3.0.2",
"info": {
"version": "1.0.0",
"title": "Swagger Petstore",
"description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "Swagger API Team",
"email": "apiteam@swagger.io",
"url": "http://swagger.io"
},
"license": {
"name": "Apache 2.0",
"url": "https://www.apache.org/licenses/LICENSE-2.0.html"
}
},
"paths": {
"/pets": {
"get": {
"description": "Returns all pets alesuada ac...",
"operationId": "findPets",
"responses": {
"200": {
"description": "pet response",
"schema": {
"type": "array",
"items": {
"$ref\"": "./schemas/schemas.yaml"
}
}
},
"default": {
"description": "unexpected error",
"schema": {
"$ref": "#/components/schemas/_schemas_schemas.yaml-components_schemas_ErrorDetail"
}
}
}
}
}
},
"components": {
"schemas": {
"_schemas_schemas.yaml-components_schemas_ErrorDetail": {
"type": "object",
"description": "The error detail.",
"properties": {
"code": {
"readOnly": true,
"type": "string",
"description": "The error code."
},
"message": {
"readOnly": true,
"type": "string",
"description": "The error message."
},
"target": {
"readOnly": true,
"type": "string",
"description": "The error target."
},
"details": {
"readOnly": true,
"type": "array",
"items": {
"items": "- Circular"
},
"description": "The error details."
}
}
}
}
}
}

View File

@@ -0,0 +1,33 @@
openapi: "3.0.2"
info:
version: 1.0.0
title: Swagger Petstore
description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification
termsOfService: http://swagger.io/terms/
contact:
name: Swagger API Team
email: apiteam@swagger.io
url: http://swagger.io
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
paths:
/pets:
get:
description: Returns all pets alesuada ac...
operationId: findPets
responses:
"200":
description: pet response
schema:
type: array
items:
$ref": "./schemas/schemas.yaml"
default:
description: unexpected error
schema:
$ref: "./schemas/schemas.yaml#components/schemas/ErrorDetail"

View File

@@ -0,0 +1,24 @@
components:
schemas:
ErrorDetail:
type: object
description: The error detail.
properties:
code:
readOnly: true
type: string
description: The error code.
message:
readOnly: true
type: string
description: The error message.
target:
readOnly: true
type: string
description: The error target.
details:
readOnly: true
type: array
items:
$ref: "#/components/schemas/ErrorDetail"
description: The error details.

View File

@@ -47,7 +47,8 @@ let expect = require('chai').expect,
referencedPath = path.join(__dirname, BUNDLES_FOLDER + '/referenced_path'),
referencedPathSchema = path.join(__dirname, BUNDLES_FOLDER + '/paths_schema'),
exampleValue = path.join(__dirname, BUNDLES_FOLDER + '/example_value'),
example2 = path.join(__dirname, BUNDLES_FOLDER + '/example2');
example2 = path.join(__dirname, BUNDLES_FOLDER + '/example2'),
schemaCircularRef = path.join(__dirname, BUNDLES_FOLDER + '/circular_reference');
describe('bundle files method - 3.0', function () {
it('Should return bundled file as json - schema_from_response', async function () {
@@ -2622,6 +2623,41 @@ describe('bundle files method - 3.0', function () {
expect(res.output.specification.version).to.equal('3.0');
expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected);
});
it('Should resolve circular reference in schema correctly', async function () {
let contentRootFile = fs.readFileSync(schemaCircularRef + '/root.yaml', 'utf8'),
schema = fs.readFileSync(schemaCircularRef + '/schemas/schemas.yaml', 'utf8'),
expected = fs.readFileSync(schemaCircularRef + '/expected.json', 'utf8'),
input = {
type: 'multiFile',
specificationVersion: '3.0',
rootFiles: [
{
path: '/root.yaml'
}
],
data: [
{
path: '/root.yaml',
content: contentRootFile
},
{
path: '/schemas/schemas.yaml',
content: schema
}
],
options: {},
bundleFormat: 'JSON'
};
const res = await Converter.bundle(input);
expect(res).to.not.be.empty;
expect(res.result).to.be.true;
expect(res.output.specification.version).to.equal('3.0');
fs.writeFileSync('bundled.json',
JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2));
expect(JSON.stringify(JSON.parse(res.output.data[0].bundledContent), null, 2)).to.be.equal(expected);
});
});
describe('getReferences method when node does not have any reference', function() {