Skip to content
Open
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/swift-pears-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@redocly/openapi-core': patch
'@redocly/cli': patch
---

Fixed an issue where `no-enum-type-mismatch` dropped violations and reported the wrong location when `type` was written as an array.
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,96 @@ describe('Oas3 typed enum', () => {
`);
});

it('should report a mismatched value that shares its text with a matching one', async () => {
const document = parseYamlToDocument(
outdent`
openapi: 3.1.0
paths:
/some:
get:
responses:
'200':
content:
application/json:
schema:
type:
- string
enum:
- 1
- '1'
`,
'foobar.yaml'
);

const results = await lintDocument({
externalRefResolver: new BaseResolver(),
document,
config: await createConfig({ rules: { 'no-enum-type-mismatch': 'error' } }),
});

expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
[
{
"location": [
{
"pointer": "#/paths/~1some/get/responses/200/content/application~1json/schema/enum/0",
"reportOnKey": false,
"source": "foobar.yaml",
},
],
"message": "Enum value \`1\` must be of allowed types: \`string\`.",
"reference": "https://redocly.com/docs/cli/rules/common/no-enum-type-mismatch",
"ruleId": "no-enum-type-mismatch",
"severity": "error",
"suggest": [],
},
]
`);
});

it('should point at the offending value when it is not a string', async () => {
const document = parseYamlToDocument(
outdent`
openapi: 3.1.0
paths:
/some:
get:
responses:
'200':
content:
application/json:
schema:
type:
- boolean
enum:
- zz
- 2
`,
'foobar.yaml'
);

const results = await lintDocument({
externalRefResolver: new BaseResolver(),
document,
config: await createConfig({ rules: { 'no-enum-type-mismatch': 'error' } }),
});

expect(
replaceSourceWithRef(results).map((problem) => [problem.message, problem.location[0].pointer])
).toMatchInlineSnapshot(`
[
[
"Enum value \`zz\` must be of allowed types: \`boolean\`.",
"#/paths/~1some/get/responses/200/content/application~1json/schema/enum/0",
],
[
"Enum value \`2\` must be of allowed types: \`boolean\`.",
"#/paths/~1some/get/responses/200/content/application~1json/schema/enum/1",
],
]
`);
});

it('should not crash on null schema when there is struct rule', async () => {
const document = parseYamlToDocument(
outdent`
Expand Down
27 changes: 9 additions & 18 deletions packages/core/src/rules/common/no-enum-type-mismatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,18 @@ export const NoEnumTypeMismatch:
}

if (schema.enum && schema.type && Array.isArray(schema.type)) {
const mismatchedResults: { [key: string]: string[] } = {};
for (const enumValue of schema.enum) {
mismatchedResults[enumValue] = [];
const types = schema.type as string[];

for (const type of schema.type) {
const valid = matchesJsonSchemaType(
enumValue,
type as string,
schema.nullable as boolean
);
if (!valid) mismatchedResults[enumValue].push(type);
}
for (let index = 0; index < schema.enum.length; index++) {
const enumValue = schema.enum[index];
const matchesAnyType = types.some((type) =>
matchesJsonSchemaType(enumValue, type, schema.nullable as boolean)
);
if (matchesAnyType) continue;

if (mismatchedResults[enumValue].length !== schema.type.length)
delete mismatchedResults[enumValue];
}

for (const mismatchedKey of Object.keys(mismatchedResults)) {
report({
message: `Enum value \`${mismatchedKey}\` must be of allowed types: \`${schema.type}\`.`,
location: location.child(['enum', schema.enum.indexOf(mismatchedKey)]),
message: `Enum value \`${enumValue}\` must be of allowed types: \`${schema.type}\`.`,
location: location.child(['enum', index]),
reference: 'https://redocly.com/docs/cli/rules/common/no-enum-type-mismatch',
});
}
Expand Down
Loading