Skip to content

Commit 1c7a270

Browse files
authored
fix: validate nullable oas3 example correctly (#1980)
1 parent 006c0ec commit 1c7a270

File tree

5 files changed

+60
-8
lines changed

5 files changed

+60
-8
lines changed

.changeset/rich-falcons-act.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@redocly/openapi-core": patch
3+
---
4+
5+
Fixed the `no-invalid-schema-examples` rule that incorrectly validated nullable OpenAPI 3.0 schemas.

packages/core/src/rules/common/__tests__/no-invalid-schema-examples.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,39 @@ describe('no-invalid-schema-examples', () => {
4848
]
4949
`);
5050
});
51+
52+
it('should not report on nullable example for OAS3', async () => {
53+
const document = parseYamlToDocument(
54+
outdent`
55+
openapi: 3.0.3
56+
info: {}
57+
paths:
58+
/subscriptions:
59+
get:
60+
responses:
61+
"200":
62+
content:
63+
application/json:
64+
schema:
65+
nullable: true
66+
type: object
67+
example: null
68+
allOf:
69+
- $ref: "#/components/schemas/RiskMetadata"
70+
components:
71+
schemas:
72+
RiskMetadata:
73+
type: object
74+
`,
75+
'foobar.yaml'
76+
);
77+
78+
const results = await lintDocument({
79+
externalRefResolver: new BaseResolver(),
80+
document,
81+
config: await makeConfig({ rules: { 'no-invalid-schema-examples': 'error' } }),
82+
});
83+
84+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
85+
});
5186
});

packages/core/src/rules/common/no-invalid-schema-examples.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,37 @@
11
import { getAdditionalPropertiesOption, validateExample } from '../utils';
22

33
import type { UserContext } from '../../walk';
4-
import type { Oas3_1Schema } from '../../typings/openapi';
4+
import type { Oas3_1Schema, Oas3Schema } from '../../typings/openapi';
5+
import type { Oas2Rule, Oas3Rule } from '../../visitors';
56

6-
export const NoInvalidSchemaExamples: any = (opts: any) => {
7+
export const NoInvalidSchemaExamples: Oas3Rule | Oas2Rule = (opts: any) => {
78
const allowAdditionalProperties = getAdditionalPropertiesOption(opts) ?? false;
89
return {
910
Schema: {
10-
leave(schema: Oas3_1Schema, ctx: UserContext) {
11-
if (schema.examples) {
12-
for (const example of schema.examples) {
11+
leave(schema: Oas3_1Schema | Oas3Schema, ctx: UserContext) {
12+
const examples = (schema as Oas3_1Schema).examples;
13+
if (examples) {
14+
for (const example of examples) {
1315
validateExample(
1416
example,
1517
schema,
16-
ctx.location.child(['examples', schema.examples.indexOf(example)]),
18+
ctx.location.child(['examples', examples.indexOf(example)]),
1719
ctx,
1820
allowAdditionalProperties
1921
);
2022
}
2123
}
24+
2225
if (schema.example !== undefined) {
26+
// Handle nullable example for OAS3
27+
if (
28+
(schema as Oas3Schema).nullable === true &&
29+
schema.example === null &&
30+
schema.type !== undefined
31+
) {
32+
return;
33+
}
34+
2335
validateExample(schema.example, schema, ctx.location.child('example'), ctx, true);
2436
}
2537
},

packages/core/src/rules/oas2/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const rules: Oas2RuleSet<'built-in'> = {
5252
//@ts-ignore TODO: This is depricated property `spec` and should be removed in the future
5353
spec: Struct as Oas2Rule,
5454
struct: Struct as Oas2Rule,
55-
'no-invalid-schema-examples': NoInvalidSchemaExamples,
55+
'no-invalid-schema-examples': NoInvalidSchemaExamples as Oas2Rule,
5656
'no-invalid-parameter-examples': NoInvalidParameterExamples,
5757
'info-contact': InfoContact as Oas2Rule,
5858
'info-license': InfoLicense as Oas2Rule,

packages/core/src/rules/oas3/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export const rules: Oas3RuleSet<'built-in'> = {
109109
'request-mime-type': RequestMimeType,
110110
'response-mime-type': ResponseMimeType,
111111
'path-segment-plural': PathSegmentPlural as Oas3Rule,
112-
'no-invalid-schema-examples': NoInvalidSchemaExamples,
112+
'no-invalid-schema-examples': NoInvalidSchemaExamples as Oas3Rule,
113113
'no-invalid-parameter-examples': NoInvalidParameterExamples,
114114
'response-contains-header': ResponseContainsHeader as Oas3Rule,
115115
'response-contains-property': ResponseContainsProperty,

0 commit comments

Comments
 (0)