diff --git a/lib/swaggerUtils/componentParentMatcher.js b/lib/swaggerUtils/componentParentMatcher.js index e87f946..9aaa383 100644 --- a/lib/swaggerUtils/componentParentMatcher.js +++ b/lib/swaggerUtils/componentParentMatcher.js @@ -12,6 +12,9 @@ const COMPONENTS_KEYS_20 = [ ], INLINE = [ 'examples' + ], + PROPERTY_DEFINITION = [ + 'properties' ]; module.exports = { @@ -28,7 +31,8 @@ module.exports = { trace = [], traceToKey = [], matchFound = false, - isRootKey = false; + isRootKey = false, + traceModified = false; res.push(jsonPointerDecodeAndReplace(`${filePathName}${localPart}`)); trace = [...res].reverse(); @@ -36,6 +40,11 @@ module.exports = { for (let [index, item] of trace.entries()) { if (SCHEMA_PARENTS.includes(item)) { item = 'definitions'; + traceModified = true; + } + if (PROPERTY_DEFINITION.includes(trace[index + 2])) { + trace[index + 1] = 'definitions'; + traceModified = true; } if (INLINE.includes(item)) { matchFound = false; @@ -44,7 +53,8 @@ module.exports = { traceToKey.push(item); if (COMPONENTS_KEYS_20.includes(item)) { matchFound = true; - isRootKey = trace[index + 1] === undefined; + isRootKey = trace[index + 1] === undefined && + !traceModified; break; } } diff --git a/test/data/toBundleExamples/nestedProperties/properties/prop.yaml b/test/data/toBundleExamples/nestedProperties/properties/prop.yaml index 1164054..f1c75c9 100644 --- a/test/data/toBundleExamples/nestedProperties/properties/prop.yaml +++ b/test/data/toBundleExamples/nestedProperties/properties/prop.yaml @@ -11,4 +11,4 @@ properties: country: $ref: "./country.yaml" warrior: - $ref: "./warrior.yaml" \ No newline at end of file + $ref: "./warrior.yaml" diff --git a/test/data/toBundleExamples/nestedProperties/properties/warrior.yaml b/test/data/toBundleExamples/nestedProperties/properties/warrior.yaml index 6434c31..0308186 100644 --- a/test/data/toBundleExamples/nestedProperties/properties/warrior.yaml +++ b/test/data/toBundleExamples/nestedProperties/properties/warrior.yaml @@ -3,4 +3,4 @@ properties: power: type: string weapon: - type: string \ No newline at end of file + type: string diff --git a/test/data/toBundleExamples/schema_from_response_with_headers/expected.json b/test/data/toBundleExamples/schema_from_response_with_headers/expected.json new file mode 100644 index 0000000..46c7d7f --- /dev/null +++ b/test/data/toBundleExamples/schema_from_response_with_headers/expected.json @@ -0,0 +1,52 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Sample API", + "description": "Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.", + "version": "0.1.9" + }, + "servers": [ + { + "url": "http://api.example.com/v1", + "description": "Optional server description, e.g. Main (production) server" + }, + { + "url": "http://staging-api.example.com", + "description": "Optional server description, e.g. Internal staging server for testing" + } + ], + "paths": { + "/users/{userId}": { + "get": { + "summary": "Get a user by ID", + "responses": { + "200": { + "description": "A single user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/~1schemas~1user.yaml" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "/schemas/user.yaml": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "userName": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/schema_from_response_with_headers/expected.yaml b/test/data/toBundleExamples/schema_from_response_with_headers/expected.yaml new file mode 100644 index 0000000..e405d31 --- /dev/null +++ b/test/data/toBundleExamples/schema_from_response_with_headers/expected.yaml @@ -0,0 +1,32 @@ +openapi: 3.0.0 +info: + title: Sample API + description: >- + Optional multiline or single-line description in + [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 +servers: + - url: 'http://api.example.com/v1' + description: 'Optional server description, e.g. Main (production) server' + - url: 'http://staging-api.example.com' + description: 'Optional server description, e.g. Internal staging server for testing' +paths: + '/users/{userId}': + get: + summary: Get a user by ID + responses: + '200': + description: A single user. + content: + application/json: + schema: + $ref: '#/components/schemas/~1schemas~1user.yaml' +components: + schemas: + /schemas/user.yaml: + type: object + properties: + id: + type: integer + userName: + type: string diff --git a/test/data/toBundleExamples/schema_from_response_with_headers/headers/headers.yaml b/test/data/toBundleExamples/schema_from_response_with_headers/headers/headers.yaml new file mode 100644 index 0000000..5d1da1b --- /dev/null +++ b/test/data/toBundleExamples/schema_from_response_with_headers/headers/headers.yaml @@ -0,0 +1,6 @@ +headerTest: + schema: + type: integer + example: 5000 + description: >- + The test header \ No newline at end of file diff --git a/test/data/toBundleExamples/schema_from_response_with_headers/root.yaml b/test/data/toBundleExamples/schema_from_response_with_headers/root.yaml new file mode 100644 index 0000000..0529fe4 --- /dev/null +++ b/test/data/toBundleExamples/schema_from_response_with_headers/root.yaml @@ -0,0 +1,26 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 + +servers: + - url: http://api.example.com/v1 + description: Optional server description, e.g. Main (production) server + - url: http://staging-api.example.com + description: Optional server description, e.g. Internal staging server for testing + +paths: + /users/{userId}: + get: + summary: Get a user by ID + responses: + 200: + description: A single user. + headers: + ratelimit-limit: + $ref: '../headers.yaml#/headerTest' + content: + application/json: + schema: + $ref: "./schemas/user.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/schema_from_response_with_headers/schemas/user.yaml b/test/data/toBundleExamples/schema_from_response_with_headers/schemas/user.yaml new file mode 100644 index 0000000..81370de --- /dev/null +++ b/test/data/toBundleExamples/schema_from_response_with_headers/schemas/user.yaml @@ -0,0 +1,6 @@ +type: object +properties: + id: + type: integer + userName: + type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json index eb6ddbb..74a6a8a 100644 --- a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternal/bundleExpected.json @@ -51,18 +51,21 @@ "type": "string" }, "Color": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "uses": { - "type": "string" - }, - "color": { - "type": "string" - } - } + "$ref": "#/definitions/~1pet.yaml%23~1Color" + } + } + }, + "/pet.yaml#/Color": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uses": { + "type": "string" + }, + "color": { + "type": "string" } } } diff --git a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json index 8554787..2579a1c 100644 --- a/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/bringLocalDependenciesFromExternalMultiple/bundleExpected.json @@ -80,38 +80,47 @@ "type": "string" }, "Color": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "uses": { - "type": "string" - }, - "color": { - "type": "string" - } - } + "$ref": "#/definitions/~1pet.yaml%23~1Color" }, "FavoriteFood": { - "type": "object", - "properties": { - "brand": { - "type": "string" - }, - "benefits": { - "type": "array", - "items": { - "$ref": "#/definitions/~1food.yaml%23~1Benefit" - } - }, - "cost": { - "type": "string" - } - } + "$ref": "#/definitions/~1food.yaml%23~1Food" } } }, + "/pet.yaml#/Color": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uses": { + "type": "string" + }, + "color": { + "type": "string" + } + } + }, + "/food.yaml#/Food": { + "type": "object", + "properties": { + "brand": { + "$ref": "#/definitions/~1food.yaml%23~1Brand" + }, + "benefits": { + "type": "array", + "items": { + "$ref": "#/definitions/~1food.yaml%23~1Benefit" + } + }, + "cost": { + "type": "string" + } + } + }, + "/food.yaml#/Brand": { + "type": "string" + }, "/food.yaml#/Benefit": { "type": "object", "properties": { diff --git a/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json index f8a5b21..d673521 100644 --- a/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/multipleRefFromRootComponents/bundleExpected.json @@ -52,18 +52,21 @@ "type": "string" }, "Color": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "uses": { - "type": "string" - }, - "color": { - "type": "string" - } - } + "$ref": "#/definitions/~1pet.yaml%23~1Color" + } + } + }, + "/pet.yaml#/Color": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uses": { + "type": "string" + }, + "color": { + "type": "string" } } } diff --git a/test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json b/test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json index 5ab6064..647921b 100644 --- a/test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/nestedLocalRef/bundleExpected.json @@ -47,20 +47,26 @@ "type": "string" }, "favoriteFood": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "brand": { - "type": "string" - } - } + "$ref": "#/definitions/~1schemas~1favorite_food.yaml%23~1FavoriteFood" }, "foodPrice": { - "type": "integer" + "$ref": "#/definitions/~1schemas~1favorite_food.yaml%23~1Price" } } + }, + "/schemas/favorite_food.yaml#/FavoriteFood": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "brand": { + "type": "string" + } + } + }, + "/schemas/favorite_food.yaml#/Price": { + "type": "integer" } } } \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/bundleExpected.json b/test/data/toBundleExamples/swagger20/nestedProperties20/bundleExpected.json new file mode 100644 index 0000000..eca6b1e --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/bundleExpected.json @@ -0,0 +1,68 @@ +{ + "swagger": "2.0", + "info": { + "title": "Sample API", + "description": "API description in Markdown.", + "version": "1.0.0" + }, + "host": "api.example.com", + "basePath": "/v1", + "schemes": [ + "https" + ], + "paths": { + "/users": { + "get": { + "summary": "Returns a list of users.", + "description": "Optional extended description in Markdown.", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/~1schemas~1user.yaml" + } + } + } + } + } + } + }, + "definitions": { + "/schemas/user.yaml": { + "type": "object", + "properties": { + "age": { + "$ref": "#/definitions/~1schemas~1age.yaml" + }, + "hobbies": { + "$ref": "#/definitions/~1schemas~1hobbies.yaml" + } + } + }, + "/schemas/age.yaml": { + "type": "string" + }, + "/schemas/hobbies.yaml": { + "type": "array", + "items": { + "$ref": "#/definitions/~1schemas~1hobby.yaml" + } + }, + "/schemas/hobby.yaml": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "position": { + "type": "integer" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/index.yaml b/test/data/toBundleExamples/swagger20/nestedProperties20/index.yaml new file mode 100644 index 0000000..f2f08fa --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/index.yaml @@ -0,0 +1,9 @@ +swagger: "2.0" +info: + $ref: ./info.yaml +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + $ref: ./paths.yaml \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/info.yaml b/test/data/toBundleExamples/swagger20/nestedProperties20/info.yaml new file mode 100644 index 0000000..19565f9 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/info.yaml @@ -0,0 +1,3 @@ +title: Sample API +description: API description in Markdown. +version: 1.0.0 diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/paths.yaml b/test/data/toBundleExamples/swagger20/nestedProperties20/paths.yaml new file mode 100644 index 0000000..1a7dc22 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/paths.yaml @@ -0,0 +1,13 @@ +/users: + get: + summary: Returns a list of users. + description: Optional extended description in Markdown. + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "./schemas/user.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/age.yaml b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/age.yaml new file mode 100644 index 0000000..2d8bb0e --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/age.yaml @@ -0,0 +1 @@ +type: string \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/hobbies.yaml b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/hobbies.yaml new file mode 100644 index 0000000..1b4d1b3 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/hobbies.yaml @@ -0,0 +1,3 @@ +type: array +items: + $ref: "./hobby.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/hobby.yaml b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/hobby.yaml new file mode 100644 index 0000000..8545a7b --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/hobby.yaml @@ -0,0 +1,6 @@ +type: object +properties: + name: + type: string + position: + type: integer \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/user.yaml b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/user.yaml new file mode 100644 index 0000000..8c13a61 --- /dev/null +++ b/test/data/toBundleExamples/swagger20/nestedProperties20/schemas/user.yaml @@ -0,0 +1,6 @@ +type: object +properties: + age: + $ref: "./age.yaml" + hobbies: + $ref: "./hobbies.yaml" \ No newline at end of file diff --git a/test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json b/test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json index c5d8dbd..90b2aa1 100644 --- a/test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/nestedRefs/bundleExpected.json @@ -47,15 +47,18 @@ "type": "string" }, "favoriteFood": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "brand": { - "type": "string" - } - } + "$ref": "#/definitions/~1schemas~1favorite_food.yaml" + } + } + }, + "/schemas/favorite_food.yaml": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "brand": { + "type": "string" } } } diff --git a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json index 06c61ac..620a304 100644 --- a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json +++ b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/bundleExpected.json @@ -22,7 +22,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/~1schemas~1user.yaml" + "$ref": "#/definitions/~1schemas~1user.yaml%23User" } } } @@ -39,7 +39,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/~1otherSchemas~1client.yaml" + "$ref": "#/definitions/~1otherSchemas~1client.yaml%23Client" } } } @@ -47,42 +47,38 @@ } }, "definitions": { - "/schemas/user.yaml": { - "User": { - "type": "object", - "required": [ - "id", - "name" - ], - "properties": { - "id": { - "type": "integer" - }, - "detail": { - "type": "array", - "items": { - "$ref": "#/definitions/~1schemas~1detail.yaml%23~1Detail" - } + "/schemas/user.yaml#User": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer" + }, + "detail": { + "type": "array", + "items": { + "$ref": "#/definitions/~1schemas~1detail.yaml%23~1Detail" } } } }, - "/otherSchemas/client.yaml": { - "Client": { - "type": "object", - "required": [ - "id", - "name" - ], - "properties": { - "id": { - "type": "integer" - }, - "details": { - "type": "array", - "items": { - "$ref": "#/definitions/~1otherSchemas~1detail.yaml%23~1Detail" - } + "/otherSchemas/client.yaml#Client": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/~1otherSchemas~1detail.yaml%23~1Detail" } } } diff --git a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/paths.yaml b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/paths.yaml index 06abba8..cd37e5f 100644 --- a/test/data/toBundleExamples/swagger20/sameRefDifferentSource/paths.yaml +++ b/test/data/toBundleExamples/swagger20/sameRefDifferentSource/paths.yaml @@ -8,7 +8,7 @@ 200: description: OK schema: - $ref: "./schemas/user.yaml" + $ref: "./schemas/user.yaml#User" /clients: get: summary: Returns a list of users. @@ -19,4 +19,4 @@ 200: description: OK schema: - $ref: "./otherSchemas/client.yaml" \ No newline at end of file + $ref: "./otherSchemas/client.yaml#Client" \ No newline at end of file diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 811f7d2..3669c61 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -40,13 +40,13 @@ let expect = require('chai').expect, '/bringLocalDependenciesFromExternalMultiple'), multipleRefFromRootComponents = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/multipleRefFromRootComponents'), sameRefDifferentSource = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/sameRefDifferentSource'), + nestedProperties20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/nestedProperties20'), simpleRef = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/simpleRef'), refExample20 = path.join(__dirname, SWAGGER_MULTIFILE_FOLDER + '/referenced_example'), properties = path.join(__dirname, BUNDLES_FOLDER + '/properties'), sameSourceDifferentPlace = path.join(__dirname, BUNDLES_FOLDER + '/same_source_different_place'), nestedProperties = path.join(__dirname, BUNDLES_FOLDER + '/nestedProperties'); - describe('bundle files method - 3.0', function () { it('Should return bundled file as json - schema_from_response', async function () { let contentRootFile = fs.readFileSync(schemaFromResponse + '/root.yaml', 'utf8'), @@ -997,6 +997,8 @@ describe('bundle files method - 3.0', function () { expect(res.result).to.be.true; expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); expect(JSON.stringify(res.output.data[1].bundledContent, null, 2)).to.be.equal(expected2); + expect(res.output.data[0].rootFile.path).to.equal('/root.yaml'); + expect(res.output.data[1].rootFile.path).to.equal('/root2.yaml'); }); it('Should throw error when root is not present in data array', async function () { @@ -1492,6 +1494,62 @@ describe('bundle files method - 3.0', function () { }); describe('bundle files method - 2.0', function() { + it('Should return bundled result from - nestedProperties20', async function() { + let contentRootFile = fs.readFileSync(nestedProperties20 + '/index.yaml', 'utf8'), + info = fs.readFileSync(nestedProperties20 + '/info.yaml', 'utf8'), + paths = fs.readFileSync(nestedProperties20 + '/paths.yaml', 'utf8'), + age = fs.readFileSync(nestedProperties20 + '/schemas/age.yaml', 'utf8'), + hobbies = fs.readFileSync(nestedProperties20 + '/schemas/hobbies.yaml', 'utf8'), + hobby = fs.readFileSync(nestedProperties20 + '/schemas/hobby.yaml', 'utf8'), + user = fs.readFileSync(nestedProperties20 + '/schemas/user.yaml', 'utf8'), + expected = fs.readFileSync(nestedProperties20 + '/bundleExpected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '2.0', + rootFiles: [ + { + path: '/index.yaml' + } + ], + data: [ + { + path: '/index.yaml', + content: contentRootFile + }, + { + path: '/info.yaml', + content: info + }, + { + path: '/paths.yaml', + content: paths + }, + { + path: '/schemas/user.yaml', + content: user + }, + { + path: '/schemas/age.yaml', + content: age + }, + { + path: '/schemas/hobbies.yaml', + content: hobbies + }, + { + path: '/schemas/hobby.yaml', + content: hobby + } + ], + options: {}, + bundleFormat: 'JSON' + }; + const res = await Converter.bundle(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + it('Should return bundled result from - sameRefDifferentSource', async function() { let contentRootFile = fs.readFileSync(sameRefDifferentSource + '/index.yaml', 'utf8'), info = fs.readFileSync(sameRefDifferentSource + '/info.yaml', 'utf8'),