Skip to content

Commit 462446c

Browse files
Merge conflict
2 parents 832f283 + 99823b3 commit 462446c

File tree

8 files changed

+318
-14
lines changed

8 files changed

+318
-14
lines changed

.github/workflows/spectral-lint.yml

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,22 @@ name: Spectral Lint Commited OpenAPI Spec
33
# Trigger the workflow on pull requests and pushes to the main branch
44
on:
55
pull_request:
6-
paths:
6+
paths:
77
- 'tools/spectral/**'
88
- 'openapi/**.yaml'
99
- 'package.json'
1010
push:
1111
branches:
1212
- main
13-
paths:
13+
paths:
1414
- 'tools/spectral/**'
1515
- 'openapi/**.yaml'
1616
- 'package.json'
17-
permissions:
18-
issues: write
19-
contents: write
20-
checks: write
2117

2218
jobs:
2319
spectral-lint:
2420
runs-on: ubuntu-latest
25-
21+
2622
steps:
2723
# Checkout the code
2824
- name: Checkout repository
@@ -31,8 +27,15 @@ jobs:
3127
sparse-checkout: |
3228
openapi/
3329
tools/spectral
30+
- name: Setup Node
31+
uses: actions/setup-node@v4
32+
with:
33+
node-version: '20.x'
34+
cache: 'npm'
35+
- name: Install npm dependencies
36+
run: npm install
3437
- name: Fetch OAS file from Dev Branch
35-
run: curl -o dev-oas.yaml "https://raw.githubusercontent.com/mongodb/openapi/refs/heads/dev/openapi/.raw/v2.yaml"
38+
run: curl -O "https://raw.githubusercontent.com/mongodb/openapi/refs/heads/dev/openapi/.raw/v2.yaml"
3639
working-directory: ${{ github.workspace }}
3740
- name: Spectral action
3841
uses: stoplightio/spectral-action@2ad0b9302e32a77c1caccf474a9b2191a8060d83
@@ -43,9 +46,5 @@ jobs:
4346
- name: IPA validation action
4447
uses: stoplightio/spectral-action@2ad0b9302e32a77c1caccf474a9b2191a8060d83
4548
with:
46-
file_glob: dev-oas.yaml
47-
spectral_ruleset: tools/spectral/ipa/ipa-spectral.yaml
48-
- name: IPA validation
49-
run: |
50-
npx spectral lint dev-oas.yaml --ruleset=${{ github.workspace }}/tools/spectral/ipa/ipa-spectral.yaml
51-
49+
file_glob: v2.yaml
50+
spectral_ruleset: tools/spectral/ipa/ipa-spectral.yaml
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import testRule from './__helpers__/testRule';
2+
import { DiagnosticSeverity } from '@stoplight/types';
3+
4+
testRule('xgen-IPA-113-singleton-must-not-have-delete-method', [
5+
{
6+
name: 'valid resources',
7+
document: {
8+
paths: {
9+
'/resource': {
10+
post: {},
11+
get: {},
12+
},
13+
'/resource/{exampleId}': {
14+
get: {},
15+
patch: {},
16+
delete: {},
17+
},
18+
'/resource/{exampleId}/singleton': {
19+
get: {},
20+
patch: {},
21+
},
22+
},
23+
},
24+
errors: [],
25+
},
26+
{
27+
name: 'invalid resource',
28+
document: {
29+
paths: {
30+
'/resource/{exampleId}/singleton': {
31+
delete: {},
32+
},
33+
},
34+
},
35+
errors: [
36+
{
37+
code: 'xgen-IPA-113-singleton-must-not-have-delete-method',
38+
message:
39+
'Singleton resources must not define the Delete standard method. If this is not a singleton resource, please implement all CRUDL methods.',
40+
path: ['paths', '/resource/{exampleId}/singleton'],
41+
severity: DiagnosticSeverity.Warning,
42+
},
43+
],
44+
},
45+
{
46+
name: 'invalid resources with exceptions',
47+
document: {
48+
paths: {
49+
'/resource/{exampleId}/singleton': {
50+
delete: {},
51+
'x-xgen-IPA-exception': {
52+
'xgen-IPA-113-singleton-must-not-have-delete-method': 'reason',
53+
},
54+
},
55+
},
56+
},
57+
errors: [],
58+
},
59+
]);
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import testRule from './__helpers__/testRule';
2+
import { DiagnosticSeverity } from '@stoplight/types';
3+
4+
testRule('xgen-IPA-113-singleton-should-have-update-method', [
5+
{
6+
name: 'valid resources',
7+
document: {
8+
paths: {
9+
'/resource/{exampleId}/singletonOne': {
10+
patch: {},
11+
},
12+
'/resource/{exampleId}/singletonTwo': {
13+
put: {},
14+
},
15+
'/resource/{exampleId}/singletonThree': {
16+
patch: {},
17+
put: {},
18+
},
19+
},
20+
},
21+
errors: [],
22+
},
23+
{
24+
name: 'invalid resource',
25+
document: {
26+
paths: {
27+
'/resource/{exampleId}/singletonOne': {
28+
get: {},
29+
},
30+
'/resource/{exampleId}/singletonTwo': {},
31+
},
32+
},
33+
errors: [
34+
{
35+
code: 'xgen-IPA-113-singleton-should-have-update-method',
36+
message:
37+
'Singleton resources should define the Update method. If this is not a singleton resource, please implement all CRUDL methods.',
38+
path: ['paths', '/resource/{exampleId}/singletonOne'],
39+
severity: DiagnosticSeverity.Warning,
40+
},
41+
{
42+
code: 'xgen-IPA-113-singleton-should-have-update-method',
43+
message:
44+
'Singleton resources should define the Update method. If this is not a singleton resource, please implement all CRUDL methods.',
45+
path: ['paths', '/resource/{exampleId}/singletonTwo'],
46+
severity: DiagnosticSeverity.Warning,
47+
},
48+
],
49+
},
50+
{
51+
name: 'invalid resources with exceptions',
52+
document: {
53+
paths: {
54+
'/resource/{exampleId}/singletonOne': {
55+
get: {},
56+
'x-xgen-IPA-exception': {
57+
'xgen-IPA-113-singleton-should-have-update-method': 'reason',
58+
},
59+
},
60+
'/resource/{exampleId}/singletonTwo': {
61+
'x-xgen-IPA-exception': {
62+
'xgen-IPA-113-singleton-should-have-update-method': 'reason',
63+
},
64+
},
65+
},
66+
},
67+
errors: [],
68+
},
69+
]);

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
functions:
55
- IPA113SingletonHasNoId
6+
- IPA113SingletonHasNoDeleteMethod
7+
- IPA113SingletonHasUpdateMethod
68

79
rules:
810
xgen-IPA-113-singleton-must-not-have-id:
@@ -21,3 +23,29 @@ rules:
2123
given: '$.paths[*]'
2224
then:
2325
function: 'IPA113SingletonHasNoId'
26+
xgen-IPA-113-singleton-must-not-have-delete-method:
27+
description: |
28+
Singleton resources must not define the Delete standard method.
29+
30+
##### Implementation details
31+
Rule checks for the following conditions:
32+
- Applies only to singleton resources
33+
- Checks that the resource does not have a DELETE method defined
34+
message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-113-singleton-must-not-have-delete-method'
35+
severity: warn
36+
given: '$.paths[*]'
37+
then:
38+
function: 'IPA113SingletonHasNoDeleteMethod'
39+
xgen-IPA-113-singleton-should-have-update-method:
40+
description: |
41+
Singleton resources should define the Update method. Validation for the presence of Get method is covered by IPA-104 (see [xgen-IPA-104-resource-has-GET](https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-104-resource-has-GET)).
42+
43+
##### Implementation details
44+
Rule checks for the following conditions:
45+
- Applies only to singleton resources
46+
- Checks that the resource has the PUT and/or PATCH methods defined
47+
message: '{{error}} https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-113-singleton-should-have-update-method'
48+
severity: warn
49+
given: '$.paths[*]'
50+
then:
51+
function: 'IPA113SingletonHasUpdateMethod'

tools/spectral/ipa/rulesets/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,26 @@ Rule checks for the following conditions:
482482
- Verifies that no schema contains 'id' or '_id' properties in their object definitions
483483
- Fails if any response schema contains these identifier properties
484484

485+
#### xgen-IPA-113-singleton-must-not-have-delete-method
486+
487+
![warn](https://img.shields.io/badge/warning-yellow)
488+
Singleton resources must not define the Delete standard method.
489+
490+
##### Implementation details
491+
Rule checks for the following conditions:
492+
- Applies only to singleton resources
493+
- Checks that the resource does not have a DELETE method defined
494+
495+
#### xgen-IPA-113-singleton-should-have-update-method
496+
497+
![warn](https://img.shields.io/badge/warning-yellow)
498+
Singleton resources should define the Update method. Validation for the presence of Get method is covered by IPA-104 (see [xgen-IPA-104-resource-has-GET](https://mdb.link/mongodb-atlas-openapi-validation#xgen-IPA-104-resource-has-GET)).
499+
500+
##### Implementation details
501+
Rule checks for the following conditions:
502+
- Applies only to singleton resources
503+
- Checks that the resource has the PUT and/or PATCH methods defined
504+
485505

486506

487507
### IPA-123
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {
2+
getResourcePathItems,
3+
isSingletonResource,
4+
isResourceCollectionIdentifier,
5+
hasDeleteMethod,
6+
} from './utils/resourceEvaluation.js';
7+
import { hasException } from './utils/exceptions.js';
8+
import {
9+
collectAdoption,
10+
collectAndReturnViolation,
11+
collectException,
12+
handleInternalError,
13+
} from './utils/collectionUtils.js';
14+
15+
const RULE_NAME = 'xgen-IPA-113-singleton-must-not-have-delete-method';
16+
const ERROR_MESSAGE =
17+
'Singleton resources must not define the Delete standard method. If this is not a singleton resource, please implement all CRUDL methods.';
18+
19+
export default (input, opts, { path, documentInventory }) => {
20+
const oas = documentInventory.resolved;
21+
const resourcePath = path[1];
22+
const resourcePathItems = getResourcePathItems(resourcePath, oas.paths);
23+
24+
if (!(isResourceCollectionIdentifier(resourcePath) && isSingletonResource(resourcePathItems))) {
25+
return;
26+
}
27+
28+
if (hasException(input, RULE_NAME)) {
29+
collectException(input, RULE_NAME, path);
30+
return;
31+
}
32+
33+
const errors = checkViolationsAndReturnErrors(input, path);
34+
if (errors.length !== 0) {
35+
return collectAndReturnViolation(path, RULE_NAME, errors);
36+
}
37+
collectAdoption(path, RULE_NAME);
38+
};
39+
40+
function checkViolationsAndReturnErrors(input, path) {
41+
try {
42+
if (hasDeleteMethod(input)) {
43+
return [{ path, message: ERROR_MESSAGE }];
44+
}
45+
return [];
46+
} catch (e) {
47+
handleInternalError(RULE_NAME, path, e);
48+
}
49+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import {
2+
getResourcePathItems,
3+
isSingletonResource,
4+
isResourceCollectionIdentifier,
5+
hasPutMethod,
6+
hasPatchMethod,
7+
} from './utils/resourceEvaluation.js';
8+
import { hasException } from './utils/exceptions.js';
9+
import {
10+
collectAdoption,
11+
collectAndReturnViolation,
12+
collectException,
13+
handleInternalError,
14+
} from './utils/collectionUtils.js';
15+
16+
const RULE_NAME = 'xgen-IPA-113-singleton-should-have-update-method';
17+
const ERROR_MESSAGE =
18+
'Singleton resources should define the Update method. If this is not a singleton resource, please implement all CRUDL methods.';
19+
20+
export default (input, opts, { path, documentInventory }) => {
21+
const oas = documentInventory.resolved;
22+
const resourcePath = path[1];
23+
const resourcePathItems = getResourcePathItems(resourcePath, oas.paths);
24+
25+
if (!(isResourceCollectionIdentifier(resourcePath) && isSingletonResource(resourcePathItems))) {
26+
return;
27+
}
28+
29+
if (hasException(input, RULE_NAME)) {
30+
collectException(input, RULE_NAME, path);
31+
return;
32+
}
33+
34+
const errors = checkViolationsAndReturnErrors(input, path);
35+
if (errors.length !== 0) {
36+
return collectAndReturnViolation(path, RULE_NAME, errors);
37+
}
38+
collectAdoption(path, RULE_NAME);
39+
};
40+
41+
function checkViolationsAndReturnErrors(input, path) {
42+
try {
43+
if (!(hasPutMethod(input) || hasPatchMethod(input))) {
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/utils/resourceEvaluation.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,36 @@ export function hasPostMethod(pathObject) {
102102
return Object.keys(pathObject).includes('post');
103103
}
104104

105+
/**
106+
* Checks if a path object has a DELETE method
107+
*
108+
* @param pathObject the path object to evaluate
109+
* @returns {boolean}
110+
*/
111+
export function hasDeleteMethod(pathObject) {
112+
return Object.keys(pathObject).includes('delete');
113+
}
114+
115+
/**
116+
* Checks if a path object has a PUT method
117+
*
118+
* @param pathObject the path object to evaluate
119+
* @returns {boolean}
120+
*/
121+
export function hasPutMethod(pathObject) {
122+
return Object.keys(pathObject).includes('put');
123+
}
124+
125+
/**
126+
* Checks if a path object has a PATCH method
127+
*
128+
* @param pathObject the path object to evaluate
129+
* @returns {boolean}
130+
*/
131+
export function hasPatchMethod(pathObject) {
132+
return Object.keys(pathObject).includes('patch');
133+
}
134+
105135
/**
106136
* Get all path items for a resource based on the path for the resource collection
107137
* For example, resource collection path '/resource' may return path items for ['/resource', '/resource{id}', '/resource{id}:customMethod']

0 commit comments

Comments
 (0)