Skip to content

Commit e237b66

Browse files
CLOUDP-305172: Refactor the rule implementations (#500)
1 parent e096c19 commit e237b66

17 files changed

+436
-210
lines changed

tools/spectral/ipa/__tests__/createMethodShouldNotHaveQueryParameters.test.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ const componentSchemas = {
1515
type: 'string',
1616
},
1717
},
18+
QueryParam2: {
19+
name: 'query-param-2',
20+
in: 'query',
21+
schema: {
22+
type: 'string',
23+
},
24+
},
1825
PathParam: {
1926
name: 'resource-id',
2027
in: 'path',
@@ -102,6 +109,9 @@ testRule('xgen-IPA-106-create-method-should-not-have-query-parameters', [
102109
{
103110
$ref: '#/components/parameters/QueryParam',
104111
},
112+
{
113+
$ref: '#/components/parameters/QueryParam2',
114+
},
105115
],
106116
},
107117
},
@@ -110,13 +120,20 @@ testRule('xgen-IPA-106-create-method-should-not-have-query-parameters', [
110120
errors: [
111121
{
112122
code: 'xgen-IPA-106-create-method-should-not-have-query-parameters',
113-
message: 'Create operations should not have query parameters. http://go/ipa/106',
123+
message: 'Input parameter [filter]: Create operations should not have query parameters. http://go/ipa/106',
114124
path: ['paths', '/resource', 'post'],
115125
severity: DiagnosticSeverity.Warning,
116126
},
117127
{
118128
code: 'xgen-IPA-106-create-method-should-not-have-query-parameters',
119-
message: 'Create operations should not have query parameters. http://go/ipa/106',
129+
message: 'Input parameter [query-param]: Create operations should not have query parameters. http://go/ipa/106',
130+
path: ['paths', '/resource2', 'post'],
131+
severity: DiagnosticSeverity.Warning,
132+
},
133+
{
134+
code: 'xgen-IPA-106-create-method-should-not-have-query-parameters',
135+
message:
136+
'Input parameter [query-param-2]: Create operations should not have query parameters. http://go/ipa/106',
120137
path: ['paths', '/resource2', 'post'],
121138
severity: DiagnosticSeverity.Warning,
122139
},

tools/spectral/ipa/rulesets/IPA-104.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ rules:
2222
description: 'The purpose of the Get method is to return data from a single resource. http://go/ipa/104'
2323
message: '{{error}} http://go/ipa/104'
2424
severity: warn
25-
given: '$.paths[*].get'
25+
given: '$.paths[*].get.responses[*].content'
2626
then:
27+
field: '@key'
2728
function: 'getMethodReturnsSingleResource'
2829
xgen-IPA-104-get-method-response-code-is-200:
2930
description: 'The Get method must return a 200 OK response. http://go/ipa/104'
Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { hasException } from './utils/exceptions.js';
2-
import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js';
2+
import {
3+
collectAdoption,
4+
collectAndReturnViolation,
5+
collectException,
6+
handleInternalError,
7+
} from './utils/collectionUtils.js';
38
import { isCustomMethodIdentifier } from './utils/resourceEvaluation.js';
49
import { resolveObject } from './utils/componentUtils.js';
510
import { getSchemaRef } from './utils/methodUtils.js';
@@ -11,28 +16,37 @@ const ERROR_MESSAGE_SCHEMA_REF = 'The response body schema is defined inline and
1116
export default (input, _, { path, documentInventory }) => {
1217
const oas = documentInventory.unresolved;
1318
const resourcePath = path[1];
14-
const contentMediaType = path[path.length - 1];
19+
const contentPerMediaType = resolveObject(oas, path);
1520

16-
if (isCustomMethodIdentifier(resourcePath) || !contentMediaType.endsWith('json')) {
21+
if (isCustomMethodIdentifier(resourcePath) || !input.endsWith('json') || !contentPerMediaType.schema) {
1722
return;
1823
}
1924

20-
const contentPerMediaType = resolveObject(oas, path);
21-
2225
if (hasException(contentPerMediaType, RULE_NAME)) {
2326
collectException(contentPerMediaType, RULE_NAME, path);
2427
return;
2528
}
2629

27-
if (contentPerMediaType.schema) {
30+
const errors = checkViolationsAndReturnErrors(contentPerMediaType, path);
31+
if (errors.length !== 0) {
32+
return collectAndReturnViolation(path, RULE_NAME, errors);
33+
}
34+
collectAdoption(path, RULE_NAME);
35+
};
36+
37+
function checkViolationsAndReturnErrors(contentPerMediaType, path) {
38+
try {
2839
const schema = contentPerMediaType.schema;
2940
const schemaRef = getSchemaRef(schema);
41+
3042
if (!schemaRef) {
31-
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_REF);
43+
return [{ path, message: ERROR_MESSAGE_SCHEMA_REF }];
3244
}
3345
if (!schemaRef.endsWith('Request')) {
34-
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_NAME);
46+
return [{ path, message: ERROR_MESSAGE_SCHEMA_NAME }];
3547
}
36-
collectAdoption(path, RULE_NAME);
48+
return [];
49+
} catch (e) {
50+
handleInternalError(RULE_NAME, path, e);
3751
}
38-
};
52+
}

tools/spectral/ipa/rulesets/functions/createMethodShouldNotHaveQueryParameters.js

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { hasException } from './utils/exceptions.js';
2-
import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js';
2+
import {
3+
collectAdoption,
4+
collectAndReturnViolation,
5+
collectException,
6+
handleInternalError,
7+
} from './utils/collectionUtils.js';
38
import { isCustomMethodIdentifier } from './utils/resourceEvaluation.js';
49

510
const RULE_NAME = 'xgen-IPA-106-create-method-should-not-have-query-parameters';
@@ -24,11 +29,26 @@ export default (input, _, { path }) => {
2429
return;
2530
}
2631

27-
for (const parameter of postMethod.parameters) {
28-
if (parameter.in === 'query' && !ignoredParameters.includes(parameter.name)) {
29-
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE);
30-
}
32+
const errors = checkViolationsAndReturnErrors(postMethod.parameters, path);
33+
if (errors.length !== 0) {
34+
return collectAndReturnViolation(path, RULE_NAME, errors);
3135
}
32-
3336
collectAdoption(path, RULE_NAME);
3437
};
38+
39+
function checkViolationsAndReturnErrors(postMethodParameters, path) {
40+
const errors = [];
41+
try {
42+
for (const parameter of postMethodParameters) {
43+
if (parameter.in === 'query' && !ignoredParameters.includes(parameter.name)) {
44+
errors.push({
45+
path: path,
46+
message: `Input parameter [${parameter.name}]: ${ERROR_MESSAGE}`,
47+
});
48+
}
49+
}
50+
return errors;
51+
} catch (e) {
52+
handleInternalError(RULE_NAME, path, e);
53+
}
54+
}

tools/spectral/ipa/rulesets/functions/deleteMethod204Response.js

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js';
1+
import {
2+
collectAdoption,
3+
collectAndReturnViolation,
4+
collectException,
5+
handleInternalError,
6+
} from './utils/collectionUtils.js';
27
import { hasException } from './utils/exceptions.js';
38

49
const RULE_NAME = 'xgen-IPA-108-delete-method-return-204-response';
@@ -17,9 +22,29 @@ export default (input, _, { path }) => {
1722
return;
1823
}
1924

20-
const responses = input.responses;
21-
if (!responses || !responses['204']) {
22-
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE);
25+
const errors = checkViolationsAndReturnErrors(input, path);
26+
if (errors.length !== 0) {
27+
return collectAndReturnViolation(path, RULE_NAME, errors);
2328
}
24-
return collectAdoption(path, RULE_NAME);
29+
collectAdoption(path, RULE_NAME);
2530
};
31+
32+
function checkViolationsAndReturnErrors(input, path) {
33+
try {
34+
console.log(input);
35+
const responses = input.responses;
36+
console.log(responses);
37+
// If there is no 204 response, return a violation
38+
if (!responses || !responses['204']) {
39+
return [{ path, message: ERROR_MESSAGE }];
40+
}
41+
42+
// If there are other 2xx responses that are not 204, return a violation
43+
if (Object.keys(responses).some((key) => key.startsWith('2') && key !== '204')) {
44+
return [{ path, message: ERROR_MESSAGE }];
45+
}
46+
return [];
47+
} catch (e) {
48+
handleInternalError(RULE_NAME, path, e);
49+
}
50+
}

tools/spectral/ipa/rulesets/functions/deleteMethodResponseShouldNotHaveSchema.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export default (input, _, { path }) => {
2323
}
2424

2525
// 2. Validation
26-
const errors = checkViolations(input, path);
26+
const errors = checkViolationsAndReturnErrors(input, path);
2727
if (errors.length > 0) {
2828
return collectAndReturnViolation(path, RULE_NAME, errors);
2929
}
@@ -37,7 +37,7 @@ export default (input, _, { path }) => {
3737
* @param {object} jsonPathArray - The jsonPathArray covering location in the OpenAPI schema
3838
* @return {Array<string>} - errors array ()
3939
*/
40-
function checkViolations(input, jsonPathArray) {
40+
function checkViolationsAndReturnErrors(input, jsonPathArray) {
4141
const errors = [];
4242
try {
4343
const successResponse = input;
Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import { isCustomMethodIdentifier } from './utils/resourceEvaluation.js';
22
import { hasException } from './utils/exceptions.js';
3-
import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js';
3+
import {
4+
collectAdoption,
5+
collectAndReturnViolation,
6+
collectException,
7+
handleInternalError,
8+
} from './utils/collectionUtils.js';
49

510
const RULE_NAME = 'xgen-IPA-109-custom-method-must-be-GET-or-POST';
611
const ERROR_MESSAGE = 'The HTTP method for custom methods must be GET or POST.';
712
const VALID_METHODS = ['get', 'post'];
813
const HTTP_METHODS = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'];
914

10-
export default (input, opts, { path }) => {
15+
export default (input, _, { path }) => {
1116
// Extract the path key (e.g., '/a/{exampleId}:method') from the JSONPath.
1217
let pathKey = path[1];
1318

@@ -20,21 +25,32 @@ export default (input, opts, { path }) => {
2025
return;
2126
}
2227

23-
//Extract the keys which are equivalent of the http methods
24-
let keys = Object.keys(input);
25-
const httpMethods = keys.filter((key) => HTTP_METHODS.includes(key));
26-
27-
// Check for invalid methods
28-
if (httpMethods.some((method) => !VALID_METHODS.includes(method))) {
29-
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE);
30-
}
31-
32-
// Check for multiple valid methods
33-
const validMethodCount = httpMethods.filter((method) => VALID_METHODS.includes(method)).length;
34-
35-
if (validMethodCount > 1) {
36-
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE);
28+
const errors = checkViolationsAndReturnErrors(input, path);
29+
if (errors.length !== 0) {
30+
return collectAndReturnViolation(path, RULE_NAME, errors);
3731
}
38-
3932
collectAdoption(path, RULE_NAME);
4033
};
34+
35+
function checkViolationsAndReturnErrors(input, path) {
36+
try {
37+
//Extract the keys which are equivalent of the http methods
38+
let keys = Object.keys(input);
39+
const httpMethods = keys.filter((key) => HTTP_METHODS.includes(key));
40+
41+
// Check for invalid methods
42+
if (httpMethods.some((method) => !VALID_METHODS.includes(method))) {
43+
return [{ path, message: ERROR_MESSAGE }];
44+
}
45+
46+
// Check for multiple valid methods
47+
const validMethodCount = httpMethods.filter((method) => VALID_METHODS.includes(method)).length;
48+
49+
if (validMethodCount > 1) {
50+
return [{ path, message: ERROR_MESSAGE }];
51+
}
52+
return [];
53+
} catch (e) {
54+
handleInternalError(RULE_NAME, path, e);
55+
}
56+
}
Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { getCustomMethodName, isCustomMethodIdentifier } from './utils/resourceEvaluation.js';
22
import { hasException } from './utils/exceptions.js';
33
import { casing } from '@stoplight/spectral-functions';
4-
import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js';
4+
import {
5+
collectAdoption,
6+
collectAndReturnViolation,
7+
collectException,
8+
handleInternalError,
9+
} from './utils/collectionUtils.js';
510

611
const RULE_NAME = 'xgen-IPA-109-custom-method-must-use-camel-case';
712

@@ -18,16 +23,27 @@ export default (input, opts, { path }) => {
1823
return;
1924
}
2025

21-
let methodName = getCustomMethodName(pathKey);
22-
if (methodName.length === 0 || methodName.trim().length === 0) {
23-
const errorMessage = 'Custom method name cannot be empty or blank.';
24-
return collectAndReturnViolation(path, RULE_NAME, errorMessage);
26+
const errors = checkViolationsAndReturnErrors(pathKey, path);
27+
if (errors.length !== 0) {
28+
return collectAndReturnViolation(path, RULE_NAME, errors);
2529
}
26-
27-
if (casing(methodName, { type: 'camel', disallowDigits: true })) {
28-
const errorMessage = `${methodName} must use camelCase format.`;
29-
return collectAndReturnViolation(path, RULE_NAME, errorMessage);
30-
}
31-
3230
collectAdoption(path, RULE_NAME);
3331
};
32+
33+
function checkViolationsAndReturnErrors(pathKey, path) {
34+
try {
35+
let methodName = getCustomMethodName(pathKey);
36+
if (methodName.length === 0 || methodName.trim().length === 0) {
37+
const errorMessage = 'Custom method name cannot be empty or blank.';
38+
return [{ path, message: errorMessage }];
39+
}
40+
41+
if (casing(methodName, { type: 'camel', disallowDigits: true })) {
42+
const errorMessage = `${methodName} must use camelCase format.`;
43+
return [{ path, message: errorMessage }];
44+
}
45+
return [];
46+
} catch (e) {
47+
handleInternalError(RULE_NAME, path, e);
48+
}
49+
}

0 commit comments

Comments
 (0)