Skip to content
Merged
122 changes: 122 additions & 0 deletions tools/spectral/ipa/__tests__/eachCustomMethodMustBeGetOrPost.test.js
Original file line number Diff line number Diff line change
@@ -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,
},
],
},
]);
1 change: 1 addition & 0 deletions tools/spectral/ipa/ipa-spectral.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
extends:
- ./rulesets/IPA-102.yaml
- ./rulesets/IPA-104.yaml
- ./rulesets/IPA-109.yaml
14 changes: 14 additions & 0 deletions tools/spectral/ipa/rulesets/IPA-109.yaml
Original file line number Diff line number Diff line change
@@ -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'
Original file line number Diff line number Diff line change
@@ -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 EXCEPTION_EXTENSION_KEY = 'x-xgen-IPA-exception';

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;

//Exclude exception extension key
let keys = Object.keys(input);
const httpMethods = keys.filter((key) => key !== EXCEPTION_EXTENSION_KEY);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lovisaberggren I excluded the exception extension key here, but in the opposite I can only check the list of predefined HTTP methods as well

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Yeah perhaps the opposite is more stable, as there may be other extensions for example added to paths, and HTTP methods probably won't change a lot 😄


// Check for invalid methods
if (httpMethods.some((method) => !VALID_METHODS.includes(method))) {
return ERROR_RESULT;
}
Comment on lines +19 to +21
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this return error if the path has an exception extension?

Suggestion: use $.path[get,put,post,delete,options,head,patch,trace] to ignore any other objects than the methods, kinda like we do here:

- "#PathItem[get,put,post,delete,options,head,patch,trace]"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it would return error with an exception extension. Good catch!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"#PathItem[get,put,post,delete,options,head,patch,trace]" gives the object inside HTTP method. I will exclude x-xgen-IPA-exception


// Check for multiple valid methods
const validMethodCount = httpMethods.filter((method) => VALID_METHODS.includes(method)).length;

if (validMethodCount > 1) {
return ERROR_RESULT;
}
};
Loading