Skip to content

Commit 4ef072b

Browse files
CLOUDP-271988: IPA-102: Validate paths follow expected format
1 parent ecacc90 commit 4ef072b

File tree

6 files changed

+272
-1
lines changed

6 files changed

+272
-1
lines changed
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
import testRule from './__helpers__/testRule';
2+
import { DiagnosticSeverity } from '@stoplight/types';
3+
4+
testRule('xgen-IPA-102-path-alternate-resource-name-path-param', [
5+
{
6+
name: 'valid paths - api/atlas/v2',
7+
document: {
8+
paths: {
9+
'/api/atlas/v2/resourceName': {
10+
post: {},
11+
get: {},
12+
},
13+
'/api/atlas/v2/resourceName/{pathParam}': {
14+
get: {},
15+
patch: {},
16+
delete: {},
17+
},
18+
'/api/atlas/v2/resourceName1/{pathParam}/resourceName2': {
19+
post: {},
20+
get: {},
21+
},
22+
'/api/atlas/v2/resourceName1/{pathParam1p}/resourceName2/{pathParam2}': {
23+
get: {},
24+
patch: {},
25+
delete: {},
26+
},
27+
'/api/atlas/v2/resourceName/{pathParam}:method': {
28+
post: {},
29+
},
30+
'/api/atlas/v2/custom:method': {
31+
post: {},
32+
},
33+
'/api/atlas/v2': {
34+
post: {},
35+
}
36+
},
37+
},
38+
errors: [],
39+
},
40+
{
41+
name: 'valid paths - api/atlas/v2/unauth',
42+
document: {
43+
paths: {
44+
'/api/atlas/v2/unauth/resourceName': {
45+
post: {},
46+
get: {},
47+
},
48+
'/api/atlas/v2/unauth/resourceName/{pathParam}': {
49+
get: {},
50+
patch: {},
51+
delete: {},
52+
},
53+
'/api/atlas/v2/unauth/resourceName1/{pathParam}/resourceName2': {
54+
post: {},
55+
get: {},
56+
},
57+
'/api/atlas/v2/unauth/resourceName1/{pathParam1p}/resourceName2/{pathParam2}': {
58+
get: {},
59+
patch: {},
60+
delete: {},
61+
},
62+
'/api/atlas/v2/unauth/resourceName/{pathParam}:method': {
63+
post: {},
64+
},
65+
'/api/atlas/v2/unauth/custom:method': {
66+
post: {},
67+
},
68+
'/api/atlas/v2/unauth': {
69+
post: {},
70+
}
71+
},
72+
},
73+
errors: [],
74+
},
75+
{
76+
name: 'invalid paths - api/atlas/v2',
77+
document: {
78+
paths: {
79+
'/api/atlas/v2/resourceName1/resourceName2': {
80+
post: {},
81+
get: {},
82+
},
83+
'/api/atlas/v2/resourceName/{pathParam1}/{pathParam2}': {
84+
patch: {},
85+
delete: {},
86+
},
87+
'/api/atlas/v2/resourceName1/{pathParam1}/resourceName2/resourceName3': {
88+
patch: {},
89+
delete: {},
90+
},
91+
'/api/atlas/v2/resourceName1/{pathParam1}/resourceName2/{pathParam2}/{pathParam3}': {
92+
patch: {},
93+
delete: {},
94+
},
95+
'/api/atlas/v2/{pathParam}': {
96+
post: {},
97+
get: {},
98+
},
99+
'/api/atlas/v2/{pathParam1}/{pathParam2}': {
100+
post: {},
101+
get: {},
102+
}
103+
},
104+
},
105+
errors: [
106+
{
107+
code: 'xgen-IPA-102-path-alternate-resource-name-path-param',
108+
message: 'API paths must alternate between resource name and path params. http://go/ipa/117',
109+
path: ['paths', '/api/atlas/v2/resourceName1/resourceName2'],
110+
severity: DiagnosticSeverity.Warning,
111+
},
112+
{
113+
code: 'xgen-IPA-102-path-alternate-resource-name-path-param',
114+
message: 'API paths must alternate between resource name and path params. http://go/ipa/117',
115+
path: ['paths', '/api/atlas/v2/resourceName/{pathParam1}/{pathParam2}'],
116+
severity: DiagnosticSeverity.Warning,
117+
},
118+
{
119+
code: 'xgen-IPA-102-path-alternate-resource-name-path-param',
120+
message: 'API paths must alternate between resource name and path params. http://go/ipa/117',
121+
path: ['paths', '/api/atlas/v2/resourceName1/{pathParam1}/resourceName2/resourceName3'],
122+
severity: DiagnosticSeverity.Warning,
123+
},
124+
{
125+
code: 'xgen-IPA-102-path-alternate-resource-name-path-param',
126+
message: 'API paths must alternate between resource name and path params. http://go/ipa/117',
127+
path: ['paths', '/api/atlas/v2/resourceName1/{pathParam1}/resourceName2/{pathParam2}/{pathParam3}'],
128+
severity: DiagnosticSeverity.Warning,
129+
},
130+
{
131+
code: 'xgen-IPA-102-path-alternate-resource-name-path-param',
132+
message: 'API paths must alternate between resource name and path params. http://go/ipa/117',
133+
path: ['paths', '/api/atlas/v2/{pathParam}'],
134+
severity: DiagnosticSeverity.Warning,
135+
},
136+
{
137+
code: 'xgen-IPA-102-path-alternate-resource-name-path-param',
138+
message: 'API paths must alternate between resource name and path params. http://go/ipa/117',
139+
path: ['paths', '/api/atlas/v2/{pathParam1}/{pathParam2}'],
140+
severity: DiagnosticSeverity.Warning,
141+
},
142+
]
143+
},
144+
{
145+
name: 'invalid paths - api/atlas/v2/unauth',
146+
document: {
147+
paths: {
148+
'/api/atlas/v2/unauth/resourceName1/resourceName2': {
149+
post: {},
150+
get: {},
151+
},
152+
'/api/atlas/v2/unauth/resourceName/{pathParam1}/{pathParam2}': {
153+
patch: {},
154+
delete: {},
155+
},
156+
'/api/atlas/v2/unauth/resourceName1/{pathParam1}/resourceName2/resourceName3': {
157+
patch: {},
158+
delete: {},
159+
},
160+
'/api/atlas/v2/unauth/resourceName1/{pathParam1}/resourceName2/{pathParam2}/{pathParam3}': {
161+
patch: {},
162+
delete: {},
163+
},
164+
'/api/atlas/v2/unauth/{pathParam}': {
165+
post: {},
166+
get: {},
167+
},
168+
'/api/atlas/v2/unauth/{pathParam1}/{pathParam2}': {
169+
post: {},
170+
get: {},
171+
}
172+
},
173+
},
174+
errors: [
175+
{
176+
code: 'xgen-IPA-102-path-alternate-resource-name-path-param',
177+
message: 'API paths must alternate between resource name and path params. http://go/ipa/117',
178+
path: ['paths', '/api/atlas/v2/unauth/resourceName1/resourceName2'],
179+
severity: DiagnosticSeverity.Warning,
180+
},
181+
{
182+
code: 'xgen-IPA-102-path-alternate-resource-name-path-param',
183+
message: 'API paths must alternate between resource name and path params. http://go/ipa/117',
184+
path: ['paths', '/api/atlas/v2/unauth/resourceName/{pathParam1}/{pathParam2}'],
185+
severity: DiagnosticSeverity.Warning,
186+
},
187+
{
188+
code: 'xgen-IPA-102-path-alternate-resource-name-path-param',
189+
message: 'API paths must alternate between resource name and path params. http://go/ipa/117',
190+
path: ['paths', '/api/atlas/v2/unauth/resourceName1/{pathParam1}/resourceName2/resourceName3'],
191+
severity: DiagnosticSeverity.Warning,
192+
},
193+
{
194+
code: 'xgen-IPA-102-path-alternate-resource-name-path-param',
195+
message: 'API paths must alternate between resource name and path params. http://go/ipa/117',
196+
path: ['paths', '/api/atlas/v2/unauth/resourceName1/{pathParam1}/resourceName2/{pathParam2}/{pathParam3}'],
197+
severity: DiagnosticSeverity.Warning,
198+
},
199+
{
200+
code: 'xgen-IPA-102-path-alternate-resource-name-path-param',
201+
message: 'API paths must alternate between resource name and path params. http://go/ipa/117',
202+
path: ['paths', '/api/atlas/v2/unauth/{pathParam}'],
203+
severity: DiagnosticSeverity.Warning,
204+
},
205+
{
206+
code: 'xgen-IPA-102-path-alternate-resource-name-path-param',
207+
message: 'API paths must alternate between resource name and path params. http://go/ipa/117',
208+
path: ['paths', '/api/atlas/v2/unauth/{pathParam1}/{pathParam2}'],
209+
severity: DiagnosticSeverity.Warning,
210+
},
211+
],
212+
},
213+
]);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
extends:
2+
- ./rulesets/IPA-102.yaml
23
- ./rulesets/IPA-104.yaml
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# IPA-102: Resource Identifiers
2+
# http://go/ipa/102
3+
4+
functions:
5+
- eachPathAlternatesBetweenResourceNameAndPathParam
6+
7+
rules:
8+
xgen-IPA-102-path-alternate-resource-name-path-param:
9+
description: "Paths should alternate between resource names and path params. http://go/ipa/102"
10+
message: "{{error}} http://go/ipa/102"
11+
severity: warn
12+
given: "$.paths"
13+
then:
14+
field: "@key"
15+
function: "eachPathAlternatesBetweenResourceNameAndPathParam"

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ functions:
77
rules:
88
xgen-IPA-104-resource-has-GET:
99
description: "APIs must provide a get method for resources. http://go/ipa/104"
10-
message: "{{error}} http://go/ipa/117"
10+
message: "{{error}} http://go/ipa/104"
1111
severity: warn
1212
given: "$.paths"
1313
then:
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { isPathParam } from './utils/pathUtils';
2+
3+
const ERROR_MESSAGE = 'API paths must alternate between resource name and path params.';
4+
const ERROR_RESULT = [{ message: ERROR_MESSAGE }];
5+
const AUTH_PREFIX = "/api/atlas/v2";
6+
const UNAUTH_PREFIX = "/api/atlas/v2/unauth"
7+
8+
const getPrefix = (path) => {
9+
if (path.includes(UNAUTH_PREFIX)) return UNAUTH_PREFIX;
10+
if (path.includes(AUTH_PREFIX)) return AUTH_PREFIX;
11+
return null;
12+
};
13+
14+
const validatePathStructure = (elements) => {
15+
return elements.every((element, index) => {
16+
const isEvenIndex = index % 2 === 0;
17+
return isEvenIndex
18+
? !isPathParam(element)
19+
: isPathParam(element);
20+
});
21+
};
22+
23+
// eslint-disable-next-line no-unused-vars
24+
export default (input, _0, _1) => {
25+
const prefix = getPrefix(input);
26+
if (!prefix) return [];
27+
28+
let suffixWithLeadingSlash = input.slice(prefix.length);
29+
if(suffixWithLeadingSlash.length === 0) {
30+
return [];
31+
}
32+
33+
let suffix = suffixWithLeadingSlash.slice(1);
34+
let elements = suffix.split("/");
35+
return validatePathStructure(elements) ? [] : ERROR_RESULT;
36+
37+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export function isPathParam(str) {
2+
const pathParamRegEx = new RegExp(`^{[a-z][a-zA-Z0-9]*}$`);
3+
const pathParamWithCustomMethodRegEx = new RegExp(`^{[a-z][a-zA-Z0-9]*}:[a-z][a-zA-Z0-9]*$`);
4+
return pathParamRegEx.test(str) || pathParamWithCustomMethodRegEx.test(str);
5+
}

0 commit comments

Comments
 (0)