Skip to content
Merged
114 changes: 114 additions & 0 deletions tools/spectral/ipa/__tests__/eachCustomMethodMustBeGetOrPost.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
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: {},
},
},
},
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,33 @@
import { isCustomMethod } from './utils/resourceEvaluation.js';

const ERROR_MESSAGE = 'The HTTP method for custom methods must be GET or POST.';
const VALID_METHODS = ['get', 'post'];

export default (paths) => {
// Collect all errors
const errors = [];

// Iterate through each path key and its corresponding path item
for (const [pathKey, pathItem] of Object.entries(paths)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah didn't realize that paths is not an array, I thought that the change would only make the input contain a single path and it's pathitems, not the whole paths object. Sorry about that! This makes the function a bit harder to read IMO, feel free to revert back to the previous approach if you want. WDYT?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Or perhaps try $.paths[*]

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It worked with $.paths[*] and using path from the context. Could you take another look and let me know how it looks?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

$.paths[*] gives you the object for each path, but it doesn't include the path keys themselves, so it's not quite enough on its own

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks! Looks good! Just one question on the logic

// Skip if not a custom method
if (!isCustomMethod(pathKey)) continue;

// Get HTTP methods for this path
const httpMethods = Object.keys(pathItem);

// Check for invalid methods
if (httpMethods.some((method) => !VALID_METHODS.includes(method))) {
errors.push({ path: ['paths', pathKey], message: ERROR_MESSAGE });
continue;
}

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

if (validMethodCount > 1) {
errors.push({ path: ['paths', pathKey], message: ERROR_MESSAGE });
}
}

return errors;
};
Loading