Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"dependencies": {
"@stoplight/spectral-cli": "^6.14.2",
"@stoplight/spectral-core": "^1.19.4",
"@stoplight/spectral-functions": "^1.9.3",
"@stoplight/spectral-ref-resolver": "^1.0.5",
"@stoplight/spectral-ruleset-bundler": "^1.6.1",
"eslint-plugin-jest": "^28.9.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,28 @@ testRule('xgen-IPA-109-custom-method-must-be-GET-or-POST', [
},
errors: [],
},
{
name: 'invalid methods with exception',
document: {
paths: {
'/d/{exampleId}:method': {
get: {},
post: {},
'x-xgen-IPA-exception': {
'xgen-IPA-109-custom-method-must-be-GET-or-POST': {},
},
},
'/d:method': {
get: {},
post: {},
'x-xgen-IPA-exception': {
'xgen-IPA-109-custom-method-must-be-GET-or-POST': {},
},
},
},
},
errors: [],
},
{
name: 'invalid methods',
document: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import testRule from './__helpers__/testRule';
import { DiagnosticSeverity } from '@stoplight/types';

testRule('xgen-IPA-109-custom-method-must-use-camel-case', [
{
name: 'valid methods',
document: {
paths: {
'/a/{exampleId}:methodName': {},
'/a:methodName': {},
},
},
errors: [],
},
{
name: 'invalid methods with exception',
document: {
paths: {
'/b/{exampleId}:MethodName': {
'x-xgen-IPA-exception': {
'xgen-IPA-109-custom-method-must-use-camel-case': {},
},
},
'/b:MethodName': {
'x-xgen-IPA-exception': {
'xgen-IPA-109-custom-method-must-use-camel-case': {},
},
},
},
},
errors: [],
},
{
name: 'invalid methods',
document: {
paths: {
'/a/{exampleId}:MethodName': {},
'/a:MethodName': {},
'/a/{exampleId}:method_name': {},
'/a:method_name': {},
},
},
errors: [
{
code: 'xgen-IPA-109-custom-method-must-use-camel-case',
message: 'The custom method must use camelCase format. http://go/ipa/109',
path: ['paths', '/a/{exampleId}:MethodName'],
severity: DiagnosticSeverity.Warning,
},
{
code: 'xgen-IPA-109-custom-method-must-use-camel-case',
message: 'The custom method must use camelCase format. http://go/ipa/109',
path: ['paths', '/a:MethodName'],
severity: DiagnosticSeverity.Warning,
},
{
code: 'xgen-IPA-109-custom-method-must-use-camel-case',
message: 'The custom method must use camelCase format. http://go/ipa/109',
path: ['paths', '/a/{exampleId}:method_name'],
severity: DiagnosticSeverity.Warning,
},
{
code: 'xgen-IPA-109-custom-method-must-use-camel-case',
message: 'The custom method must use camelCase format. http://go/ipa/109',
path: ['paths', '/a:method_name'],
severity: DiagnosticSeverity.Warning,
},
],
},
]);
9 changes: 9 additions & 0 deletions tools/spectral/ipa/rulesets/IPA-109.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

functions:
- eachCustomMethodMustBeGetOrPost
- eachCustomMethodMustUseCamelCase

rules:
xgen-IPA-109-custom-method-must-be-GET-or-POST:
Expand All @@ -12,3 +13,11 @@ rules:
given: '$.paths[*]'
then:
function: 'eachCustomMethodMustBeGetOrPost'

xgen-IPA-109-custom-method-must-use-camel-case:
description: 'The custom method must use camelCase format. http://go/ipa/109'
message: '{{error}} http://go/ipa/109'
severity: warn
given: '$.paths[*]'
then:
function: 'eachCustomMethodMustUseCamelCase'
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { isCustomMethod } from './utils/resourceEvaluation.js';
import { hasException } from './utils/exceptions.js';

const RULE_NAME = 'xgen-IPA-109-custom-method-must-be-GET-or-POST';
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'];
Expand All @@ -11,6 +13,10 @@ export default (input, opts, { path }) => {

if (!isCustomMethod(pathKey)) return;

if (hasException(input, RULE_NAME)) {
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));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { getCustomMethod, isCustomMethod } from './utils/resourceEvaluation.js';
import { hasException } from './utils/exceptions.js';
import { casing } from '@stoplight/spectral-functions';

const RULE_NAME = 'xgen-IPA-109-custom-method-must-use-camel-case';
const ERROR_MESSAGE = 'The custom method must use camelCase format.';
const ERROR_RESULT = [{ message: ERROR_MESSAGE }];

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;

if (hasException(input, RULE_NAME)) {
return;
}

let methodName = getCustomMethod(pathKey);
if (!methodName) {
return;
}

const isCamelCase = casing(methodName, { type: 'camel', disallowDigits: true });
if (isCamelCase !== undefined) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

undefined means that the methodName is validated against camelCase

return ERROR_RESULT;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const isCamelCase = casing(methodName, { type: 'camel', disallowDigits: true });
if (isCamelCase !== undefined) {
return ERROR_RESULT;
}
return casing(methodName, { type: 'camel', disallowDigits: true });

Copy link
Collaborator

Choose a reason for hiding this comment

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

If you want ot customize the error message, you can change the yaml here:
message: '{{error}} http://go/ipa/109' -> message: 'Custom error message here. http://go/ipa/109'

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 would return undefined if validated or something like {... use camel case} if not validated.
Also, we disregard returning ERROR_RESULT.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If you want ot customize the error message, you can change the yaml here:
message: '{{error}} http://go/ipa/109' -> message: 'Custom error message here. http://go/ipa/109'

I can send a custom message which includes the problematic method name

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah I see, yeah it would be nice to have {methodName} must use camelCase, let's go with original approach and add the evaluated string to the message 👍

};
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ export function isCustomMethod(path) {
return path.includes(':');
}

export function getCustomMethod(path) {
const [, methodName] = path.split(':');
return methodName;
}

/**
* Checks if a resource is a singleton resource ({@link https://docs.devprod.prod.corp.mongodb.com/ipa/113 IPA-113}) based on the paths for the
* resource. The resource may have custom methods. Use {@link getResourcePaths} to get all paths of a resource.
Expand Down
Loading