Skip to content

Commit

Permalink
updates to the multi-version function to include corner cases, and or…
Browse files Browse the repository at this point in the history
…ganized an exception list
  • Loading branch information
stsfartz committed Oct 3, 2024
1 parent 2c3d9b8 commit e2e8af9
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 26 deletions.
29 changes: 16 additions & 13 deletions contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -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],
Expand All @@ -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': [
Expand All @@ -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],
Expand All @@ -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],
Expand All @@ -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',
Expand All @@ -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',
Expand All @@ -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],
Expand All @@ -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': [
Expand All @@ -172,23 +172,26 @@ 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': [
'$',
],
'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': [
Expand Down
20 changes: 11 additions & 9 deletions functions/multiVersion.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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],
});
}
Expand All @@ -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],
});
}
Expand Down
9 changes: 5 additions & 4 deletions util/getVersion.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = /(?<![-\w])\/?v\d+(\.\d+)*\/?(\b|$)/; // does NOT capture v6 as a version number for path: "/device/cedgecflowd/app-fwd-cflowd-v6-flows"

let version = '';
const parts = str.match(versionRegex);
if (parts) {
version = parts[0];

// Remove leading or trailing '/'
// The latest regexp captures /v1 in some cases, let's remove leading or trailing '/' if any
version = version.charAt(0) === '/' ? version.slice(1) : version;
version = version.charAt(version.length - 1) === '/' ? version.slice(0, -1) : version;
}
Expand Down

0 comments on commit e2e8af9

Please sign in to comment.