From a61f6159ee8e2289dcba8796cd64364f89208625 Mon Sep 17 00:00:00 2001 From: Shuyu Chen Date: Wed, 12 Feb 2020 09:51:13 -0600 Subject: [PATCH 1/2] feat: validator should check for mandatory swagger or openapi field --- .../oas3/semantic-validators/openapi.js | 41 +++++++++++++++++ .../swagger2/semantic-validators/swagger.js | 33 +++++++++++++ test/plugins/validation/oas3/openapi.js | 46 +++++++++++++++++++ test/plugins/validation/swagger2/swagger.js | 46 +++++++++++++++++++ 4 files changed, 166 insertions(+) create mode 100644 src/plugins/validation/oas3/semantic-validators/openapi.js create mode 100644 src/plugins/validation/swagger2/semantic-validators/swagger.js create mode 100644 test/plugins/validation/oas3/openapi.js create mode 100644 test/plugins/validation/swagger2/swagger.js 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/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`' + ); + }); +}); From 2a8060aaf5d6c515a8006dd860ddcaa95ce2805c Mon Sep 17 00:00:00 2001 From: Shuyu Chen Date: Wed, 12 Feb 2020 11:53:18 -0600 Subject: [PATCH 2/2] added more test coverage increase code coverage --- .../validation/2and3/semantic-validators/operation-ids.js | 2 +- test/plugins/validation/2and3/operation-ids.js | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) 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/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' } } }