diff --git a/src/plugins/validation/2and3/semantic-validators/operation-ids.js b/src/plugins/validation/2and3/semantic-validators/operation-ids.js index 9dc3b7509..976c42e34 100644 --- a/src/plugins/validation/2and3/semantic-validators/operation-ids.js +++ b/src/plugins/validation/2and3/semantic-validators/operation-ids.js @@ -112,7 +112,7 @@ module.exports.validate = function({ resolvedSpec }) { // If PATCH operation doesn't exist for path, POST operationId should start with "update" if ( !allPathOperations.includes('patch') && - !operationId.match(/^(update[a-zA-Z0-9_]+/m) + !operationId.match(/^update[a-zA-Z0-9_]+/m) ) { checkPassed = false; verbs.push('update'); diff --git a/src/plugins/validation/oas3/semantic-validators/openapi.js b/src/plugins/validation/oas3/semantic-validators/openapi.js new file mode 100644 index 000000000..6ddb3bb09 --- /dev/null +++ b/src/plugins/validation/oas3/semantic-validators/openapi.js @@ -0,0 +1,41 @@ +// Assertation 1: +// check if openapi field exist + +// Assertation 2: +// make sure the field is of type string + +// Assertation 3: +// make sure the string follows semantic versioning 2.0.0 + +module.exports.validate = function({ jsSpec }) { + const errors = []; + const warnings = []; + + // Regex taken from Semantic Versioning 2.0.0 documentation to check if string follows Semantic Versioning + // https://semver.org/ + // Regex from: https://regex101.com/r/vkijKf/1/ + + const semverRegex = new RegExp( + /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/gm + ); + + const openapi = jsSpec.openapi; + + if (!openapi) { + errors.push({ + path: ['openapi'], + message: 'API definition must have an `openapi` field' + }); + } else if (typeof openapi !== 'string') { + errors.push({ + path: ['openapi'], + message: 'API definition must have an `openapi` field as type string' + }); + } else if (!openapi.match(semverRegex)) { + errors.push({ + path: ['openapi'], + message: '`openapi` string must follow Semantic Versioning 2.0.0' + }); + } + return { errors, warnings }; +}; diff --git a/src/plugins/validation/swagger2/semantic-validators/swagger.js b/src/plugins/validation/swagger2/semantic-validators/swagger.js new file mode 100644 index 000000000..b9b176113 --- /dev/null +++ b/src/plugins/validation/swagger2/semantic-validators/swagger.js @@ -0,0 +1,33 @@ +// Assertation 1: +// check if swagger field exist + +// Assertation 2: +// make sure the swagger field is of type string + +// Assertation 3: +// make sure the value of swagger field must be "2.0" + +module.exports.validate = function({ jsSpec }) { + const errors = []; + const warnings = []; + + const swagger = jsSpec.swagger; + + if (!swagger) { + errors.push({ + path: ['swagger'], + message: 'API definition must have an `swagger` field' + }); + } else if (typeof swagger !== 'string') { + errors.push({ + path: ['swagger'], + message: 'API definition must have an `swagger` field as type string' + }); + } else if (swagger !== '2.0') { + errors.push({ + path: ['swagger'], + message: '`swagger` string must have the value `2.0`' + }); + } + return { errors, warnings }; +}; diff --git a/test/plugins/validation/2and3/operation-ids.js b/test/plugins/validation/2and3/operation-ids.js index 924f3f92a..0f5fda485 100644 --- a/test/plugins/validation/2and3/operation-ids.js +++ b/test/plugins/validation/2and3/operation-ids.js @@ -111,7 +111,7 @@ describe('validation plugin - semantic - operation-ids', function() { }, '/coffee': { get: { - operationId: 'get books' + operationId: 'get_books' }, post: { operationId: 'change_books' @@ -158,9 +158,6 @@ describe('validation plugin - semantic - operation-ids', function() { }, put: { operationId: 'changeCoffee2' - }, - patch: { - operationId: 'changeCoffee3' } } } diff --git a/test/plugins/validation/oas3/openapi.js b/test/plugins/validation/oas3/openapi.js new file mode 100644 index 000000000..02c40cda8 --- /dev/null +++ b/test/plugins/validation/oas3/openapi.js @@ -0,0 +1,46 @@ +const expect = require('expect'); +const { + validate +} = require('../../../../src/plugins/validation/oas3/semantic-validators/openapi'); + +describe('validation plugin - semantic - openapi', () => { + //this is for openapi object + it('should return an error when an API definition does not have openapi field', () => { + const spec = { + Openapi: '3.0.0' + }; + + const res = validate({ jsSpec: spec }); + expect(res.errors.length).toEqual(1); + expect(res.errors[0].path).toEqual(['openapi']); + expect(res.errors[0].message).toEqual( + 'API definition must have an `openapi` field' + ); + }); + + it('should return an error when an openapi field is not a string', () => { + const spec = { + openapi: 123 + }; + + const res = validate({ jsSpec: spec }); + expect(res.errors.length).toEqual(1); + expect(res.errors[0].path).toEqual(['openapi']); + expect(res.errors[0].message).toEqual( + 'API definition must have an `openapi` field as type string' + ); + }); + + it('should return an error when an openapi does not conform to semantic versioning 2.0.0', () => { + const spec = { + openapi: 'v1.0.10' + }; + + const res = validate({ jsSpec: spec }); + expect(res.errors.length).toEqual(1); + expect(res.errors[0].path).toEqual(['openapi']); + expect(res.errors[0].message).toEqual( + '`openapi` string must follow Semantic Versioning 2.0.0' + ); + }); +}); diff --git a/test/plugins/validation/swagger2/swagger.js b/test/plugins/validation/swagger2/swagger.js new file mode 100644 index 000000000..5f06cc742 --- /dev/null +++ b/test/plugins/validation/swagger2/swagger.js @@ -0,0 +1,46 @@ +const expect = require('expect'); +const { + validate +} = require('../../../../src/plugins/validation/swagger2/semantic-validators/swagger'); + +describe('validation plugin - semantic - swagger', () => { + //this is for openapi object + it('should return an error when an API definition does not have swagger field', () => { + const spec = { + Swagger: '2.0' + }; + + const res = validate({ jsSpec: spec }); + expect(res.errors.length).toEqual(1); + expect(res.errors[0].path).toEqual(['swagger']); + expect(res.errors[0].message).toEqual( + 'API definition must have an `swagger` field' + ); + }); + + it('should return an error when an swagger field is not a string', () => { + const spec = { + swagger: 123 + }; + + const res = validate({ jsSpec: spec }); + expect(res.errors.length).toEqual(1); + expect(res.errors[0].path).toEqual(['swagger']); + expect(res.errors[0].message).toEqual( + 'API definition must have an `swagger` field as type string' + ); + }); + + it('should return an error when an swagger field does not have value `2.0`', () => { + const spec = { + swagger: '2.0.0' + }; + + const res = validate({ jsSpec: spec }); + expect(res.errors.length).toEqual(1); + expect(res.errors[0].path).toEqual(['swagger']); + expect(res.errors[0].message).toEqual( + '`swagger` string must have the value `2.0`' + ); + }); +});