Skip to content

Commit 62c115a

Browse files
committed
wip
1 parent 2dc9b51 commit 62c115a

9 files changed

+374
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import testRule from './__helpers__/testRule';
2+
import { DiagnosticSeverity } from '@stoplight/types';
3+
4+
testRule('xgen-IPA-108-delete-204-response', [
5+
{
6+
name: 'valid DELETE with 204',
7+
document: {
8+
paths: {
9+
'/resource/{id}': {
10+
delete: {
11+
responses: {
12+
'204': {
13+
description: 'Resource deleted'
14+
}
15+
}
16+
}
17+
}
18+
}
19+
},
20+
errors: []
21+
},
22+
{
23+
name: 'invalid DELETE missing 204',
24+
document: {
25+
paths: {
26+
'/resource/{id}': {
27+
delete: {
28+
responses: {
29+
'200': {
30+
description: 'Resource deleted'
31+
}
32+
}
33+
}
34+
}
35+
}
36+
},
37+
errors: [
38+
{
39+
code: 'xgen-IPA-108-delete-204-response',
40+
message: 'DELETE method should return 204 No Content status code http://go/ipa/108',
41+
path: ['paths', '/resource/{id}'],
42+
severity: DiagnosticSeverity.Error
43+
}
44+
]
45+
},
46+
{
47+
name: 'valid with exception',
48+
document: {
49+
paths: {
50+
'/resource/{id}': {
51+
delete: {
52+
'x-xgen-IPA-exception': {
53+
'xgen-IPA-108-delete-204-response': 'Legacy API'
54+
},
55+
responses: {
56+
'200': {
57+
description: 'Resource deleted'
58+
}
59+
}
60+
}
61+
}
62+
}
63+
},
64+
errors: []
65+
}
66+
]);
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import testRule from './__helpers__/testRule';
2+
import { DiagnosticSeverity } from '@stoplight/types';
3+
4+
testRule('xgen-IPA-108-delete-404-response', [
5+
{
6+
name: 'valid DELETE with 404',
7+
document: {
8+
paths: {
9+
'/resource/{id}': {
10+
delete: {
11+
responses: {
12+
'204': {},
13+
'404': {
14+
description: 'Resource not found'
15+
}
16+
}
17+
}
18+
}
19+
}
20+
},
21+
errors: []
22+
},
23+
{
24+
name: 'invalid DELETE missing 404',
25+
document: {
26+
paths: {
27+
'/resource/{id}': {
28+
delete: {
29+
responses: {
30+
'204': {}
31+
}
32+
}
33+
}
34+
}
35+
},
36+
errors: [
37+
{
38+
code: 'xgen-IPA-108-delete-404-response',
39+
message: 'DELETE method should include 404 status code for not found resources http://go/ipa/108',
40+
path: ['paths', '/resource/{id}'],
41+
severity: DiagnosticSeverity.Error
42+
}
43+
]
44+
},
45+
{
46+
name: 'valid with exception',
47+
document: {
48+
paths: {
49+
'/resource/{id}': {
50+
delete: {
51+
'x-xgen-IPA-exception': {
52+
'xgen-IPA-108-delete-404-response': 'Idempotent delete'
53+
},
54+
responses: {
55+
'204': {}
56+
}
57+
}
58+
}
59+
}
60+
},
61+
errors: []
62+
}
63+
]);
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import testRule from './__helpers__/testRule';
2+
import { DiagnosticSeverity } from '@stoplight/types';
3+
4+
testRule('xgen-IPA-108-delete-no-body', [
5+
{
6+
name: 'valid DELETE without body',
7+
document: {
8+
paths: {
9+
'/resource/{id}': {
10+
delete: {
11+
responses: {
12+
'204': {}
13+
}
14+
}
15+
}
16+
}
17+
},
18+
errors: []
19+
},
20+
{
21+
name: 'invalid DELETE with body',
22+
document: {
23+
paths: {
24+
'/resource/{id}': {
25+
delete: {
26+
requestBody: {
27+
content: {
28+
'application/json': {
29+
schema: { type: 'object' }
30+
}
31+
}
32+
},
33+
responses: {
34+
'204': {}
35+
}
36+
}
37+
}
38+
}
39+
},
40+
errors: [
41+
{
42+
code: 'xgen-IPA-108-delete-no-body',
43+
message: 'DELETE method should not have a request body http://go/ipa/108',
44+
path: ['paths', '/resource/{id}'],
45+
severity: DiagnosticSeverity.Error
46+
}
47+
]
48+
},
49+
{
50+
name: 'valid with exception',
51+
document: {
52+
paths: {
53+
'/resource/{id}': {
54+
delete: {
55+
'x-xgen-IPA-exception': {
56+
'xgen-IPA-108-delete-no-body': 'Bulk delete operation'
57+
},
58+
requestBody: {
59+
content: {
60+
'application/json': {
61+
schema: { type: 'object' }
62+
}
63+
}
64+
}
65+
}
66+
}
67+
}
68+
},
69+
errors: []
70+
}
71+
]);

tools/spectral/ipa/ipa-spectral.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ extends:
55
- ./rulesets/IPA-109.yaml
66
- ./rulesets/IPA-113.yaml
77
- ./rulesets/IPA-123.yaml
8+
- ./rulesets/IPA-108.yaml
89

910
overrides:
1011
- files:
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# IPA-123: Enums
2+
# http://go/ipa/123
3+
4+
functions:
5+
- deleteMethodResponseShouldBeVoid
6+
- deleteMethod204Response
7+
- deleteMethodNoRequestBody
8+
- deleteMethod404Response
9+
10+
rules:
11+
xgen-IPA-108-delete-response-should-be-void:
12+
description: 'Delete method response must be void http://go/ipa/108'
13+
message: '{{error}} http://go/ipa/108'
14+
severity: error
15+
given: '$.paths.*'
16+
then:
17+
function: 'deleteMethodResponseShouldBeVoid'
18+
19+
xgen-IPA-108-delete-204-response:
20+
description: 'DELETE method must return 204 No Content'
21+
message: '{{error}} http://go/ipa/108'
22+
severity: error
23+
given: '$.paths.*'
24+
then:
25+
function: deleteMethod204Response
26+
27+
xgen-IPA-108-delete-no-body:
28+
description: 'DELETE method must not have request body'
29+
message: '{{error}} http://go/ipa/108'
30+
severity: error
31+
given: '$.paths.*'
32+
then:
33+
function: deleteMethodNoRequestBody
34+
35+
xgen-IPA-108-delete-404-response:
36+
description: 'DELETE method must include 404 response'
37+
message: '{{error}} http://go/ipa/108'
38+
severity: error
39+
given: '$.paths.*'
40+
then:
41+
function: deleteMethod404Response
42+
43+
xgen-IPA-108-delete-204-content-type:
44+
description: '204 response must not include content-type header'
45+
message: '204 No Content response must not include content-type header http://go/ipa/108'
46+
severity: error
47+
given: $.paths[*].delete.responses['204'].content
48+
then:
49+
function: falsy
50+
51+
xgen-IPA-108-delete-404-schema:
52+
description: '404 response must include error schema'
53+
message: '404 Not Found response must include error schema http://go/ipa/108'
54+
severity: error
55+
given: $.paths[*].delete.responses['404']
56+
then:
57+
field: content
58+
function: truthy
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js';
2+
import { hasException } from './utils/exceptions.js';
3+
4+
const RULE_NAME = 'xgen-IPA-108-delete-204-response';
5+
const ERROR_MESSAGE = 'DELETE method should return 204 No Content status code';
6+
7+
export default (paths, _, { path }) => {
8+
if (!paths.delete) return;
9+
10+
if (hasException(paths.delete, RULE_NAME)) {
11+
return collectException(paths.delete, RULE_NAME, path);
12+
}
13+
14+
const responses = paths.delete.responses || {};
15+
if (!responses['204']) {
16+
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE);
17+
}
18+
19+
collectAdoption(path, RULE_NAME);
20+
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js';
2+
import { hasException } from './utils/exceptions.js';
3+
4+
const RULE_NAME = 'xgen-IPA-108-delete-404-response';
5+
const ERROR_MESSAGE = 'DELETE method should include 404 status code for not found resources';
6+
7+
export default (paths, _, { path }) => {
8+
if (!paths.delete) return;
9+
10+
if (hasException(paths.delete, RULE_NAME)) {
11+
return collectException(paths.delete, RULE_NAME, path);
12+
}
13+
14+
const responses = paths.delete.responses || {};
15+
if (!responses['404']) {
16+
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE);
17+
}
18+
19+
collectAdoption(path, RULE_NAME);
20+
};
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js';
2+
import { hasException } from './utils/exceptions.js';
3+
4+
const RULE_NAME = 'xgen-IPA-108-delete-no-body';
5+
const ERROR_MESSAGE = 'DELETE method should not have a request body';
6+
7+
export default (paths, _, { path }) => {
8+
if (!paths.delete) return;
9+
10+
if (hasException(paths.delete, RULE_NAME)) {
11+
return collectException(paths.delete, RULE_NAME, path);
12+
}
13+
14+
if (paths.delete.requestBody) {
15+
return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE);
16+
}
17+
18+
collectAdoption(path, RULE_NAME);
19+
};
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { hasException } from './utils/exceptions.js';
2+
import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js';
3+
4+
const RULE_NAME = 'xgen-IPA-108-delete-response-should-be-void';
5+
const ERROR_MESSAGES = {
6+
NON_EMPTY_RESPONSE: 'DELETE method should return an empty response',
7+
INVALID_STATUS: 'DELETE method should return 204 No Content status code',
8+
REQUEST_BODY: 'DELETE method should not have a request body',
9+
MISSING_404: 'DELETE method should include 404 status code for not found resources'
10+
};
11+
12+
export default (paths, _, { path }) => {
13+
const errors = [];
14+
//const oas = documentInventory.resolved;
15+
16+
Object.entries(paths).forEach(([pathUrl, pathItem]) => {
17+
console.log(pathUrl)
18+
if (!pathItem.delete) return;
19+
20+
if (hasException(pathItem.delete, RULE_NAME)) {
21+
collectException(pathItem.delete, RULE_NAME, path);
22+
return;
23+
}
24+
25+
// Validate request body
26+
if (pathItem.delete.requestBody) {
27+
errors.push(collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGES.REQUEST_BODY));
28+
}
29+
30+
// Validate response
31+
const responses = pathItem.delete.responses || {};
32+
33+
// Check for 204 status
34+
if (!responses['204']) {
35+
errors.push(collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGES.INVALID_STATUS));
36+
}
37+
38+
// Check for 404 status
39+
if (!responses['404']) {
40+
errors.push(collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGES.MISSING_404));
41+
}
42+
43+
// Validate empty response
44+
Object.entries(responses).forEach(([status, response]) => {
45+
if (status === '204' && response.content) {
46+
errors.push(collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGES.NON_EMPTY_RESPONSE));
47+
}
48+
});
49+
50+
if (errors.length === 0) {
51+
collectAdoption(path, RULE_NAME);
52+
}
53+
});
54+
55+
return errors;
56+
};

0 commit comments

Comments
 (0)