Skip to content

Commit 781f5ba

Browse files
authored
fix: do not fail linting when examples structure is invalid (#2539)
1 parent 9963d35 commit 781f5ba

File tree

7 files changed

+113
-4
lines changed

7 files changed

+113
-4
lines changed

.changeset/neat-bars-tie.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+
Fixed an issue where improperly structured OpenAPI examples caused the linter to fail.

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,38 @@ describe('no-invalid-parameter-examples', () => {
111111
]
112112
`);
113113
});
114+
115+
it('should not crash when examples in not an object', async () => {
116+
const document = parseYamlToDocument(
117+
outdent`
118+
openapi: 3.2.0
119+
paths:
120+
/:
121+
get:
122+
parameters:
123+
- name: foo
124+
in: query
125+
schema:
126+
type: string
127+
examples: Wrong multiple example
128+
- name: bar
129+
in: query
130+
schema:
131+
type: string
132+
examples:
133+
test: Wrong nested example
134+
`,
135+
'foobar.yaml'
136+
);
137+
138+
const results = await lintDocument({
139+
externalRefResolver: new BaseResolver(),
140+
document,
141+
config: await createConfig({
142+
rules: { 'no-invalid-parameter-examples': 'error' },
143+
}),
144+
});
145+
146+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
147+
});
114148
});

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,36 @@ describe('no-invalid-schema-examples', () => {
138138
]
139139
`);
140140
});
141+
142+
it('should not crash when examples in not an array', async () => {
143+
const document = parseYamlToDocument(
144+
outdent`
145+
openapi: 3.2.0
146+
paths:
147+
/:
148+
get:
149+
responses:
150+
200:
151+
content:
152+
application/json:
153+
schema:
154+
type: object
155+
properties:
156+
foo:
157+
type: string
158+
examples: Wrong multiple example
159+
`,
160+
'foobar.yaml'
161+
);
162+
163+
const results = await lintDocument({
164+
externalRefResolver: new BaseResolver(),
165+
document,
166+
config: await createConfig({
167+
rules: { 'no-invalid-schema-examples': 'error' },
168+
}),
169+
});
170+
171+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
172+
});
141173
});

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { validateExample } from '../utils.js';
22
import { isDefined } from '../../utils/is-defined.js';
3+
import { isPlainObject } from '../../utils/is-plain-object.js';
34

45
import type { UserContext } from '../../walk.js';
56
import type { Oas3Parameter } from '../../typings/openapi.js';
@@ -18,9 +19,9 @@ export const NoInvalidParameterExamples: any = (opts: any) => {
1819
);
1920
}
2021

21-
if (parameter.examples) {
22+
if (isPlainObject(parameter.examples)) {
2223
for (const [key, example] of Object.entries(parameter.examples)) {
23-
if ('value' in example) {
24+
if (isPlainObject(example) && 'value' in example) {
2425
validateExample(
2526
example.value,
2627
parameter.schema!,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const NoInvalidSchemaExamples: Oas3Rule | Oas2Rule = (opts: any) => {
1111
leave(schema: Oas3_1Schema | Oas3Schema, ctx: UserContext) {
1212
const examples = (schema as Oas3_1Schema).examples;
1313

14-
if (examples) {
14+
if (Array.isArray(examples)) {
1515
for (const example of examples) {
1616
validateExample(
1717
example,

packages/core/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,4 +793,39 @@ describe('no-invalid-media-type-examples', () => {
793793
]
794794
`);
795795
});
796+
797+
it('should not crash when examples in not an object', async () => {
798+
const document = parseYamlToDocument(
799+
outdent`
800+
openapi: 3.2.0
801+
paths:
802+
/:
803+
get:
804+
responses:
805+
200:
806+
content:
807+
application/json:
808+
schema:
809+
type: string
810+
examples: Wrong multiple example
811+
application+nested/json:
812+
schema:
813+
type: string
814+
examples:
815+
test: Wrong nested example
816+
817+
`,
818+
'foobar.yaml'
819+
);
820+
821+
const results = await lintDocument({
822+
externalRefResolver: new BaseResolver(),
823+
document,
824+
config: await createConfig({
825+
rules: { 'no-invalid-media-type-examples': 'error' },
826+
}),
827+
});
828+
829+
expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
830+
});
796831
});

packages/core/src/rules/oas3/no-invalid-media-type-examples.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { isRef } from '../../ref-utils.js';
22
import { validateExample } from '../utils.js';
33
import { isDefined } from '../../utils/is-defined.js';
4+
import { isPlainObject } from '../../utils/is-plain-object.js';
45

56
import type { Oas3Rule } from '../../visitors.js';
67
import type { Location } from '../../ref-utils.js';
@@ -18,7 +19,7 @@ export const ValidContentExamples: Oas3Rule = (opts) => {
1819

1920
if (isDefined(mediaType.example)) {
2021
resolveAndValidateExample(mediaType.example, location.child('example'));
21-
} else if (mediaType.examples) {
22+
} else if (isPlainObject(mediaType.examples)) {
2223
for (const exampleName of Object.keys(mediaType.examples)) {
2324
resolveAndValidateExample(
2425
mediaType.examples[exampleName],

0 commit comments

Comments
 (0)