Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading