From c0254456aeb0bbb955a74590a994a87c3a632400 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 8 Jun 2022 13:12:57 -0500 Subject: [PATCH 1/3] bundle only found content bundle only the files that has content and add test for filtering root files in bundle according to the version. --- lib/schemapack.js | 17 ++- .../schema_from_response/expected3_1.json | 52 +++++++ .../schema_from_response/root3_1.yaml | 23 +++ test/unit/bundle.test.js | 139 ++++++++++++++++++ test/unit/detectRelatedFiles.test.js | 47 ++++++ 5 files changed, 272 insertions(+), 6 deletions(-) create mode 100644 test/data/toBundleExamples/schema_from_response/expected3_1.json create mode 100644 test/data/toBundleExamples/schema_from_response/root3_1.yaml diff --git a/lib/schemapack.js b/lib/schemapack.js index 88f2eaf..78fc4fa 100644 --- a/lib/schemapack.js +++ b/lib/schemapack.js @@ -674,16 +674,19 @@ class SchemaPack { let adaptedRootFiles = input.rootFiles.map((rootFile) => { let foundInData = input.data.find((file) => { return file.fileName === rootFile.path; }); if (!foundInData) { - throw new Error('Root file content not found in data array'); + return undefined; } - return { fileName: rootFile.path, content: foundInData.content }; - }); + }).filter((rootFile) => { return rootFile !== undefined; }); + if (adaptedRootFiles.length === 0) { + throw new Error('Root file content not found in data array'); + } input.rootFiles = adaptedRootFiles; return schemaUtils.processRelatedFiles(input); } + /** * * @description Takes in a folder and identifies the related files from the @@ -700,11 +703,13 @@ class SchemaPack { let adaptedRootFiles = input.rootFiles.map((rootFile) => { let foundInData = input.data.find((file) => { return file.fileName === rootFile.path; }); if (!foundInData) { - throw new Error('Root file content not found in data array'); + return undefined; } - return { fileName: rootFile.path, content: foundInData.content }; - }); + }).filter((rootFile) => { return rootFile !== undefined; }); + if (adaptedRootFiles.length === 0) { + throw new Error('Root file content not found in data array'); + } input.rootFiles = adaptedRootFiles; return schemaUtils.processRelatedFiles(input, true); } diff --git a/test/data/toBundleExamples/schema_from_response/expected3_1.json b/test/data/toBundleExamples/schema_from_response/expected3_1.json new file mode 100644 index 0000000..f4550a4 --- /dev/null +++ b/test/data/toBundleExamples/schema_from_response/expected3_1.json @@ -0,0 +1,52 @@ +{ + "openapi": "3.1.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/root3_1.yaml b/test/data/toBundleExamples/schema_from_response/root3_1.yaml new file mode 100644 index 0000000..543a962 --- /dev/null +++ b/test/data/toBundleExamples/schema_from_response/root3_1.yaml @@ -0,0 +1,23 @@ +openapi: 3.1.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: "./schemas/user.yaml" diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 617c841..d591058 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -54,6 +54,7 @@ describe('bundle files method - 3.0', function () { 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(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); }); @@ -935,6 +936,144 @@ describe('bundle files method - 3.0', function () { expect(error.message).to.equal('Root file content not found in data array'); } }); + it('Should return bundled 1 file with 2 root but 1 is missing', async function () { + let contentRootFile = fs.readFileSync(schemaFromResponse + '/root.yaml', 'utf8'), + user = fs.readFileSync(schemaFromResponse + '/schemas/user.yaml', 'utf8'), + expected = fs.readFileSync(schemaFromResponse + '/expected.json', 'utf8'), + responses = fs.readFileSync(localRefFolder + '/responses.yaml', 'utf8'), + schemasIndex = fs.readFileSync(localRefFolder + '/schemas/index.yaml', 'utf8'), + schemasClient = fs.readFileSync(localRefFolder + '/schemas/client.yaml', 'utf8'), + toySchema = fs.readFileSync(localRefFolder + '/otherSchemas/toy.yaml', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root.yaml' + }, + { + path: '/root2.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/schemas/user.yaml', + content: user + }, + { + path: '/responses.yaml', + content: responses + }, + { + path: '/schemas/index.yaml', + content: schemasIndex + }, + { + path: '/schemas/client.yaml', + content: schemasClient + }, + { + path: '/otherSchemas/toy.yaml', + content: toySchema + } + ], + 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); + expect(res.output.data.length).to.equal(1); + }); + + it('Should bundle only the files with the specified version 3.1', async function () { + let contentRootFile = fs.readFileSync(schemaFromResponse + '/root.yaml', 'utf8'), + contentRootFile31 = fs.readFileSync(schemaFromResponse + '/root3_1.yaml', 'utf8'), + user = fs.readFileSync(schemaFromResponse + '/schemas/user.yaml', 'utf8'), + expected = fs.readFileSync(schemaFromResponse + '/expected3_1.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.1', + rootFiles: [ + { + path: '/root3_1.yaml' + }, + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/root3_1.yaml', + content: contentRootFile31 + }, + { + path: '/schemas/user.yaml', + content: user + } + ], + 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.1'); + expect(res.output.data.length).to.equal(1); + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); + + it('Should bundle only the files with the specified version 3.0', async function () { + let contentRootFile = fs.readFileSync(schemaFromResponse + '/root.yaml', 'utf8'), + contentRootFile31 = fs.readFileSync(schemaFromResponse + '/root3_1.yaml', 'utf8'), + user = fs.readFileSync(schemaFromResponse + '/schemas/user.yaml', 'utf8'), + expected = fs.readFileSync(schemaFromResponse + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + specificationVersion: '3.0', + rootFiles: [ + { + path: '/root3_1.yaml' + }, + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/root3_1.yaml', + content: contentRootFile31 + }, + { + path: '/schemas/user.yaml', + content: user + } + ], + 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(res.output.data.length).to.equal(1); + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); }); diff --git a/test/unit/detectRelatedFiles.test.js b/test/unit/detectRelatedFiles.test.js index 84e01f8..b848426 100644 --- a/test/unit/detectRelatedFiles.test.js +++ b/test/unit/detectRelatedFiles.test.js @@ -442,4 +442,51 @@ describe('detectRelatedFiles method', function () { expect(error.message).to.equal('"Data" parameter should be provided'); } }); + + it('Should throw error when root is not present in data array', async function () { + let contentFileHop = fs.readFileSync(validHopService31x, 'utf8'), + input = { + type: 'multiFile', + rootFiles: [ + { + path: '/petstore.yaml' + } + ], + data: [ + { + path: '/hopService.yaml', + content: contentFileHop + }] + }; + try { + await Converter.detectRelatedFiles(input); + } + catch (error) { + expect(error.message).to.equal('Root file content not found in data array'); + } + }); + + it('Should return 1 file with 2 root but 1 is missing', async function () { + let contentFilePet = fs.readFileSync(validPetstore, 'utf8'), + input = { + type: 'multiFile', + rootFiles: [ + { + path: '/petstore.yaml' + }, + { + path: '/petstore2.yaml' + } + ], + data: [{ + path: '/petstore.yaml', + content: contentFilePet + }] + }; + const res = await Converter.detectRelatedFiles(input); + expect(res).to.not.be.empty; + expect(res.result).to.be.true; + expect(res.output.data[0].rootFile.path).to.equal('/petstore.yaml'); + expect(res.output.data.length).to.equal(1); + }); }); From bb3a3c00d9d3055789415205f9743adb6d278855 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 8 Jun 2022 16:51:56 -0500 Subject: [PATCH 2/3] Change defatult version change the default version from 3.0.0 to 3.0 and validate incorrect input.type value (different than multiFile) --- lib/schemaUtils.js | 14 +++++---- lib/schemapack.js | 3 +- test/unit/bundle.test.js | 55 ++++++++++++++++++++++++++++++++++++ test/unit/detectRoot.test.js | 2 +- 4 files changed, 67 insertions(+), 7 deletions(-) diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index f01d99a..40d526e 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -92,7 +92,8 @@ const { formatDataPath, checkIsCorrectType, isKnownType } = require('./common/sc { getRelatedFiles } = require('./relatedFiles'), { compareVersion } = require('./common/versionUtils.js'), parse = require('./parse'), - { getBundleContentAndComponents } = require('./bundle.js'); + { getBundleContentAndComponents } = require('./bundle.js'), + MULTI_FILE_API_TYPE_ALLOWED_VALUE = 'multiFile'; /* eslint-enable */ // See https://github.com/json-schema-faker/json-schema-faker/tree/master/docs#available-options @@ -4797,7 +4798,7 @@ module.exports = { */ mapGetRootFilesOutputToDetectRootFilesOutput(output, version) { if (!version) { - version = '3.0.0'; + version = '3.0'; } let adaptedData = output.map((file) => { return { path: file }; @@ -4906,7 +4907,7 @@ module.exports = { * @returns {object} root files information and data input */ processRelatedFiles(inputRelatedFiles, toBundle = false) { - let version = inputRelatedFiles.specificationVersion ? inputRelatedFiles.specificationVersion : '3.0.0', + let version = inputRelatedFiles.specificationVersion ? inputRelatedFiles.specificationVersion : '3.0', res = { result: true, output: { @@ -4957,6 +4958,9 @@ module.exports = { if (!processInput.type) { throw new Error('"Type" parameter should be provided'); } + if (processInput.type !== MULTI_FILE_API_TYPE_ALLOWED_VALUE) { + throw new Error('"Type" parameter value allowed is ' + MULTI_FILE_API_TYPE_ALLOWED_VALUE); + } if (!processInput.data || processInput.data.length === 0) { throw new Error('"Data" parameter should be provided'); } @@ -4971,6 +4975,6 @@ module.exports = { throw new ParseError(result.reason); } return result; - } - + }, + MULTI_FILE_API_TYPE_ALLOWED_VALUE }; diff --git a/lib/schemapack.js b/lib/schemapack.js index 78fc4fa..3bd1f34 100644 --- a/lib/schemapack.js +++ b/lib/schemapack.js @@ -34,7 +34,8 @@ let path = require('path'), class SchemaPack { constructor (input, options = {}) { - if (input.type === 'multiFile' && input.data && input.data[0] && input.data[0].path) { + if (input.type === schemaUtils.MULTI_FILE_API_TYPE_ALLOWED_VALUE && + input.data && input.data[0] && input.data[0].path) { input = schemaUtils.mapDetectRootFilesInputToFolderInput(input); } this.input = input; diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index d591058..7986c6f 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -1074,6 +1074,47 @@ describe('bundle files method - 3.0', function () { expect(res.output.data.length).to.equal(1); expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); }); + + it('should bundle files to 3.0 when specificationVersion is not provided', async function () { + let contentRootFile = fs.readFileSync(schemaFromResponse + '/root.yaml', 'utf8'), + contentRootFile31 = fs.readFileSync(schemaFromResponse + '/root3_1.yaml', 'utf8'), + user = fs.readFileSync(schemaFromResponse + '/schemas/user.yaml', 'utf8'), + expected = fs.readFileSync(schemaFromResponse + '/expected.json', 'utf8'), + input = { + type: 'multiFile', + rootFiles: [ + { + path: '/root3_1.yaml' + }, + { + path: '/root.yaml' + } + ], + data: [ + { + path: '/root.yaml', + content: contentRootFile + }, + { + path: '/root3_1.yaml', + content: contentRootFile31 + }, + { + path: '/schemas/user.yaml', + content: user + } + ], + 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(res.output.data.length).to.equal(1); + expect(JSON.stringify(res.output.data[0].bundledContent, null, 2)).to.be.equal(expected); + }); }); @@ -1180,4 +1221,18 @@ describe('getReferences method when node does not have any reference', function( expect(error.message).to.equal('"Data" parameter should be provided'); } }); + + it('should return error when "type" parameter is not multiFile', async function () { + try { + await Converter.bundle({ + type: 'folder', + bundleFormat: 'JSON', + data: [] + }); + } + catch (error) { + expect(error).to.not.be.undefined; + expect(error.message).to.equal('"Type" parameter value allowed is multiFile'); + } + }); }); diff --git a/test/unit/detectRoot.test.js b/test/unit/detectRoot.test.js index 5266258..34b07f9 100644 --- a/test/unit/detectRoot.test.js +++ b/test/unit/detectRoot.test.js @@ -32,7 +32,7 @@ describe('detectRoot method', function() { expect(res).to.not.be.empty; expect(res.result).to.be.true; expect(res.output.data[0].path).to.equal('/petstore.yaml'); - expect(res.output.specification.version).to.equal('3.0.0'); + expect(res.output.specification.version).to.equal('3.0'); }); it('should return one root 3.0 correctly', async function() { From fcf43811d09b73e4abdfec283c82ea9b604b4007 Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 8 Jun 2022 17:07:57 -0500 Subject: [PATCH 3/3] Update bundle.test.js --- test/unit/bundle.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/bundle.test.js b/test/unit/bundle.test.js index 7986c6f..7fb6c0c 100644 --- a/test/unit/bundle.test.js +++ b/test/unit/bundle.test.js @@ -799,6 +799,7 @@ describe('bundle files method - 3.0', function () { 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);