From 50ba85ce6894e291355d6b89c5f2d4d61b24f52c Mon Sep 17 00:00:00 2001 From: Luis Tejeda <46000487+LuisTejedaS@users.noreply.github.com> Date: Wed, 9 Feb 2022 12:46:31 -0600 Subject: [PATCH] Change validation input when version is 3.1.x if the option includeWebhooks is false and there are no paths (only webhooks or componentes) throw error. --- lib/31XUtils/inputValidation31X.js | 15 ++++- lib/31XUtils/schemaUtils31X.js | 5 +- lib/common/schemaUtilsCommon.js | 5 +- lib/parse.js | 57 +------------------ lib/schemapack.js | 4 +- .../31Xsupport/inputValidation31X.test.js | 34 ++++++++--- test/unit/31Xsupport/schemaUtils31X.test.js | 6 +- test/unit/x31schemapack.test.js | 12 ++-- 8 files changed, 58 insertions(+), 80 deletions(-) diff --git a/lib/31XUtils/inputValidation31X.js b/lib/31XUtils/inputValidation31X.js index 590833f..5f923a9 100644 --- a/lib/31XUtils/inputValidation31X.js +++ b/lib/31XUtils/inputValidation31X.js @@ -6,10 +6,11 @@ module.exports = { * OpenAPI 3.1 only openapi and info are always required, * but the document must also contain at least one of paths or webhooks or components. * @param {Object} spec OpenAPI spec + * @param {Object} options computed process options * @return {Object} Validation result */ - validateSpec: function (spec) { - + validateSpec: function (spec, options) { + const includeWebhooksOption = options.includeWebhooks; if (!spec.hasOwnProperty('openapi')) { return { result: false, @@ -24,7 +25,15 @@ module.exports = { }; } - if (!spec.hasOwnProperty('paths') && !spec.hasOwnProperty('webhooks') && !spec.hasOwnProperty('components')) { + if (!spec.hasOwnProperty('paths') && !includeWebhooksOption) { + return { + result: false, + reason: 'Specification must contain Paths Object for the available operational paths' + }; + } + + if (includeWebhooksOption && !spec.hasOwnProperty('paths') && + !spec.hasOwnProperty('webhooks') && !spec.hasOwnProperty('components')) { return { result: false, reason: 'Specification must contain either Paths, Webhooks or Components sections' diff --git a/lib/31XUtils/schemaUtils31X.js b/lib/31XUtils/schemaUtils31X.js index 9d1ec74..c1894f7 100644 --- a/lib/31XUtils/schemaUtils31X.js +++ b/lib/31XUtils/schemaUtils31X.js @@ -13,11 +13,12 @@ module.exports = { /** * Parses an OAS 3.1.X string/object as a YAML or JSON * @param {YAML/JSON} openApiSpec - The OAS 3.1.x specification specified in either YAML or JSON + * @param {Object} options computed process options * @returns {Object} - Contains the parsed JSON-version of the OAS spec, or an error * @no-unit-test */ - parseSpec: function (openApiSpec) { - return schemaUtilsCommon.parseSpec(openApiSpec, inputValidation31X); + parseSpec: function (openApiSpec, options) { + return schemaUtilsCommon.parseSpec(openApiSpec, inputValidation31X, options); }, inputValidation: inputValidation31X, diff --git a/lib/common/schemaUtilsCommon.js b/lib/common/schemaUtilsCommon.js index 3f7f871..d9619ad 100644 --- a/lib/common/schemaUtilsCommon.js +++ b/lib/common/schemaUtilsCommon.js @@ -10,10 +10,11 @@ module.exports = { * Parses an OAS string/object as a YAML or JSON * @param {YAML/JSON} openApiSpec - The OAS 3.x specification specified in either YAML or JSON * @param {object} inputValidation - Concrete validator according to version + * @param {Object} options computed process options * @returns {Object} - Contains the parsed JSON-version of the OAS spec, or an error * @no-unit-test */ - parseSpec: function (openApiSpec, inputValidation) { + parseSpec: function (openApiSpec, inputValidation, options) { var openApiObj = openApiSpec, obj, rootValidation; @@ -32,7 +33,7 @@ module.exports = { // spec is a valid JSON object at this point // Validate the root level object for semantics - rootValidation = inputValidation.validateSpec(openApiObj); + rootValidation = inputValidation.validateSpec(openApiObj, options); if (!rootValidation.result) { return { result: false, diff --git a/lib/parse.js b/lib/parse.js index a1008cb..2f337e0 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -34,58 +34,6 @@ module.exports = { } }, - /** - * Validate Spec to check if some of the required fields are present. - * - * @param {Object} spec OpenAPI spec - * @return {Object} Validation result - */ - validateSpec: function (spec) { - - // Checking for the all the required properties in the specification - if (!spec.hasOwnProperty('openapi')) { - return { - result: false, - reason: 'Specification must contain a semantic version number of the OAS specification' - }; - } - - if (!spec.hasOwnProperty('paths')) { - return { - result: false, - reason: 'Specification must contain Paths Object for the available operational paths' - }; - } - - if (!spec.hasOwnProperty('info')) { - return { - result: false, - reason: 'Specification must contain an Info Object for the meta-data of the API' - }; - } - if (!spec.info.hasOwnProperty('$ref')) { - if (!spec.info.hasOwnProperty('title')) { - return { - result: false, - reason: 'Specification must contain a title in order to generate a collection' - }; - } - - if (!spec.info.hasOwnProperty('version')) { - return { - result: false, - reason: 'Specification must contain a semantic version number of the API in the Info Object' - }; - } - } - - // Valid specification - return { - result: true, - openapi: spec - }; - }, - /** Converts OpenAPI input to OpenAPI Object * @param {String} openApiSpec OpenAPI input in string * @returns {Object} oasObject @@ -129,10 +77,11 @@ module.exports = { * * @param {Array} input input object that contains files array * @param {Object} inputValidation Validator according to version + * @param {Object} options computed process options * @param {Object} files Files map * @return {String} rootFile */ - getRootFiles: function (input, inputValidation, files = {}) { + getRootFiles: function (input, inputValidation, options, files = {}) { let rootFilesArray = [], filesPathArray = input.data, origin = input.origin || ''; @@ -160,7 +109,7 @@ module.exports = { else { throw new Error(obj.reason); } - if (inputValidation.validateSpec(oasObject).result) { + if (inputValidation.validateSpec(oasObject, options).result) { rootFilesArray.push(filePath.fileName); } } diff --git a/lib/schemapack.js b/lib/schemapack.js index 02e9166..c4227a3 100644 --- a/lib/schemapack.js +++ b/lib/schemapack.js @@ -108,7 +108,7 @@ class SchemaPack { return this.validationResult; } - specParseResult = concreteUtils.parseSpec(json); + specParseResult = concreteUtils.parseSpec(json, this.computedOptions); if (!specParseResult.result) { // validation failed @@ -190,7 +190,7 @@ class SchemaPack { } try { - rootFiles = parse.getRootFiles(input, concreteUtils.inputValidation, files); + rootFiles = parse.getRootFiles(input, concreteUtils.inputValidation, this.computedOptions, files); } catch (e) { return cb(null, { diff --git a/test/unit/31Xsupport/inputValidation31X.test.js b/test/unit/31Xsupport/inputValidation31X.test.js index 0800cb7..b90aafe 100644 --- a/test/unit/31Xsupport/inputValidation31X.test.js +++ b/test/unit/31Xsupport/inputValidation31X.test.js @@ -69,44 +69,64 @@ const { expect } = require('chai'), describe('validateSpec method', function () { it('should return true with a valid simple spec with webhooks', function () { - const validationResult = validateSpec(correctMockedEntryWH); + const validationResult = validateSpec(correctMockedEntryWH, { includeWebhooks: true }); expect(validationResult.result).to.be.true; }); it('should return true with a valid simple spec with paths', function () { - const validationResult = validateSpec(correctMockedEntryPath); + const validationResult = validateSpec(correctMockedEntryPath, { includeWebhooks: true }); expect(validationResult.result).to.be.true; }); it('should return true with a valid simple spec with components', function () { - const validationResult = validateSpec(correctMockedEntryComponent); + const validationResult = validateSpec(correctMockedEntryComponent, { includeWebhooks: true }); expect(validationResult.result).to.be.true; }); it('should return true with a valid simple spec with components webhooks y paths', function () { - const validationResult = validateSpec(correctMockedEntry); + const validationResult = validateSpec(correctMockedEntry, { includeWebhooks: true }); expect(validationResult.result).to.be.true; }); it('should return false with an invalid input without openapi field', function () { - const validationResult = validateSpec(incorrectMockedEntryNoOpenapi); + const validationResult = validateSpec(incorrectMockedEntryNoOpenapi, { includeWebhooks: true }); expect(validationResult.result).to.be.false; expect(validationResult.reason) .to.equal('Specification must contain a semantic version number of the OAS specification'); }); it('should return false with an invalid input without info field', function () { - const validationResult = validateSpec(incorrectMockedEntryNoInfo); + const validationResult = validateSpec(incorrectMockedEntryNoInfo, { includeWebhooks: true }); expect(validationResult.result).to.be.false; expect(validationResult.reason) .to.equal('Specification must contain an Info Object for the meta-data of the API'); }); it('should return false with an invalid input without path components and webhooks', function () { - const validationResult = validateSpec(incorrectMockedEntryNOPathsComponentsWebhooks); + const validationResult = validateSpec(incorrectMockedEntryNOPathsComponentsWebhooks, { includeWebhooks: true }); expect(validationResult.result).to.be.false; expect(validationResult.reason) .to.equal('Specification must contain either Paths, Webhooks or Components sections'); }); + it('should return false with a valid simple spec with only webhooks but include webhooks is false', function () { + const validationResult = validateSpec(correctMockedEntryWH, { includeWebhooks: false }); + expect(validationResult.result).to.be.false; + }); + + it('should return true with a valid simple spec with paths and webhooks include webhooks is false', function () { + const validationResult = validateSpec(correctMockedEntryPath, { includeWebhooks: false }); + expect(validationResult.result).to.be.true; + }); + + it('should return false with a valid simple spec with only components and include webhooks is false', function () { + const validationResult = validateSpec(correctMockedEntryComponent, { includeWebhooks: false }); + expect(validationResult.result).to.be.false; + }); + + it('should return false with input without path components or webhooks include webhooks is false', function () { + const validationResult = validateSpec(incorrectMockedEntryNOPathsComponentsWebhooks, { includeWebhooks: false }); + expect(validationResult.result).to.be.false; + }); + }); diff --git a/test/unit/31Xsupport/schemaUtils31X.test.js b/test/unit/31Xsupport/schemaUtils31X.test.js index 1a2ba01..fd5ac87 100644 --- a/test/unit/31Xsupport/schemaUtils31X.test.js +++ b/test/unit/31Xsupport/schemaUtils31X.test.js @@ -7,7 +7,7 @@ const { expect } = require('chai'), describe('parseSpec method', function () { it('should return true and a parsed specification', function () { let fileContent = fs.readFileSync(valid31xFolder + '/webhooks.json', 'utf8'); - const parsedSpec = concreteUtils.parseSpec(fileContent); + const parsedSpec = concreteUtils.parseSpec(fileContent, { includeWebhooks: true }); expect(parsedSpec.result).to.be.true; expect(parsedSpec.openapi.openapi).to.equal('3.1.0'); expect(parsedSpec.openapi.webhooks).to.not.be.undefined; @@ -15,14 +15,14 @@ describe('parseSpec method', function () { it('should return false and invalid format message when input content is sent', function () { let fileContent = fs.readFileSync(invalid31xFolder + '/empty-spec.yaml', 'utf8'); - const parsedSpec = concreteUtils.parseSpec(fileContent); + const parsedSpec = concreteUtils.parseSpec(fileContent, { includeWebhooks: false }); expect(parsedSpec.result).to.be.false; expect(parsedSpec.reason).to.equal('Invalid format. Input must be in YAML or JSON format.'); }); it('should return false and Spec must contain info object', function () { let fileContent = fs.readFileSync(invalid31xFolder + '/invalid-no-info.json', 'utf8'); - const parsedSpec = concreteUtils.parseSpec(fileContent); + const parsedSpec = concreteUtils.parseSpec(fileContent, { includeWebhooks: false }); expect(parsedSpec.result).to.be.false; expect(parsedSpec.reason).to.equal('Specification must contain an Info Object for the meta-data of the API'); }); diff --git a/test/unit/x31schemapack.test.js b/test/unit/x31schemapack.test.js index 9b673df..5ff3d87 100644 --- a/test/unit/x31schemapack.test.js +++ b/test/unit/x31schemapack.test.js @@ -668,7 +668,7 @@ describe('Webhooks support', function() { }); }); - it('Should resolve a file with only webhooks but includeWebhooks is false', function() { + it('Should return the validation error with a file with only webhooks but includeWebhooks is false', function () { const fileSource = path.join(__dirname, OPENAPI_31_FOLDER + '/webhooks/payments-webhooks.yaml'), fileData = fs.readFileSync(fileSource, 'utf8'), input = { @@ -676,12 +676,10 @@ describe('Webhooks support', function() { data: fileData }, converter = new SchemaPack(input, { includeWebhooks: false }); - - converter.convert((err, result) => { - expect(err).to.be.null; - expect(result.result).to.be.true; - expect(result.output[0].data.item.length).to.eql(0); - }); + expect(converter.validated).to.be.false; + expect(converter.validationResult.result).to.be.false; + expect(converter.validationResult.reason) + .to.equal('Specification must contain Paths Object for the available operational paths'); }); it('Should resolve correctly a file with only webhooks, folderStrategy as tag', function() {