From e2e8af959c378bd632f2987b1702b67e42864f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A8ve=20Sfartz?= Date: Thu, 3 Oct 2024 15:41:38 +0200 Subject: [PATCH] updates to the multi-version function to include corner cases, and organized an exception list --- contract.js | 29 ++++++++++++++++------------- functions/multiVersion.js | 20 +++++++++++--------- util/getVersion.js | 9 +++++---- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/contract.js b/contract.js index 9d2d90f..5c2380f 100644 --- a/contract.js +++ b/contract.js @@ -37,7 +37,7 @@ export default { 'oas3-schema': 'error', 'oas2-schema': 'error', 'oas3-missing-schema-definition': { - 'description': 'There is no schema attribute for a component.', + 'description': 'a schema must describe the structure of responses (except 204s) and request bodies if any', 'message': '{{description}}; {{error}}', 'severity': 'error', 'formats': [oas3], @@ -53,7 +53,7 @@ export default { }, }, 'oas2-missing-schema-definition': { - 'description': 'There is no schema attribute for a component.', + 'description': 'a schema must describe the structure of responses (except 204s) and request bodies if any', 'message': '{{description}}; {{error}}', 'severity': 'error', 'formats': [oas2], @@ -69,7 +69,7 @@ export default { }, }, 'general-schema-definition': { - 'description': 'Some of the defined schema use object as a final field when describing their object structure', + 'description': 'schemas must be fully defined and should not use \'object\' as a final type', 'message': '{{description}}; {{error}}', 'severity': 'error', 'given': [ @@ -87,7 +87,7 @@ export default { ], }, 'oas3-missing-returned-representation': { - 'description': '2XX (except 204) responses must have a response schema defined', + 'description': 'a response schema must be defined for 2xx responses (except 204)', 'message': '{{description}}; {{error}}', 'severity': 'error', 'formats': [oas3], @@ -100,7 +100,7 @@ export default { }, }, 'oas2-missing-returned-representation': { - 'description': '2XX (except 204) responses must have a response schema defined.', + 'description': 'a response schema must be defined for 2xx responses (except 204)', 'message': '{{description}}; {{error}}', 'severity': 'error', 'formats': [oas2], @@ -113,7 +113,7 @@ export default { }, }, 'success-status-code': { - 'description': 'For every operation in the OAS document, there should be at least one success status code defined. A successful status code is in the 1xx, 2xx or 3xx range series, and generally a 200, 201 or 204.', + 'description': 'responses must include a success status code in the 1xx, 2xx or 3xx range (generally 200, 201 or 204)', 'message': '{{description}}; {{error}}', 'severity': 'error', 'given': '$.paths.*.*.responses', @@ -125,7 +125,7 @@ export default { }, }, 'error-status-code': { - 'description': 'There should be at least one error status code either 4xx or 5xx.', + 'description': 'responses should include at least one error status code (4xx or 5xx)', 'message': '{{description}}; {{error}}', 'severity': 'warn', 'given': '$.paths.*.*.responses', @@ -141,7 +141,7 @@ export default { }, }, 'oas2-meta-info': { - 'description': 'Some meta fields must be present.', + 'description': 'some meta fields are missing', 'message': '{{description}}; {{error}}', 'severity': 'error', 'formats': [oas2], @@ -162,8 +162,8 @@ export default { }, }, }, - 'multi-versions-server-url-missing-version': { - 'description': 'No version is specified in the server object of the OpenAPI Document. Best practices recommend specifying the version in the server object only once', + 'server-url-missing-version': { + 'description': 'the API version should be specified in the \'server.url\' field', 'message': '{{description}}; {{error}}', 'severity': 'warn', 'given': [ @@ -172,12 +172,12 @@ export default { 'then': { 'function': multiVersion, 'functionOptions': { - 'check': 'server-url-missing', + 'check': 'server-url-missing' }, }, }, 'multi-versions': { - 'description': 'should only contain a single API version at a time.', + 'description': 'all paths should reference a single API version', 'message': '{{description}}; {{error}}', 'severity': 'error', 'given': [ @@ -185,10 +185,13 @@ export default { ], 'then': { 'function': multiVersion, + 'functionOptions': { + 'exceptions' : [ "networks/{networkId}/switch/dhcp/v4" ] + }, }, }, 'operationId-required-and-unique': { - 'description': 'operationId must be required and uniq', + 'description': 'an \'operationId\' field must be present for every operation and must be unique', 'message': '{{description}}; {{error}}', 'severity': 'error', 'given': [ diff --git a/functions/multiVersion.js b/functions/multiVersion.js index f535875..675ff1d 100644 --- a/functions/multiVersion.js +++ b/functions/multiVersion.js @@ -21,12 +21,6 @@ import { isObject } from '../util/funcUtils.js'; import getVersion from '../util/getVersion.js'; -// Corner cases of API paths that are indeed versions on the url BUT should not be flagged by the rule -const PATH_EXCEPTIONS = [ - // Meraki: /networks/{networkId}/switch/dhcp/v4/servers/seen - "dhcp/v1" , -]; - /** * Checks if there is only one version in server urls and paths. @@ -86,16 +80,24 @@ export default function (targetVal, opts) { let pathFirstVersion = ''; for (const { path } of getAllPaths(paths)) { - const version = getVersion(path, PATH_EXCEPTIONS); + // Is there a version identified on the path, considering the path fragments exceptions if any + let version; + if (opts && opts.exceptions) { + version = getVersion(path, opts.exceptions); + } + else { + version = getVersion(path); + } if (version === '') { continue; } + // Check if the version detected is acceptable if (pathFirstVersion === '') { if (serverFirstVersion !== '') { results.push({ - message: 'path should not have version while server object has one.', + message: `the path should not include a version number when the \'servers.url\' attribute already does: \'servers.url\' version detected is /${serverFirstVersion}`, path: ['paths', path], }); } @@ -105,7 +107,7 @@ export default function (targetVal, opts) { if (pathFirstVersion !== version) { results.push({ - message: 'multi versions in paths.', + message: `more than one version has been detected across paths: /${pathFirstVersion} and /${version}`, path: ['paths', path], }); } diff --git a/util/getVersion.js b/util/getVersion.js index c581957..07ea319 100644 --- a/util/getVersion.js +++ b/util/getVersion.js @@ -10,16 +10,17 @@ export default function getVersion(str, exceptionList) { } } - // The version regex is pretty complex to address most use cases at Cisco, check this asana issue for context: - // The regexp requires to remove leading or trailing '/' if any. - const versionRegex = /\b\/?v\d+(\.\d+)*\/?(\b|$)/; + // The version regex is pretty complex to address all use cases at Cisco + // Please check this Asana issue for full context and explorations: https://app.asana.com/0/1206872740398257/1206657704786474 + //const versionRegex = /\b\/?v\d+(\.\d+)*\/?(\b|$)/; // does not fully work, captures v6 as a version number for path: "/device/cedgecflowd/app-fwd-cflowd-v6-flows" + const versionRegex = /(?