mirror of
https://github.com/postmanlabs/openapi-to-postman.git
synced 2022-11-29 22:05:00 +03:00
Merge pull request #595 from postmanlabs/fix/CircularReferences
Fix circular references in bundle
This commit is contained in:
187
bundled.json
187
bundled.json
@@ -1,187 +0,0 @@
|
||||
{
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"version": "1.0.10",
|
||||
"title": "Petstore 224",
|
||||
"description": "A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification",
|
||||
"termsOfService": "http://swagger.io/terms/",
|
||||
"contact": {
|
||||
"name": "Postman"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0",
|
||||
"url": "https://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
},
|
||||
"x-postman-projectname": "PostmanPetstore"
|
||||
},
|
||||
"paths": {
|
||||
"/pets": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "GlobalParam",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "GlobalCookie",
|
||||
"in": "cookie",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"post": {
|
||||
"description": "Creates a new pet in the store. Duplicates are allowed",
|
||||
"operationId": "addPet",
|
||||
"requestBody": {
|
||||
"description": "Pet to add to the store",
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"default": {
|
||||
"description": "unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
},
|
||||
"examples": {
|
||||
"Single Droplet Create Request": {
|
||||
"$ref": "#/components/examples/_example2.yaml-_droplet_create_request"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Pet": {
|
||||
"type": "object",
|
||||
"description": "A pet",
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"typeOf": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Cat",
|
||||
"Dog",
|
||||
"Bird",
|
||||
"Reptile"
|
||||
]
|
||||
},
|
||||
"tag": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Pets": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StoreItem": {
|
||||
"description": "Single store item",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"typeOf": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Cat",
|
||||
"Dog",
|
||||
"Bird",
|
||||
"Reptile"
|
||||
]
|
||||
},
|
||||
"tag": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"description": "price"
|
||||
},
|
||||
"saleValue": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"description": "price if on sale"
|
||||
},
|
||||
"onSale": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "True if item is on sale, false if not."
|
||||
}
|
||||
}
|
||||
},
|
||||
"Error": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"code",
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"examples": {
|
||||
"_example2.yaml-_droplet_create_request": {
|
||||
"value": {
|
||||
"name": "example.com",
|
||||
"region": "nyc3",
|
||||
"size": "s-1vcpu-1gb",
|
||||
"image": "ubuntu-20-04-x64",
|
||||
"ssh_keys": [
|
||||
289794,
|
||||
"3b:16:e4:bf:8b:00:8b:b8:59:8c:a9:d3:f0:19:fa:45"
|
||||
],
|
||||
"backups": true,
|
||||
"ipv6": true,
|
||||
"monitoring": true,
|
||||
"tags": [
|
||||
"env:prod",
|
||||
"web"
|
||||
],
|
||||
"user_data": "#cloud-config\nruncmd:\n - touch /test.txt\n",
|
||||
"vpc_uuid": "760e09ef-dc84-11e8-981e-3cfdfeaae000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,7 +666,7 @@ module.exports = {
|
||||
specRoot.parsed.oasObject,
|
||||
rootContextData.nodeContents
|
||||
);
|
||||
generateComponentsObject(
|
||||
finalElements = generateComponentsObject(
|
||||
rootContextData,
|
||||
rootContextData.nodeContents[specRoot.fileName],
|
||||
isExtRef,
|
||||
@@ -661,8 +674,8 @@ module.exports = {
|
||||
version
|
||||
);
|
||||
return {
|
||||
fileContent: rootContextData.nodeContents[specRoot.fileName],
|
||||
components,
|
||||
fileContent: finalElements.resRoot,
|
||||
components: finalElements.newComponents,
|
||||
fileName: specRoot.fileName,
|
||||
referenceMap: getReferenceMap(rootContextData.globalReferences)
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
76
test/data/toBundleExamples/circular_reference/expected.json
Normal file
76
test/data/toBundleExamples/circular_reference/expected.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
33
test/data/toBundleExamples/circular_reference/root.yaml
Normal file
33
test/data/toBundleExamples/circular_reference/root.yaml
Normal 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"
|
||||
|
||||
@@ -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.
|
||||
@@ -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,38 @@ 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');
|
||||
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() {
|
||||
|
||||
Reference in New Issue
Block a user