Skip to content

Commit a2e127c

Browse files
1 parent 4982da0 commit a2e127c

File tree

5 files changed

+167
-121
lines changed

5 files changed

+167
-121
lines changed

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

Lines changed: 112 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,11 @@ import { DiagnosticSeverity } from '@stoplight/types';
33

44
const componentSchemas = {
55
schemas: {
6-
ExampleResponse: {
7-
properties: {
8-
exampleProperty: {
9-
type: 'string',
10-
},
11-
},
6+
SchemaResponse: {
7+
type: 'object',
128
},
13-
ExampleRequest: {
14-
properties: {
15-
exampleProperty: {
16-
type: 'string',
17-
},
18-
},
9+
Schema: {
10+
type: 'object',
1911
},
2012
},
2113
};
@@ -25,22 +17,44 @@ testRule('xgen-IPA-104-get-method-returns-response-suffixed-object', [
2517
name: 'valid schema names names',
2618
document: {
2719
paths: {
28-
'/resource1/{id}': {
20+
'/resource/{id}': {
2921
get: {
3022
responses: {
3123
200: {
3224
content: {
33-
'application/vnd.atlas.2024-08-05+json': {
25+
'application/vnd.atlas.2023-01-01+json': {
26+
schema: {
27+
$ref: '#/components/schemas/SchemaResponse',
28+
},
29+
},
30+
'application/vnd.atlas.2024-01-01+json': {
31+
schema: {
32+
$ref: '#/components/schemas/SchemaResponse',
33+
},
34+
},
35+
'application/vnd.atlas.2025-01-01+json': {
36+
schema: {
37+
type: 'array',
38+
items: {
39+
$ref: '#/components/schemas/SchemaResponse',
40+
},
41+
},
42+
},
43+
},
44+
},
45+
400: {
46+
content: {
47+
'application/vnd.atlas.2023-01-01+json': {
3448
schema: {
35-
$ref: '#/components/schemas/ExampleResponse',
49+
$ref: '#/components/schemas/Schema',
3650
},
3751
},
3852
},
3953
},
4054
},
4155
},
4256
},
43-
'/resource2/{id}': {
57+
'/resourceTwo/{id}': {
4458
get: {
4559
responses: {
4660
200: {
@@ -53,14 +67,14 @@ testRule('xgen-IPA-104-get-method-returns-response-suffixed-object', [
5367
},
5468
},
5569
},
56-
'/singleton': {
70+
'/resource/{id}/singleton': {
5771
get: {
5872
responses: {
5973
200: {
6074
content: {
6175
'application/vnd.atlas.2024-08-05+json': {
6276
schema: {
63-
$ref: '#/components/schemas/ExampleResponse',
77+
$ref: '#/components/schemas/SchemaResponse',
6478
},
6579
},
6680
},
@@ -82,24 +96,37 @@ testRule('xgen-IPA-104-get-method-returns-response-suffixed-object', [
8296
responses: {
8397
200: {
8498
content: {
85-
'application/vnd.atlas.2024-08-05+json': {
99+
'application/vnd.atlas.2023-01-01+json': {
86100
schema: {
87-
$ref: '#/components/schemas/ExampleRequest',
101+
$ref: '#/components/schemas/Schema',
102+
},
103+
},
104+
'application/vnd.atlas.2024-01-01+json': {
105+
schema: {
106+
$ref: '#/components/schemas/Schema',
107+
},
108+
},
109+
'application/vnd.atlas.2025-01-01+json': {
110+
schema: {
111+
type: 'array',
112+
items: {
113+
$ref: '#/components/schemas/Schema',
114+
},
88115
},
89116
},
90117
},
91118
},
92119
},
93120
},
94121
},
95-
'/singleton': {
122+
'/resource/{id}/singleton': {
96123
get: {
97124
responses: {
98125
200: {
99126
content: {
100127
'application/vnd.atlas.2024-08-05+json': {
101128
schema: {
102-
$ref: '#/components/schemas/ExampleRequest',
129+
$ref: '#/components/schemas/Schema',
103130
},
104131
},
105132
},
@@ -113,22 +140,58 @@ testRule('xgen-IPA-104-get-method-returns-response-suffixed-object', [
113140
errors: [
114141
{
115142
code: 'xgen-IPA-104-get-method-returns-response-suffixed-object',
116-
message: 'ExampleRequest schema name should end in "Response". http://go/ipa/104',
143+
message: 'The request schema must reference a schema with a Response suffix. http://go/ipa/104',
117144
path: [
118145
'paths',
119146
'/resource/{id}',
120147
'get',
121148
'responses',
122149
'200',
123150
'content',
124-
'application/vnd.atlas.2024-08-05+json',
151+
'application/vnd.atlas.2023-01-01+json',
125152
],
126153
severity: DiagnosticSeverity.Warning,
127154
},
128155
{
129156
code: 'xgen-IPA-104-get-method-returns-response-suffixed-object',
130-
message: 'ExampleRequest schema name should end in "Response". http://go/ipa/104',
131-
path: ['paths', '/singleton', 'get', 'responses', '200', 'content', 'application/vnd.atlas.2024-08-05+json'],
157+
message: 'The request schema must reference a schema with a Response suffix. http://go/ipa/104',
158+
path: [
159+
'paths',
160+
'/resource/{id}',
161+
'get',
162+
'responses',
163+
'200',
164+
'content',
165+
'application/vnd.atlas.2024-01-01+json',
166+
],
167+
severity: DiagnosticSeverity.Warning,
168+
},
169+
{
170+
code: 'xgen-IPA-104-get-method-returns-response-suffixed-object',
171+
message: 'The request schema must reference a schema with a Response suffix. http://go/ipa/104',
172+
path: [
173+
'paths',
174+
'/resource/{id}',
175+
'get',
176+
'responses',
177+
'200',
178+
'content',
179+
'application/vnd.atlas.2025-01-01+json',
180+
],
181+
severity: DiagnosticSeverity.Warning,
182+
},
183+
{
184+
code: 'xgen-IPA-104-get-method-returns-response-suffixed-object',
185+
message: 'The request schema must reference a schema with a Response suffix. http://go/ipa/104',
186+
path: [
187+
'paths',
188+
'/resource/{id}/singleton',
189+
'get',
190+
'responses',
191+
'200',
192+
'content',
193+
'application/vnd.atlas.2024-08-05+json',
194+
],
132195
severity: DiagnosticSeverity.Warning,
133196
},
134197
],
@@ -142,20 +205,39 @@ testRule('xgen-IPA-104-get-method-returns-response-suffixed-object', [
142205
responses: {
143206
200: {
144207
content: {
145-
'application/vnd.atlas.2024-08-05+json': {
208+
'application/vnd.atlas.2023-01-01+json': {
209+
'x-xgen-IPA-exception': {
210+
'xgen-IPA-104-get-method-returns-response-suffixed-object': 'reason',
211+
},
212+
schema: {
213+
$ref: '#/components/schemas/Schema',
214+
},
215+
},
216+
'application/vnd.atlas.2024-01-01+json': {
217+
'x-xgen-IPA-exception': {
218+
'xgen-IPA-104-get-method-returns-response-suffixed-object': 'reason',
219+
},
220+
schema: {
221+
$ref: '#/components/schemas/Schema',
222+
},
223+
},
224+
'application/vnd.atlas.2025-01-01+json': {
146225
'x-xgen-IPA-exception': {
147226
'xgen-IPA-104-get-method-returns-response-suffixed-object': 'reason',
148227
},
149228
schema: {
150-
$ref: '#/components/schemas/ExampleRequest',
229+
type: 'array',
230+
items: {
231+
$ref: '#/components/schemas/Schema',
232+
},
151233
},
152234
},
153235
},
154236
},
155237
},
156238
},
157239
},
158-
'/singleton': {
240+
'/resource/{id}/singleton': {
159241
get: {
160242
responses: {
161243
200: {
@@ -165,7 +247,7 @@ testRule('xgen-IPA-104-get-method-returns-response-suffixed-object', [
165247
'xgen-IPA-104-get-method-returns-response-suffixed-object': 'reason',
166248
},
167249
schema: {
168-
$ref: '#/components/schemas/ExampleRequest',
250+
$ref: '#/components/schemas/Schema',
169251
},
170252
},
171253
},

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ rules:
3434
description: 'The get method of a resource should return a "Response" suffixed object. http://go/ipa/104'
3535
message: '{{error}} http://go/ipa/104'
3636
severity: warn
37-
given: '$.paths[*].get'
37+
given: '$.paths[*].get.responses[*].content'
3838
then:
39+
field: '@key'
3940
function: 'getMethodReturnsResponseSuffixedObject'

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

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { hasException } from './utils/exceptions.js';
22
import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js';
33
import { isCustomMethodIdentifier } from './utils/resourceEvaluation.js';
44
import { resolveObject } from './utils/componentUtils.js';
5+
import { getSchemaRef } from './utils/methodUtils.js';
56

67
const RULE_NAME = 'xgen-IPA-106-create-method-request-body-is-request-suffixed-object';
78
const ERROR_MESSAGE_SCHEMA_NAME = 'The response body schema must reference a schema with a Request suffix.';
@@ -23,25 +24,15 @@ export default (input, _, { path, documentInventory }) => {
2324
}
2425

2526
if (contentPerMediaType.schema) {
27+
console.log(contentPerMediaType);
2628
const schema = contentPerMediaType.schema;
27-
if (schema.type === 'array' && schema.items) {
28-
let schemaItems = schema.items;
29-
if (!schemaItems.$ref) {
30-
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_REF);
31-
}
32-
if (!schemaItems.$ref.endsWith('Request')) {
33-
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_NAME);
34-
}
35-
} else {
36-
if (!schema.$ref) {
37-
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_REF);
38-
}
39-
40-
if (!schema.$ref.endsWith('Request')) {
41-
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_NAME);
42-
}
29+
const schemaName = getSchemaRef(schema);
30+
if (!schemaName) {
31+
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_REF);
32+
}
33+
if (!schemaName.endsWith('Request')) {
34+
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_NAME);
4335
}
44-
4536
collectAdoption(path, RULE_NAME);
4637
}
4738
};
Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,46 @@
11
import {
22
isSingleResourceIdentifier,
3-
isCustomMethodIdentifier,
43
isSingletonResource,
54
getResourcePathItems,
5+
isResourceCollectionIdentifier,
66
} from './utils/resourceEvaluation.js';
7-
import { getAllSuccessfulResponseSchemaNames } from './utils/methodUtils.js';
87
import { resolveObject } from './utils/componentUtils.js';
98
import { hasException } from './utils/exceptions.js';
109
import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js';
10+
import { getSchemaRef } from './utils/methodUtils.js';
1111

1212
const RULE_NAME = 'xgen-IPA-104-get-method-returns-response-suffixed-object';
13-
const ERROR_MESSAGE = 'schema name should end in "Response".';
13+
const ERROR_MESSAGE_SCHEMA_NAME = 'The request schema must reference a schema with a Response suffix.';
14+
const ERROR_MESSAGE_SCHEMA_REF = 'The response body schema is defined inline and must reference a predefined schema.';
1415

15-
export default (input, _, { path, document }) => {
16+
export default (input, _, { path, documentInventory }) => {
1617
const resourcePath = path[1];
17-
const oas = document.data;
18+
const responseCode = path[4];
19+
const oas = documentInventory.unresolved;
1820
const resourcePaths = getResourcePathItems(resourcePath, oas.paths);
1921

2022
if (
21-
isCustomMethodIdentifier(resourcePath) ||
22-
(!isSingleResourceIdentifier(resourcePath) && !isSingletonResource(resourcePaths))
23+
responseCode.startsWith('2') ||
24+
isResourceCollectionIdentifier(resourcePath) ||
25+
(isSingleResourceIdentifier(resourcePath) && isSingletonResource(resourcePaths))
2326
) {
24-
return;
25-
}
26-
27-
const errors = [];
28-
29-
const responseSchemaNames = getAllSuccessfulResponseSchemaNames(oas.paths[resourcePath].get);
30-
responseSchemaNames.forEach(({ schemaName, schemaPath }) => {
31-
const fullPath = path.concat(schemaPath);
32-
const responseObject = resolveObject(oas, fullPath);
27+
const contentPerMediaType = resolveObject(oas, path);
3328

34-
if (hasException(responseObject, RULE_NAME)) {
35-
collectException(responseObject, RULE_NAME, fullPath);
36-
} else if (!schemaName.endsWith('Response')) {
37-
collectAndReturnViolation(fullPath, RULE_NAME, `${schemaName} ${ERROR_MESSAGE}`);
38-
errors.push({
39-
path: fullPath,
40-
message: `${schemaName} ${ERROR_MESSAGE}`,
41-
});
42-
} else {
43-
collectAdoption(fullPath, RULE_NAME);
29+
if (hasException(contentPerMediaType, RULE_NAME)) {
30+
collectException(contentPerMediaType, RULE_NAME, path);
31+
return;
4432
}
45-
});
4633

47-
return errors;
34+
if (contentPerMediaType.schema) {
35+
const schema = contentPerMediaType.schema;
36+
const schemaName = getSchemaRef(schema);
37+
if (!schemaName) {
38+
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_REF);
39+
}
40+
if (!schemaName.endsWith('Response')) {
41+
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_NAME);
42+
}
43+
collectAdoption(path, RULE_NAME);
44+
}
45+
}
4846
};

0 commit comments

Comments
 (0)