Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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,84 @@
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': {},
'/a/{exampleId}:': {},
'/a:': {},
},
},
errors: [
{
code: 'xgen-IPA-109-custom-method-must-use-camel-case',
message: 'MethodName 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: 'MethodName 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: 'method_name 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: 'method_name must use camelCase format. http://go/ipa/109',
path: ['paths', '/a:method_name'],
severity: DiagnosticSeverity.Warning,
},
{
code: 'xgen-IPA-109-custom-method-must-use-camel-case',
message: 'Custom method name cannot be empty or blank. http://go/ipa/109',
path: ['paths', '/a/{exampleId}:'],
severity: DiagnosticSeverity.Warning,
},
{
code: 'xgen-IPA-109-custom-method-must-use-camel-case',
message: 'Custom method name cannot be empty or blank. http://go/ipa/109',
path: ['paths', '/a:'],
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,25 @@
import { getCustomMethodName, 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';

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 = getCustomMethodName(pathKey);
if (methodName.length === 0 || methodName.trim().length === 0) {
return [{ message: 'Custom method name cannot be empty or blank.' }];
}

if (casing(methodName, { type: 'camel', disallowDigits: true })) {
return [{ message: `${methodName} must use camelCase format.` }];
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export function isCustomMethod(path) {
return path.includes(':');
}

export function getCustomMethodName(path) {
return path.split(':')[1];
}

/**
* 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