Skip to content

Commit 5f28a92

Browse files
CLOUDP-304053: IPA-106:Create - The resource must be the request body
1 parent b365638 commit 5f28a92

File tree

5 files changed

+387
-0
lines changed

5 files changed

+387
-0
lines changed
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
import testRule from './__helpers__/testRule';
2+
import { DiagnosticSeverity } from '@stoplight/types';
3+
4+
const componentSchemas = {
5+
schemas: {
6+
SchemaRequest: {
7+
type: 'object',
8+
},
9+
Schema: {
10+
type: 'object',
11+
},
12+
},
13+
};
14+
testRule('xgen-IPA-106-create-method-request-body-is-get-method-response', [
15+
{
16+
name: 'valid methods',
17+
document: {
18+
components: componentSchemas,
19+
paths: {
20+
'/resource': {
21+
post: {
22+
requestBody: {
23+
content: {
24+
'application/vnd.atlas.2023-01-01+json': {
25+
schema: {
26+
$ref: '#/components/schemas/SchemaRequest',
27+
},
28+
},
29+
'application/vnd.atlas.2024-01-01+json': {
30+
schema: {
31+
$ref: '#/components/schemas/SchemaRequest',
32+
},
33+
},
34+
},
35+
},
36+
},
37+
},
38+
'/resource/{id}': {
39+
get: {
40+
responses: {
41+
200: {
42+
content: {
43+
'application/vnd.atlas.2023-01-01+json': {
44+
schema: {
45+
$ref: '#/components/schemas/Schema',
46+
},
47+
},
48+
},
49+
},
50+
},
51+
},
52+
},
53+
'/resource/{id}:customMethod': {
54+
post: {
55+
requestBody: {
56+
content: {
57+
'application/vnd.atlas.2023-01-01+json': {
58+
schema: {
59+
$ref: '#/components/schemas/Schema',
60+
},
61+
},
62+
'application/vnd.atlas.2024-01-01+json': {
63+
schema: {
64+
$ref: '#/components/schemas/SchemaRequest',
65+
},
66+
},
67+
},
68+
},
69+
},
70+
},
71+
}
72+
},
73+
errors: [],
74+
},
75+
{
76+
name: 'invalid methods',
77+
document: {
78+
components: componentSchemas,
79+
paths: {
80+
'/resource': {
81+
post: {
82+
requestBody: {
83+
content: {
84+
'application/vnd.atlas.2023-01-01+json': {
85+
schema: {
86+
$ref: '#/components/schemas/Schema',
87+
},
88+
},
89+
},
90+
},
91+
},
92+
},
93+
'/resource2': {
94+
post: {
95+
requestBody: {
96+
content: {
97+
'application/vnd.atlas.2023-01-01+json': {
98+
schema: {
99+
$ref: '#/components/schemas/Schema',
100+
},
101+
},
102+
'application/vnd.atlas.2024-01-01+json': {
103+
schema: {
104+
$ref: '#/components/schemas/Schema',
105+
},
106+
},
107+
},
108+
},
109+
},
110+
},
111+
'/resource3': {
112+
post: {
113+
requestBody: {
114+
content: {
115+
'application/vnd.atlas.2023-01-01+json': {
116+
schema: {
117+
type: 'object',
118+
},
119+
},
120+
},
121+
},
122+
},
123+
},
124+
},
125+
},
126+
errors: [
127+
{
128+
code: 'xgen-IPA-106-create-method-request-body-is-get-method-response',
129+
message: 'The response body schema must reference a schema with a Request suffix. http://go/ipa/106',
130+
path: ['paths', '/resource', 'post', 'requestBody', 'content', 'application/vnd.atlas.2023-01-01+json'],
131+
severity: DiagnosticSeverity.Warning,
132+
},
133+
{
134+
code: 'xgen-IPA-106-create-method-request-body-is-get-method-response',
135+
message: 'The response body schema must reference a schema with a Request suffix. http://go/ipa/106',
136+
path: ['paths', '/resource', 'post', 'requestBody', 'content', 'application/vnd.atlas.2024-01-01+json'],
137+
severity: DiagnosticSeverity.Warning,
138+
},
139+
{
140+
code: 'xgen-IPA-106-create-method-request-body-is-get-method-response',
141+
message: 'The response body schema must reference a schema with a Request suffix. http://go/ipa/106',
142+
path: ['paths', '/resource2', 'post', 'requestBody', 'content', 'application/vnd.atlas.2023-01-01+json'],
143+
severity: DiagnosticSeverity.Warning,
144+
},
145+
{
146+
code: 'xgen-IPA-106-create-method-request-body-is-get-method-response',
147+
message: 'The response body schema must reference a schema with a Request suffix. http://go/ipa/106',
148+
path: ['paths', '/resource2', 'post', 'requestBody', 'content', 'application/vnd.atlas.2024-01-01+json'],
149+
severity: DiagnosticSeverity.Warning,
150+
},
151+
{
152+
code: 'xgen-IPA-106-create-method-request-body-is-get-method-response',
153+
message: 'The response body schema is defined inline and must reference a predefined schema. http://go/ipa/106',
154+
path: ['paths', '/resource3', 'post', 'requestBody', 'content', 'application/vnd.atlas.2023-01-01+json'],
155+
severity: DiagnosticSeverity.Warning,
156+
},
157+
],
158+
},
159+
{
160+
name: 'invalid method with exception',
161+
document: {
162+
components: componentSchemas,
163+
paths: {
164+
'/resource': {
165+
post: {
166+
requestBody: {
167+
content: {
168+
'application/vnd.atlas.2023-01-01+json': {
169+
schema: {
170+
$ref: '#/components/schemas/Schema',
171+
},
172+
'x-xgen-IPA-exception': {
173+
'xgen-IPA-106-create-method-request-body-is-get-method-response': 'reason',
174+
},
175+
},
176+
'application/vnd.atlas.2024-01-01+json': {
177+
schema: {
178+
type: 'array',
179+
items: {
180+
$ref: '#/components/schemas/Schema',
181+
},
182+
},
183+
'x-xgen-IPA-exception': {
184+
'xgen-IPA-106-create-method-request-body-is-get-method-response': 'reason',
185+
},
186+
},
187+
},
188+
},
189+
},
190+
},
191+
'/resource2': {
192+
post: {
193+
requestBody: {
194+
content: {
195+
'application/vnd.atlas.2023-01-01+json': {
196+
schema: {
197+
$ref: '#/components/schemas/Schema',
198+
},
199+
'x-xgen-IPA-exception': {
200+
'xgen-IPA-106-create-method-request-body-is-get-method-response': 'reason',
201+
},
202+
},
203+
'application/vnd.atlas.2024-01-01+json': {
204+
schema: {
205+
$ref: '#/components/schemas/Schema',
206+
},
207+
'x-xgen-IPA-exception': {
208+
'xgen-IPA-106-create-method-request-body-is-get-method-response': 'reason',
209+
},
210+
},
211+
},
212+
},
213+
},
214+
},
215+
'/resource3': {
216+
post: {
217+
requestBody: {
218+
content: {
219+
'application/vnd.atlas.2023-01-01+json': {
220+
schema: {
221+
type: 'object',
222+
},
223+
'x-xgen-IPA-exception': {
224+
'xgen-IPA-106-create-method-request-body-is-get-method-response': 'reason',
225+
},
226+
},
227+
},
228+
},
229+
},
230+
},
231+
},
232+
},
233+
errors: [],
234+
},
235+
]);

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
functions:
55
- createMethodRequestBodyIsRequestSuffixedObject
66
- createMethodShouldNotHaveQueryParameters
7+
- createMethodRequestBodyIsGetResponse
78

89
rules:
910
xgen-IPA-106-create-method-request-body-is-request-suffixed-object:
@@ -21,3 +22,11 @@ rules:
2122
given: '$.paths[*].post'
2223
then:
2324
function: 'createMethodShouldNotHaveQueryParameters'
25+
xgen-IPA-106-create-method-request-body-is-get-method-response:
26+
description: 'The Create method request should be a Get method response. http://go/ipa/106'
27+
message: '{{error}} http://go/ipa/106'
28+
severity: warn
29+
given: '$.paths[*].post.requestBody.content'
30+
then:
31+
field: '@key'
32+
function: 'createMethodRequestBodyIsGetResponse'
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { getResponseOfGetMethodByMediaType, isCustomMethodIdentifier } from './utils/resourceEvaluation.js';
2+
import { resolveObject } from './utils/componentUtils.js';
3+
import { compareSchemas } from './utils/schemaUtils.js';
4+
import { hasException } from './utils/exceptions.js';
5+
import {
6+
collectAdoption,
7+
collectAndReturnViolation,
8+
collectException,
9+
} from './utils/collectionUtils.js';
10+
11+
const RULE_NAME = 'xgen-IPA-106-create-method-request-body-is-get-method-response';
12+
const ERROR_MESSAGE = 'The request body schema properties must match the response body schema properties of the Get method.';
13+
14+
15+
export default (input, _, { path, documentInventory }) => {
16+
const oas = documentInventory.resolved;
17+
const resourcePath = path[1];
18+
19+
if (isCustomMethodIdentifier(resourcePath)) {
20+
return;
21+
}
22+
23+
const contentPerMediaType = resolveObject(oas, path);
24+
25+
if (hasException(contentPerMediaType, RULE_NAME)) {
26+
collectException(contentPerMediaType, RULE_NAME, path);
27+
return;
28+
}
29+
30+
let mediaType = input;
31+
const getMethodResponseContentPerMediaType = getResponseOfGetMethodByMediaType(mediaType, resourcePath, oas);
32+
if (!getMethodResponseContentPerMediaType) {
33+
return;
34+
}
35+
36+
const postMethodRequestContentPerMediaType = resolveObject(oas, path);
37+
if (compareSchemas(postMethodRequestContentPerMediaType.schema, getMethodResponseContentPerMediaType.schema)) {
38+
collectAdoption(path, RULE_NAME);
39+
}
40+
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE);
41+
};

tools/spectral/ipa/rulesets/functions/utils/resourceEvaluation.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,43 @@ function removePrefix(path) {
150150
}
151151
return path;
152152
}
153+
154+
/**
155+
* Checks if resource has Get method
156+
* @param {string} mediaType the media type to check for
157+
* @param {string} pathForResourceCollection the path for the collection of resources
158+
* @param {Object} oas the OpenAPI document
159+
*/
160+
export function getResponseOfGetMethodByMediaType(mediaType, pathForResourceCollection, oas) {
161+
const resourcePathItems = getResourcePathItems(pathForResourceCollection, oas.paths);
162+
const resourcePaths = Object.keys(resourcePathItems);
163+
if (resourcePaths.length === 1) {
164+
return null;
165+
}
166+
let singleResourcePath = resourcePaths.find(
167+
(path) => !isCustomMethodIdentifier(path) && path !== pathForResourceCollection
168+
);
169+
if (singleResourcePath.length === 0) {
170+
return null;
171+
}
172+
const singleResourcePathObject = resourcePathItems[singleResourcePath];
173+
if (!hasGetMethod(singleResourcePathObject)) {
174+
return null;
175+
}
176+
177+
const getMethodObject = singleResourcePathObject.get;
178+
if (!getMethodObject.responses) {
179+
return null;
180+
}
181+
182+
const response = getMethodObject.responses['200'];
183+
if (!response) {
184+
return null;
185+
}
186+
187+
const schema = response.content[mediaType];
188+
if (!schema) {
189+
return null;
190+
}
191+
return schema;
192+
}

0 commit comments

Comments
 (0)