Skip to content

Commit

Permalink
feat(rules): update operation-tag-defined rule
Browse files Browse the repository at this point in the history
check for `tags` existence in `Operation` node`
  • Loading branch information
jeremyfiel authored and tatomyr committed Feb 18, 2025
1 parent ff0e2bf commit e096d2a
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 14 deletions.
6 changes: 6 additions & 0 deletions .changeset/tasty-cougars-dress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@redocly/openapi-core": patch
"@redocly/cli": patch
---

Updated `operation-tag-defined` built-in rule to verify tags are defined on the operation prior to matching them to a global tag.
36 changes: 25 additions & 11 deletions __tests__/lint/turn-on-all-rules/snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,21 @@ Operation object should contain \`operationId\` field.
Error was generated by the operation-operationId rule.
[5] openapi.yaml:9:5 at #/paths/~1ping~1{id}~1{test}/get/summary
[5] openapi.yaml:9:5 at #/paths/~1ping~1{id}~1{test}/get
Operation tags should be defined
7 | paths:
8 | '/ping/{id}/{test}':
9 | get:
| ^^^
10 | parameters:
11 | - in: path
Error was generated by the operation-tag-defined rule.
[6] openapi.yaml:9:5 at #/paths/~1ping~1{id}~1{test}/get/summary
Operation object should contain \`summary\` field.
Expand All @@ -69,7 +83,7 @@ Operation object should contain \`summary\` field.
Error was generated by the operation-summary rule.
[6] openapi.yaml:9:5 at #/paths/~1ping~1{id}~1{test}/get/description
[7] openapi.yaml:9:5 at #/paths/~1ping~1{id}~1{test}/get/description
Operation object should contain \`description\` field.
Expand All @@ -83,7 +97,7 @@ Operation object should contain \`description\` field.
Error was generated by the operation-description rule.
[7] openapi.yaml:12:17 at #/paths/~1ping~1{id}~1{test}/get/parameters/0/name
[8] openapi.yaml:12:17 at #/paths/~1ping~1{id}~1{test}/get/parameters/0/name
Path parameter \`test_id\` is not used in the path \`/ping/{id}/{test}\`.
Expand All @@ -97,7 +111,7 @@ Path parameter \`test_id\` is not used in the path \`/ping/{id}/{test}\`.
Error was generated by the path-parameters-defined rule.
[8] openapi.yaml:12:17 at #/paths/~1ping~1{id}~1{test}/get/parameters/0/name
[9] openapi.yaml:12:17 at #/paths/~1ping~1{id}~1{test}/get/parameters/0/name
Path parameter \`test_id\` is not used in the path \`/ping/{id}/{test}\`.
Expand All @@ -111,7 +125,7 @@ Path parameter \`test_id\` is not used in the path \`/ping/{id}/{test}\`.
Error was generated by the path-params-defined rule.
[9] openapi.yaml:17:7 at #/paths/~1ping~1{id}~1{test}/get/responses
[10] openapi.yaml:17:7 at #/paths/~1ping~1{id}~1{test}/get/responses
Operation must have at least one \`4XX\` response.
Expand All @@ -125,7 +139,7 @@ Operation must have at least one \`4XX\` response.
Error was generated by the operation-4xx-response rule.
[10] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
[11] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
The operation does not define the path parameter \`{id}\` expected by path \`/ping/{id}/{test}\`.
Expand All @@ -139,7 +153,7 @@ The operation does not define the path parameter \`{id}\` expected by path \`/pi
Error was generated by the path-parameters-defined rule.
[11] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
[12] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
The operation does not define the path parameter \`{test}\` expected by path \`/ping/{id}/{test}\`.
Expand All @@ -153,7 +167,7 @@ The operation does not define the path parameter \`{test}\` expected by path \`/
Error was generated by the path-parameters-defined rule.
[12] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
[13] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
The operation does not define the path parameter \`{id}\` expected by path \`/ping/{id}/{test}\`.
Expand All @@ -167,7 +181,7 @@ The operation does not define the path parameter \`{id}\` expected by path \`/pi
Error was generated by the path-params-defined rule.
[13] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
[14] openapi.yaml:10:7 at #/paths/~1ping~1{id}~1{test}/get/parameters
The operation does not define the path parameter \`{test}\` expected by path \`/ping/{id}/{test}\`.
Expand All @@ -181,7 +195,7 @@ The operation does not define the path parameter \`{test}\` expected by path \`/
Error was generated by the path-params-defined rule.
[14] openapi.yaml:8:3 at #/paths/~1ping~1{id}~1{test}
[15] openapi.yaml:8:3 at #/paths/~1ping~1{id}~1{test}
path segment \`ping\` should be plural.
Expand All @@ -197,7 +211,7 @@ Error was generated by the path-segment-plural rule.
openapi.yaml: validated in <test>ms
❌ Validation failed with 14 errors.
❌ Validation failed with 15 errors.
run \`redocly lint --generate-ignore-file\` to add all problems to the ignore file.
Expand Down
4 changes: 2 additions & 2 deletions docs/rules/oas/operation-tag-defined.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ Disallows use of tags in operations that aren't globally defined.

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

This rule says that if an operation uses a tag, it must be defined in the root tags declaration.
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.
This rule helps prevent typos and tag explosion.

## Configuration
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { outdent } from 'outdent';
import { lintDocument } from '../../../lint';
import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../../../__tests__/utils';
import { BaseResolver } from '../../../resolve';

describe('Oas3 operation-tag-defined', () => {
it('should not report on operation object if at least one tag is defined', async () => {
const document = parseYamlToDocument(
outdent`
openapi: 3.0.4
tags:
- name: a
paths:
/some:
get:
tags:
- a
`,
'foobar.yaml'
);

const results = await lintDocument({
externalRefResolver: new BaseResolver(),
document,
config: await makeConfig({ rules: { 'operation-tag-defined': 'error' } }),
});

expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
});

it('should report on operation object if no tags are defined', async () => {
const document = parseYamlToDocument(
outdent`
openapi: 3.0.4
tags:
- name: a
paths:
/some:
get:
`,
'foobar.yaml'
);

const results = await lintDocument({
externalRefResolver: new BaseResolver(),
document,
config: await makeConfig({ rules: { 'operation-tag-defined': 'error' } }),
});

expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
[
{
"location": [
{
"pointer": "#/paths/~1some/get",
"reportOnKey": true,
"source": "foobar.yaml",
},
],
"message": "Operation tags should be defined",
"ruleId": "operation-tag-defined",
"severity": "error",
"suggest": [],
},
]
`);
});
});
7 changes: 6 additions & 1 deletion packages/core/src/rules/common/operation-tag-defined.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const OperationTagDefined: Oas3Rule | Oas2Rule = () => {
definedTags = new Set((root.tags ?? []).map((t) => t.name));
},
Operation(operation: Oas2Operation | Oas3Operation, { report, location }: UserContext) {
if (operation.tags) {
if (operation?.tags) {
for (let i = 0; i < operation.tags.length; i++) {
if (!definedTags.has(operation.tags[i])) {
report({
Expand All @@ -20,6 +20,11 @@ export const OperationTagDefined: Oas3Rule | Oas2Rule = () => {
});
}
}
} else {
report({
message: `Operation tags should be defined`,
location: location.key(),
});
}
},
};
Expand Down

0 comments on commit e096d2a

Please sign in to comment.