fix: enum duplication values when schema uses a specific combination of oneOf and allOf(#2088)

This commit is contained in:
Alex Varchuk 2022-07-25 17:02:37 +03:00 committed by GitHub
parent b1afd08bcf
commit e4118479f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 189 additions and 2 deletions

View File

@ -240,7 +240,7 @@ export class OpenAPIParser {
if (enumProperty !== undefined) { if (enumProperty !== undefined) {
if (Array.isArray(enumProperty) && Array.isArray(receiver.enum)) { if (Array.isArray(enumProperty) && Array.isArray(receiver.enum)) {
receiver.enum = [...enumProperty, ...receiver.enum]; receiver.enum = Array.from(new Set([...enumProperty, ...receiver.enum]));
} else { } else {
receiver.enum = enumProperty; receiver.enum = enumProperty;
} }

View File

@ -4,7 +4,7 @@ import { outdent } from 'outdent';
import { SchemaModel } from '../../models/Schema'; import { SchemaModel } from '../../models/Schema';
import { OpenAPIParser } from '../../OpenAPIParser'; import { OpenAPIParser } from '../../OpenAPIParser';
import { RedocNormalizedOptions } from '../../RedocNormalizedOptions'; import { RedocNormalizedOptions } from '../../RedocNormalizedOptions';
import { printSchema } from './helpers'; import { enumDetailsPrinter, printSchema } from './helpers';
const opts = new RedocNormalizedOptions({}); const opts = new RedocNormalizedOptions({});
@ -283,5 +283,188 @@ describe('Models', () => {
allOf: <any>" allOf: <any>"
`); `);
}); });
describe('enum values', () => {
test('should get correct fields enum fields without duplication', () => {
const spec = parseYaml(outdent`
openapi: 3.0.0
components:
schemas:
StringField: { type: string, title: StringField, enum: [A, B, C] }
FieldA: { type: string, title: FieldA, enum: [A1, A2, A3] }
FieldB: { type: string, title: FieldB, enum: [B1, B2, B3] }
FieldC: { type: string, title: FieldC, enum: [C1, C2, C3] }
ObjectWithAllOf:
title: StringFilter
type: object
allOf:
- properties:
type: { type: string, enum: [STRING] }
field: { $ref: '#/components/schemas/StringField' }
required: [type, field, values]
- oneOf:
- properties:
field: { type: string, enum: [A] }
values: { type: array, items: { $ref: '#/components/schemas/FieldA' } }
- properties:
field: { type: string, enum: [B] }
values: { type: array, items: { $ref: '#/components/schemas/FieldB' } }
- properties:
field: { type: string, enum: [C] }
values: { type: array, items: { $ref: '#/components/schemas/FieldC' } }
ObjectWithOneOf:
title: StringFilter
type: object
properties:
type: { type: string, enum: [STRING] }
field: { $ref: '#/components/schemas/StringField' }
required: [type, field, values]
oneOf:
- properties:
field: { type: string, enum: [A] }
values: { type: array, items: { $ref: '#/components/schemas/FieldA' } }
- properties:
field: { type: string, enum: [B] }
values: { type: array, items: { $ref: '#/components/schemas/FieldB' } }
- properties:
field: { type: string, enum: [C] }
values: { type: array, items: { $ref: '#/components/schemas/FieldC' } }
`) as any;
parser = new OpenAPIParser(spec, undefined, opts);
const schemaWithOneOf = new SchemaModel(
parser,
spec.components.schemas.ObjectWithOneOf,
'#/components/schemas/ObjectWithOneOf',
opts,
);
expect(printSchema(schemaWithOneOf, enumDetailsPrinter)).toMatchInlineSnapshot(`
"oneOf
StringFilter ->
field*: <string>enum: [A,B,C]
values*: [<string>enum: [A1,A2,A3]]
type*: <string>enum: [STRING]
StringFilter ->
field*: <string>enum: [A,B,C]
values*: [<string>enum: [B1,B2,B3]]
type*: <string>enum: [STRING]
StringFilter ->
field*: <string>enum: [A,B,C]
values*: [<string>enum: [C1,C2,C3]]
type*: <string>enum: [STRING]"
`);
const schemaWithAllOf = new SchemaModel(
parser,
spec.components.schemas.ObjectWithAllOf,
'#/components/schemas/ObjectWithAllOf',
opts,
);
expect(printSchema(schemaWithAllOf, enumDetailsPrinter)).toMatchInlineSnapshot(`
"oneOf
object ->
type*: <string>enum: [STRING]
field*: <string>enum: [A,B,C]
values*: [<string>enum: [A1,A2,A3]]
object ->
type*: <string>enum: [STRING]
field*: <string>enum: [B,A,C]
values*: [<string>enum: [B1,B2,B3]]
object ->
type*: <string>enum: [STRING]
field*: <string>enum: [C,A,B]
values*: [<string>enum: [C1,C2,C3]]"
`);
});
test('should get correct fields enum limits', () => {
const spec = parseYaml(outdent`
openapi: 3.0.0
components:
schemas:
StringField: { type: string, title: StringField, enum: [A, B, C] }
FieldA: { type: string, title: FieldA, enum: [A1, A2, A3] }
FieldB: { type: string, title: FieldB, enum: [B1, B2, B3] }
FieldC: { type: string, title: FieldC, enum: [C1, C2, C3] }
ObjectWithAllOf:
title: StringFilter
type: object
allOf:
- properties:
type: { type: string, enum: [STRING] }
required: [type, field, values]
- oneOf:
- properties:
field: { type: string, enum: [A] }
values: { type: array, items: { $ref: '#/components/schemas/FieldA' } }
- properties:
field: { type: string, enum: [B] }
values: { type: array, items: { $ref: '#/components/schemas/FieldB' } }
- properties:
field: { type: string, enum: [C] }
values: { type: array, items: { $ref: '#/components/schemas/FieldC' } }
ObjectWithOneOf:
title: StringFilter
type: object
properties:
type: { type: string, enum: [STRING] }
required: [type, field, values]
oneOf:
- properties:
field: { type: string, enum: [A] }
values: { type: array, items: { $ref: '#/components/schemas/FieldA' } }
- properties:
field: { type: string, enum: [B] }
values: { type: array, items: { $ref: '#/components/schemas/FieldB' } }
- properties:
field: { type: string, enum: [C] }
values: { type: array, items: { $ref: '#/components/schemas/FieldC' } }
`) as any;
parser = new OpenAPIParser(spec, undefined, opts);
const schemaWithOneOf = new SchemaModel(
parser,
spec.components.schemas.ObjectWithOneOf,
'#/components/schemas/ObjectWithOneOf',
opts,
);
expect(printSchema(schemaWithOneOf, enumDetailsPrinter)).toMatchInlineSnapshot(`
"oneOf
StringFilter ->
field*: <string>enum: [A]
values*: [<string>enum: [A1,A2,A3]]
type*: <string>enum: [STRING]
StringFilter ->
field*: <string>enum: [B]
values*: [<string>enum: [B1,B2,B3]]
type*: <string>enum: [STRING]
StringFilter ->
field*: <string>enum: [C]
values*: [<string>enum: [C1,C2,C3]]
type*: <string>enum: [STRING]"
`);
const schemaWithAllOf = new SchemaModel(
parser,
spec.components.schemas.ObjectWithAllOf,
'#/components/schemas/ObjectWithAllOf',
opts,
);
expect(printSchema(schemaWithAllOf, enumDetailsPrinter)).toMatchInlineSnapshot(`
"oneOf
object ->
type*: <string>enum: [STRING]
field*: <string>enum: [A]
values*: [<string>enum: [A1,A2,A3]]
object ->
type*: <string>enum: [STRING]
field*: <string>enum: [B]
values*: [<string>enum: [B1,B2,B3]]
object ->
type*: <string>enum: [STRING]
field*: <string>enum: [C]
values*: [<string>enum: [C1,C2,C3]]"
`);
});
});
}); });
}); });

View File

@ -12,6 +12,10 @@ export function circularDetailsPrinter(schema: SchemaModel): string {
return schema.isCircular ? ' !circular' : ''; return schema.isCircular ? ' !circular' : '';
} }
export function enumDetailsPrinter(schema: SchemaModel): string {
return schema.enum ? `enum: [${schema.enum.toString()}]` : '';
}
export function printSchema( export function printSchema(
schema: SchemaModel, schema: SchemaModel,
detailsPrinter: (schema: SchemaModel) => string = () => '', detailsPrinter: (schema: SchemaModel) => string = () => '',