diff --git a/tools/spectral/ipa/__tests__/eachCustomMethodMustBeGetOrPost.test.js b/tools/spectral/ipa/__tests__/eachCustomMethodMustBeGetOrPost.test.js new file mode 100644 index 0000000000..369f61efcd --- /dev/null +++ b/tools/spectral/ipa/__tests__/eachCustomMethodMustBeGetOrPost.test.js @@ -0,0 +1,122 @@ +import testRule from './__helpers__/testRule'; +import { DiagnosticSeverity } from '@stoplight/types'; + +testRule('xgen-IPA-109-custom-method-must-be-GET-or-POST', [ + { + name: 'valid methods', + document: { + paths: { + '/a/{exampleId}:method': { + post: {}, + }, + '/a:method': { + post: {}, + }, + '/b/{exampleId}:method': { + get: {}, + }, + '/b:method': { + get: {}, + }, + '/c/{exampleId}:method': { + get: {}, + 'x-xgen-IPA-exception': {}, + }, + '/c:method': { + get: {}, + 'x-xgen-IPA-exception': {}, + }, + }, + }, + errors: [], + }, + { + name: 'invalid methods', + document: { + paths: { + '/a/{exampleId}:method': { + put: {}, + }, + '/a:method': { + put: {}, + }, + '/b/{exampleId}:method': { + get: {}, + put: {}, + }, + '/b:method': { + get: {}, + put: {}, + }, + '/c/{exampleId}:method': { + post: {}, + get: {}, + put: {}, + }, + '/c:method': { + post: {}, + get: {}, + put: {}, + }, + '/d/{exampleId}:method': { + post: {}, + get: {}, + }, + '/d:method': { + post: {}, + get: {}, + }, + }, + }, + errors: [ + { + code: 'xgen-IPA-109-custom-method-must-be-GET-or-POST', + message: 'The HTTP method for custom methods must be GET or POST. http://go/ipa/109', + path: ['paths', '/a/{exampleId}:method'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-109-custom-method-must-be-GET-or-POST', + message: 'The HTTP method for custom methods must be GET or POST. http://go/ipa/109', + path: ['paths', '/a:method'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-109-custom-method-must-be-GET-or-POST', + message: 'The HTTP method for custom methods must be GET or POST. http://go/ipa/109', + path: ['paths', '/b/{exampleId}:method'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-109-custom-method-must-be-GET-or-POST', + message: 'The HTTP method for custom methods must be GET or POST. http://go/ipa/109', + path: ['paths', '/b:method'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-109-custom-method-must-be-GET-or-POST', + message: 'The HTTP method for custom methods must be GET or POST. http://go/ipa/109', + path: ['paths', '/c/{exampleId}:method'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-109-custom-method-must-be-GET-or-POST', + message: 'The HTTP method for custom methods must be GET or POST. http://go/ipa/109', + path: ['paths', '/c:method'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-109-custom-method-must-be-GET-or-POST', + message: 'The HTTP method for custom methods must be GET or POST. http://go/ipa/109', + path: ['paths', '/d/{exampleId}:method'], + severity: DiagnosticSeverity.Warning, + }, + { + code: 'xgen-IPA-109-custom-method-must-be-GET-or-POST', + message: 'The HTTP method for custom methods must be GET or POST. http://go/ipa/109', + path: ['paths', '/d:method'], + severity: DiagnosticSeverity.Warning, + }, + ], + }, +]); diff --git a/tools/spectral/ipa/ipa-spectral.yaml b/tools/spectral/ipa/ipa-spectral.yaml index a948e1c544..789367fc96 100644 --- a/tools/spectral/ipa/ipa-spectral.yaml +++ b/tools/spectral/ipa/ipa-spectral.yaml @@ -1,4 +1,5 @@ extends: + - ./rulesets/IPA-005.yaml - ./rulesets/IPA-102.yaml - ./rulesets/IPA-104.yaml - - ./rulesets/IPA-005.yaml + - ./rulesets/IPA-109.yaml diff --git a/tools/spectral/ipa/rulesets/IPA-109.yaml b/tools/spectral/ipa/rulesets/IPA-109.yaml new file mode 100644 index 0000000000..48c1645e07 --- /dev/null +++ b/tools/spectral/ipa/rulesets/IPA-109.yaml @@ -0,0 +1,14 @@ +# IPA-109: Custom Methods +# http://go/ipa/109 + +functions: + - eachCustomMethodMustBeGetOrPost + +rules: + xgen-IPA-109-custom-method-must-be-GET-or-POST: + description: 'The HTTP method for custom methods must be GET or POST. http://go/ipa/109' + message: '{{error}} http://go/ipa/109' + severity: warn + given: '$.paths[*]' + then: + function: 'eachCustomMethodMustBeGetOrPost' diff --git a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js new file mode 100644 index 0000000000..bb7be025da --- /dev/null +++ b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js @@ -0,0 +1,29 @@ +import { isCustomMethod } from './utils/resourceEvaluation.js'; + +const ERROR_MESSAGE = 'The HTTP method for custom methods must be GET or POST.'; +const ERROR_RESULT = [{ message: ERROR_MESSAGE }]; +const VALID_METHODS = ['get', 'post']; +const HTTP_METHODS = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace']; + +export default (input, opts, { path }) => { + // Extract the path key (e.g., '/a/{exampleId}:method') from the JSONPath. + let pathKey = path[1]; + + if (!isCustomMethod(pathKey)) return; + + //Extract the keys which are equivalent of the http methods + let keys = Object.keys(input); + const httpMethods = keys.filter((key) => HTTP_METHODS.includes(key)); + + // Check for invalid methods + if (httpMethods.some((method) => !VALID_METHODS.includes(method))) { + return ERROR_RESULT; + } + + // Check for multiple valid methods + const validMethodCount = httpMethods.filter((method) => VALID_METHODS.includes(method)).length; + + if (validMethodCount > 1) { + return ERROR_RESULT; + } +};