Skip to content

Commit 84a6252

Browse files
authored
feat(rules): update operation-tag-defined rule (#1876)
1 parent ff0e2bf commit 84a6252

File tree

5 files changed

+107
-14
lines changed

5 files changed

+107
-14
lines changed

.changeset/tasty-cougars-dress.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@redocly/openapi-core": patch
3+
"@redocly/cli": patch
4+
---
5+
6+
Updated `operation-tag-defined` built-in rule to verify tags are defined on the operation prior to matching them to a global tag.

__tests__/lint/turn-on-all-rules/snapshot.js

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,21 @@ Operation object should contain \`operationId\` field.
5555
Error was generated by the operation-operationId rule.
5656
5757
58-
[5] openapi.yaml:9:5 at #/paths/~1ping~1{id}~1{test}/get/summary
58+
[5] openapi.yaml:9:5 at #/paths/~1ping~1{id}~1{test}/get
59+
60+
Operation tags should be defined
61+
62+
7 | paths:
63+
8 | '/ping/{id}/{test}':
64+
9 | get:
65+
| ^^^
66+
10 | parameters:
67+
11 | - in: path
68+
69+
Error was generated by the operation-tag-defined rule.
70+
71+
72+
[6] openapi.yaml:9:5 at #/paths/~1ping~1{id}~1{test}/get/summary
5973
6074
Operation object should contain \`summary\` field.
6175
@@ -69,7 +83,7 @@ Operation object should contain \`summary\` field.
6983
Error was generated by the operation-summary rule.
7084
7185
72-
[6] openapi.yaml:9:5 at #/paths/~1ping~1{id}~1{test}/get/description
86+
[7] openapi.yaml:9:5 at #/paths/~1ping~1{id}~1{test}/get/description
7387
7488
Operation object should contain \`description\` field.
7589
@@ -83,7 +97,7 @@ Operation object should contain \`description\` field.
8397
Error was generated by the operation-description rule.
8498
8599
86-
[7] openapi.yaml:12:17 at #/paths/~1ping~1{id}~1{test}/get/parameters/0/name
100+
[8] openapi.yaml:12:17 at #/paths/~1ping~1{id}~1{test}/get/parameters/0/name
87101
88102
Path parameter \`test_id\` is not used in the path \`/ping/{id}/{test}\`.
89103
@@ -97,7 +111,7 @@ Path parameter \`test_id\` is not used in the path \`/ping/{id}/{test}\`.
97111
Error was generated by the path-parameters-defined rule.
98112
99113
100-
[8] openapi.yaml:12:17 at #/paths/~1ping~1{id}~1{test}/get/parameters/0/name
114+
[9] openapi.yaml:12:17 at #/paths/~1ping~1{id}~1{test}/get/parameters/0/name
101115
102116
Path parameter \`test_id\` is not used in the path \`/ping/{id}/{test}\`.
103117
@@ -111,7 +125,7 @@ Path parameter \`test_id\` is not used in the path \`/ping/{id}/{test}\`.
111125
Error was generated by the path-params-defined rule.
112126
113127
114-
[9] openapi.yaml:17:7 at #/paths/~1ping~1{id}~1{test}/get/responses
128+
[10] openapi.yaml:17:7 at #/paths/~1ping~1{id}~1{test}/get/responses
115129
116130
Operation must have at least one \`4XX\` response.
117131
@@ -125,7 +139,7 @@ Operation must have at least one \`4XX\` response.
125139
Error was generated by the operation-4xx-response rule.
126140
127141
128-
[10] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
142+
[11] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
129143
130144
The operation does not define the path parameter \`{id}\` expected by path \`/ping/{id}/{test}\`.
131145
@@ -139,7 +153,7 @@ The operation does not define the path parameter \`{id}\` expected by path \`/pi
139153
Error was generated by the path-parameters-defined rule.
140154
141155
142-
[11] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
156+
[12] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
143157
144158
The operation does not define the path parameter \`{test}\` expected by path \`/ping/{id}/{test}\`.
145159
@@ -153,7 +167,7 @@ The operation does not define the path parameter \`{test}\` expected by path \`/
153167
Error was generated by the path-parameters-defined rule.
154168
155169
156-
[12] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
170+
[13] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
157171
158172
The operation does not define the path parameter \`{id}\` expected by path \`/ping/{id}/{test}\`.
159173
@@ -167,7 +181,7 @@ The operation does not define the path parameter \`{id}\` expected by path \`/pi
167181
Error was generated by the path-params-defined rule.
168182
169183
170-
[13] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
184+
[14] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
171185
172186
The operation does not define the path parameter \`{test}\` expected by path \`/ping/{id}/{test}\`.
173187
@@ -181,7 +195,7 @@ The operation does not define the path parameter \`{test}\` expected by path \`/
181195
Error was generated by the path-params-defined rule.
182196
183197
184-
[14] openapi.yaml:8:3 at #/paths/~1ping~1{id}~1{test}
198+
[15] openapi.yaml:8:3 at #/paths/~1ping~1{id}~1{test}
185199
186200
path segment \`ping\` should be plural.
187201
@@ -197,7 +211,7 @@ Error was generated by the path-segment-plural rule.
197211
198212
openapi.yaml: validated in <test>ms
199213
200-
❌ Validation failed with 14 errors.
214+
❌ Validation failed with 15 errors.
201215
run \`redocly lint --generate-ignore-file\` to add all problems to the ignore file.
202216
203217

docs/rules/oas/operation-tag-defined.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ Disallows use of tags in operations that aren't globally defined.
1616

1717
OpenAPI tags can be used for different purposes.
1818
Tags are declared in the root of the OpenAPI description.
19-
Then, they are used in operations.
19+
Then, they are used in operations. They are recommmended for grouping common operations within your api description.
2020

21-
This rule says that if an operation uses a tag, it must be defined in the root tags declaration.
21+
This rule first checks if a tag exists on the operation. Subsequently, if an operation uses a tag, it must be defined in the root `tags` declaration.
2222
This rule helps prevent typos and tag explosion.
2323

2424
## Configuration
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { outdent } from 'outdent';
2+
import { lintDocument } from '../../../lint';
3+
import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../../../__tests__/utils';
4+
import { BaseResolver } from '../../../resolve';
5+
6+
describe('Oas3 operation-tag-defined', () => {
7+
it('should not report on operation object if at least one tag is defined', async () => {
8+
const document = parseYamlToDocument(
9+
outdent`
10+
openapi: 3.0.4
11+
tags:
12+
- name: a
13+
paths:
14+
/some:
15+
get:
16+
tags:
17+
- a
18+
`,
19+
'foobar.yaml'
20+
);
21+
22+
const results = await lintDocument({
23+
externalRefResolver: new BaseResolver(),
24+
document,
25+
config: await makeConfig({ rules: { 'operation-tag-defined': 'error' } }),
26+
});
27+
28+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
29+
});
30+
31+
it('should report on operation object if no tags are defined', async () => {
32+
const document = parseYamlToDocument(
33+
outdent`
34+
openapi: 3.0.4
35+
tags:
36+
- name: a
37+
paths:
38+
/some:
39+
get:
40+
`,
41+
'foobar.yaml'
42+
);
43+
44+
const results = await lintDocument({
45+
externalRefResolver: new BaseResolver(),
46+
document,
47+
config: await makeConfig({ rules: { 'operation-tag-defined': 'error' } }),
48+
});
49+
50+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
51+
[
52+
{
53+
"location": [
54+
{
55+
"pointer": "#/paths/~1some/get",
56+
"reportOnKey": true,
57+
"source": "foobar.yaml",
58+
},
59+
],
60+
"message": "Operation tags should be defined",
61+
"ruleId": "operation-tag-defined",
62+
"severity": "error",
63+
"suggest": [],
64+
},
65+
]
66+
`);
67+
});
68+
});

packages/core/src/rules/common/operation-tag-defined.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const OperationTagDefined: Oas3Rule | Oas2Rule = () => {
1111
definedTags = new Set((root.tags ?? []).map((t) => t.name));
1212
},
1313
Operation(operation: Oas2Operation | Oas3Operation, { report, location }: UserContext) {
14-
if (operation.tags) {
14+
if (operation?.tags) {
1515
for (let i = 0; i < operation.tags.length; i++) {
1616
if (!definedTags.has(operation.tags[i])) {
1717
report({
@@ -20,6 +20,11 @@ export const OperationTagDefined: Oas3Rule | Oas2Rule = () => {
2020
});
2121
}
2222
}
23+
} else {
24+
report({
25+
message: `Operation tags should be defined`,
26+
location: location.key(),
27+
});
2328
}
2429
},
2530
};

0 commit comments

Comments
 (0)