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.
This commit is contained in:
Luis Tejeda
2022-02-09 12:46:31 -06:00
parent 02e22e8825
commit 50ba85ce68
8 changed files with 58 additions and 80 deletions

View File

@@ -6,10 +6,11 @@ module.exports = {
* OpenAPI 3.1 only openapi and info are always required, * 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. * but the document must also contain at least one of paths or webhooks or components.
* @param {Object} spec OpenAPI spec * @param {Object} spec OpenAPI spec
* @param {Object} options computed process options
* @return {Object} Validation result * @return {Object} Validation result
*/ */
validateSpec: function (spec) { validateSpec: function (spec, options) {
const includeWebhooksOption = options.includeWebhooks;
if (!spec.hasOwnProperty('openapi')) { if (!spec.hasOwnProperty('openapi')) {
return { return {
result: false, 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 { return {
result: false, result: false,
reason: 'Specification must contain either Paths, Webhooks or Components sections' reason: 'Specification must contain either Paths, Webhooks or Components sections'

View File

@@ -13,11 +13,12 @@ module.exports = {
/** /**
* Parses an OAS 3.1.X string/object as a YAML or JSON * 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 {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 * @returns {Object} - Contains the parsed JSON-version of the OAS spec, or an error
* @no-unit-test * @no-unit-test
*/ */
parseSpec: function (openApiSpec) { parseSpec: function (openApiSpec, options) {
return schemaUtilsCommon.parseSpec(openApiSpec, inputValidation31X); return schemaUtilsCommon.parseSpec(openApiSpec, inputValidation31X, options);
}, },
inputValidation: inputValidation31X, inputValidation: inputValidation31X,

View File

@@ -10,10 +10,11 @@ module.exports = {
* Parses an OAS string/object as a YAML or JSON * 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 {YAML/JSON} openApiSpec - The OAS 3.x specification specified in either YAML or JSON
* @param {object} inputValidation - Concrete validator according to version * @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 * @returns {Object} - Contains the parsed JSON-version of the OAS spec, or an error
* @no-unit-test * @no-unit-test
*/ */
parseSpec: function (openApiSpec, inputValidation) { parseSpec: function (openApiSpec, inputValidation, options) {
var openApiObj = openApiSpec, var openApiObj = openApiSpec,
obj, obj,
rootValidation; rootValidation;
@@ -32,7 +33,7 @@ module.exports = {
// spec is a valid JSON object at this point // spec is a valid JSON object at this point
// Validate the root level object for semantics // Validate the root level object for semantics
rootValidation = inputValidation.validateSpec(openApiObj); rootValidation = inputValidation.validateSpec(openApiObj, options);
if (!rootValidation.result) { if (!rootValidation.result) {
return { return {
result: false, result: false,

View File

@@ -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 /** Converts OpenAPI input to OpenAPI Object
* @param {String} openApiSpec OpenAPI input in string * @param {String} openApiSpec OpenAPI input in string
* @returns {Object} oasObject * @returns {Object} oasObject
@@ -129,10 +77,11 @@ module.exports = {
* *
* @param {Array} input input object that contains files array * @param {Array} input input object that contains files array
* @param {Object} inputValidation Validator according to version * @param {Object} inputValidation Validator according to version
* @param {Object} options computed process options
* @param {Object} files Files map * @param {Object} files Files map
* @return {String} rootFile * @return {String} rootFile
*/ */
getRootFiles: function (input, inputValidation, files = {}) { getRootFiles: function (input, inputValidation, options, files = {}) {
let rootFilesArray = [], let rootFilesArray = [],
filesPathArray = input.data, filesPathArray = input.data,
origin = input.origin || ''; origin = input.origin || '';
@@ -160,7 +109,7 @@ module.exports = {
else { else {
throw new Error(obj.reason); throw new Error(obj.reason);
} }
if (inputValidation.validateSpec(oasObject).result) { if (inputValidation.validateSpec(oasObject, options).result) {
rootFilesArray.push(filePath.fileName); rootFilesArray.push(filePath.fileName);
} }
} }

View File

@@ -108,7 +108,7 @@ class SchemaPack {
return this.validationResult; return this.validationResult;
} }
specParseResult = concreteUtils.parseSpec(json); specParseResult = concreteUtils.parseSpec(json, this.computedOptions);
if (!specParseResult.result) { if (!specParseResult.result) {
// validation failed // validation failed
@@ -190,7 +190,7 @@ class SchemaPack {
} }
try { try {
rootFiles = parse.getRootFiles(input, concreteUtils.inputValidation, files); rootFiles = parse.getRootFiles(input, concreteUtils.inputValidation, this.computedOptions, files);
} }
catch (e) { catch (e) {
return cb(null, { return cb(null, {

View File

@@ -69,44 +69,64 @@ const { expect } = require('chai'),
describe('validateSpec method', function () { describe('validateSpec method', function () {
it('should return true with a valid simple spec with webhooks', 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; expect(validationResult.result).to.be.true;
}); });
it('should return true with a valid simple spec with paths', function () { 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; expect(validationResult.result).to.be.true;
}); });
it('should return true with a valid simple spec with components', function () { 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; expect(validationResult.result).to.be.true;
}); });
it('should return true with a valid simple spec with components webhooks y paths', function () { 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; expect(validationResult.result).to.be.true;
}); });
it('should return false with an invalid input without openapi field', function () { 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.result).to.be.false;
expect(validationResult.reason) expect(validationResult.reason)
.to.equal('Specification must contain a semantic version number of the OAS specification'); .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 () { 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.result).to.be.false;
expect(validationResult.reason) expect(validationResult.reason)
.to.equal('Specification must contain an Info Object for the meta-data of the API'); .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 () { 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.result).to.be.false;
expect(validationResult.reason) expect(validationResult.reason)
.to.equal('Specification must contain either Paths, Webhooks or Components sections'); .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;
});
}); });

View File

@@ -7,7 +7,7 @@ const { expect } = require('chai'),
describe('parseSpec method', function () { describe('parseSpec method', function () {
it('should return true and a parsed specification', function () { it('should return true and a parsed specification', function () {
let fileContent = fs.readFileSync(valid31xFolder + '/webhooks.json', 'utf8'); 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.result).to.be.true;
expect(parsedSpec.openapi.openapi).to.equal('3.1.0'); expect(parsedSpec.openapi.openapi).to.equal('3.1.0');
expect(parsedSpec.openapi.webhooks).to.not.be.undefined; 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 () { it('should return false and invalid format message when input content is sent', function () {
let fileContent = fs.readFileSync(invalid31xFolder + '/empty-spec.yaml', 'utf8'); 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.result).to.be.false;
expect(parsedSpec.reason).to.equal('Invalid format. Input must be in YAML or JSON format.'); 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 () { it('should return false and Spec must contain info object', function () {
let fileContent = fs.readFileSync(invalid31xFolder + '/invalid-no-info.json', 'utf8'); 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.result).to.be.false;
expect(parsedSpec.reason).to.equal('Specification must contain an Info Object for the meta-data of the API'); expect(parsedSpec.reason).to.equal('Specification must contain an Info Object for the meta-data of the API');
}); });

View File

@@ -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'), const fileSource = path.join(__dirname, OPENAPI_31_FOLDER + '/webhooks/payments-webhooks.yaml'),
fileData = fs.readFileSync(fileSource, 'utf8'), fileData = fs.readFileSync(fileSource, 'utf8'),
input = { input = {
@@ -676,12 +676,10 @@ describe('Webhooks support', function() {
data: fileData data: fileData
}, },
converter = new SchemaPack(input, { includeWebhooks: false }); converter = new SchemaPack(input, { includeWebhooks: false });
expect(converter.validated).to.be.false;
converter.convert((err, result) => { expect(converter.validationResult.result).to.be.false;
expect(err).to.be.null; expect(converter.validationResult.reason)
expect(result.result).to.be.true; .to.equal('Specification must contain Paths Object for the available operational paths');
expect(result.output[0].data.item.length).to.eql(0);
});
}); });
it('Should resolve correctly a file with only webhooks, folderStrategy as tag', function() { it('Should resolve correctly a file with only webhooks, folderStrategy as tag', function() {