Skip to content

Commit

Permalink
fix: enum duplication values when schema uses a specific combination …
Browse files Browse the repository at this point in the history
…of oneOf and allOf(#2088)
  • Loading branch information
AlexVarchuk committed Jul 25, 2022
1 parent b1afd08 commit e411847
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 2 deletions.
2 changes: 1 addition & 1 deletion src/services/OpenAPIParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ export class OpenAPIParser {

if (enumProperty !== undefined) {
if (Array.isArray(enumProperty) && Array.isArray(receiver.enum)) {
receiver.enum = [...enumProperty, ...receiver.enum];
receiver.enum = Array.from(new Set([...enumProperty, ...receiver.enum]));
} else {
receiver.enum = enumProperty;
}
Expand Down
185 changes: 184 additions & 1 deletion src/services/__tests__/models/Schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { outdent } from 'outdent';
import { SchemaModel } from '../../models/Schema';
import { OpenAPIParser } from '../../OpenAPIParser';
import { RedocNormalizedOptions } from '../../RedocNormalizedOptions';
import { printSchema } from './helpers';
import { enumDetailsPrinter, printSchema } from './helpers';

const opts = new RedocNormalizedOptions({});

Expand Down Expand Up @@ -283,5 +283,188 @@ describe('Models', () => {
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]]"
`);
});
});
});
});
4 changes: 4 additions & 0 deletions src/services/__tests__/models/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export function circularDetailsPrinter(schema: SchemaModel): string {
return schema.isCircular ? ' !circular' : '';
}

export function enumDetailsPrinter(schema: SchemaModel): string {
return schema.enum ? `enum: [${schema.enum.toString()}]` : '';
}

export function printSchema(
schema: SchemaModel,
detailsPrinter: (schema: SchemaModel) => string = () => '',
Expand Down

0 comments on commit e411847

Please sign in to comment.