Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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: 'The custom method name must use camelCase format. Method name: MethodName. 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 name must use camelCase format. Method name: MethodName. 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 name must use camelCase format. Method name: method_name. 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 name must use camelCase format. Method name: method_name. 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,26 @@
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';
const ERROR_MESSAGE = 'The custom method name must use camelCase format.';

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: `${ERROR_MESSAGE} Method name: ${methodName}.` }];
}
};
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