Skip to content

Commit 4c9c055

Browse files
authored
feat: add nullable-type-sibling rule instead of validating the nullable field in the struct rule (#2146)
1 parent 8114203 commit 4c9c055

File tree

17 files changed

+183
-72
lines changed

17 files changed

+183
-72
lines changed

.changeset/fuzzy-ducks-exist.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@redocly/openapi-core": minor
3+
"@redocly/cli": minor
4+
---
5+
6+
Extracted `nullable` validation from the `struct` rule into a new `nullable-type-sibling` rule for OpenAPI 3.0. This allows users to disable `nullable` validation separately from other structural checks.

__tests__/lint/no-invalid-media-type-examples-invalid-schema/snapshot.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ The `type` field must be defined when the `nullable` field is used.
99
17 | paths:
1010
18 | /test:
1111

12-
Error was generated by the struct rule.
12+
Error was generated by the nullable-type-sibling rule.
1313

1414

1515
[2] openapi.yaml:28:17 at #/paths/~1test/get/responses/202/content/application~1json/schema

docs/@v2/rules/minimal.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Warnings:
2828
- [no-server-example.com](./oas/no-server-example-com.md)
2929
- [no-undefined-server-variable](./oas/no-undefined-server-variable.md)
3030
- [no-unused-components](./oas/no-unused-components.md)
31+
- [nullable-type-sibling](./oas/nullable-type-sibling.md)
3132
- [operation-2xx-response](./oas/operation-2xx-response.md)
3233
- [operation-operationId-unique](./oas/operation-operationId-unique.md)
3334
- [operation-operationId-url-safe](./oas/operation-operationId-url-safe.md)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
slug: /docs/cli/v2/rules/oas/nullable-type-sibling
3+
---
4+
5+
# nullable-type-sibling
6+
7+
Ensures that all schemas with `nullable` field have a `type` field explicitly set.
8+
9+
| OAS | Compatibility |
10+
| --- | ------------- |
11+
| 3.0 ||
12+
13+
## API design principles
14+
15+
The `nullable` field is not allowed without the `type` field explicitly set.
16+
17+
## Configuration
18+
19+
| Option | Type | Description |
20+
| -------- | ------ | ------------------------------------------------------------------------------------------ |
21+
| severity | string | Possible values: `off`, `warn`, `error`. Default `error` (in `recommended` configuration). |
22+
23+
An example configuration:
24+
25+
```yaml
26+
rules:
27+
nullable-type-sibling: error
28+
```
29+
30+
## Examples
31+
32+
Given this configuration:
33+
34+
```yaml
35+
rules:
36+
nullable-type-sibling: error
37+
```
38+
39+
Example of an **incorrect** usage of `nullable` field:
40+
41+
```yaml
42+
components:
43+
schemas:
44+
Incorrect:
45+
nullable: true
46+
ReferencingATypeButStillIncorrect:
47+
nullable: true
48+
allOf:
49+
- $ref: '#/components/schemas/SomeType'
50+
SomeType:
51+
type: string
52+
53+
```
54+
55+
Example of a **correct** usage:
56+
57+
```yaml
58+
components:
59+
schemas:
60+
Correct:
61+
type: string
62+
nullable: true
63+
CorrectWithAllOf:
64+
type: object
65+
nullable: true
66+
allOf:
67+
- type: object
68+
properties:
69+
name:
70+
type: string
71+
- type: object
72+
properties:
73+
age:
74+
type: number
75+
```
76+
77+
## Related rules
78+
79+
- [struct](./struct.md)
80+
81+
## Resources
82+
83+
- [Rule source](https://github.com/Redocly/redocly-cli/blob/main/packages/core/src/rules/oas3/nullable-type-sibling.ts)
84+
- [Examples](https://redocly.com/learn/openapi/openapi-visual-reference/null)

docs/@v2/rules/recommended.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Errors:
1717
- [no-server-variables-empty-enum](./oas/no-server-variables-empty-enum.md)
1818
- [no-undefined-server-variable](./oas/no-undefined-server-variable.md)
1919
- [no-unresolved-refs](./oas/no-unresolved-refs.md)
20+
- [nullable-type-sibling](./oas/nullable-type-sibling.md)
2021
- [operation-operationId-unique](./oas/operation-operationId-unique.md)
2122
- [operation-operationId-url-safe](./oas/operation-operationId-url-safe.md)
2223
- [operation-parameters-unique](./oas/operation-parameters-unique.md)

docs/@v2/rules/ruleset-templates.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ rules:
107107
operation-singular-tag: off
108108
no-unresolved-refs: error
109109
no-enum-type-mismatch: warn
110+
nullable-type-sibling: warn
110111
paths-kebab-case: off
111112
struct: error
112113
spec-strict-refs: off
@@ -384,6 +385,7 @@ rules:
384385
operation-singular-tag: off
385386
no-unresolved-refs: error
386387
no-enum-type-mismatch: error
388+
nullable-type-sibling: error
387389
paths-kebab-case: off
388390
struct: error
389391
spec-strict-refs: off

packages/core/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ exports[`resolveConfig > should ignore minimal from the root and read local file
128128
"no-undefined-server-variable": "error",
129129
"no-unresolved-refs": "error",
130130
"no-unused-components": "warn",
131+
"nullable-type-sibling": "error",
131132
"operation-2xx-response": "off",
132133
"operation-4xx-problem-details-rfc7807": "off",
133134
"operation-4xx-response": "error",
@@ -433,6 +434,7 @@ exports[`resolveConfig > should resolve extends with local file config which con
433434
"no-undefined-server-variable": "error",
434435
"no-unresolved-refs": "error",
435436
"no-unused-components": "warn",
437+
"nullable-type-sibling": "error",
436438
"operation-2xx-response": "error",
437439
"operation-4xx-problem-details-rfc7807": "off",
438440
"operation-4xx-response": "off",

packages/core/src/config/__tests__/load.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ describe('loadConfig', () => {
267267
"no-undefined-server-variable": "warn",
268268
"no-unresolved-refs": "error",
269269
"no-unused-components": "warn",
270+
"nullable-type-sibling": "warn",
270271
"operation-2xx-response": "warn",
271272
"operation-4xx-problem-details-rfc7807": "off",
272273
"operation-4xx-response": "off",
@@ -503,6 +504,7 @@ describe('loadConfig', () => {
503504
"no-undefined-server-variable": "error",
504505
"no-unresolved-refs": "error",
505506
"no-unused-components": "warn",
507+
"nullable-type-sibling": "error",
506508
"operation-2xx-response": "warn",
507509
"operation-4xx-problem-details-rfc7807": "off",
508510
"operation-4xx-response": "warn",
@@ -750,6 +752,7 @@ describe('loadConfig', () => {
750752
"no-undefined-server-variable": "warn",
751753
"no-unresolved-refs": "error",
752754
"no-unused-components": "warn",
755+
"nullable-type-sibling": "warn",
753756
"operation-2xx-response": "warn",
754757
"operation-4xx-problem-details-rfc7807": "off",
755758
"operation-4xx-response": "off",

packages/core/src/config/all.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ const all: RawGovernanceConfig<'built-in'> = {
8686
'no-unused-components': 'error',
8787
'no-undefined-server-variable': 'error',
8888
'no-server-variables-empty-enum': 'error',
89+
'nullable-type-sibling': 'error',
8990
'operation-summary': 'error',
9091
'operation-operationId': 'error',
9192
'operation-operationId-unique': 'error',

packages/core/src/config/minimal.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ const minimal: RawGovernanceConfig<'built-in'> = {
8080
'no-unused-components': 'warn',
8181
'no-undefined-server-variable': 'warn',
8282
'no-server-variables-empty-enum': 'error',
83+
'nullable-type-sibling': 'warn',
8384
'operation-summary': 'warn',
8485
'operation-operationId': 'warn',
8586
'operation-operationId-unique': 'warn',

0 commit comments

Comments
 (0)