Skip to content

Commit 3363a18

Browse files
authored
Merge simple alternatives (#3165)
1 parent ad1dc0b commit 3363a18

File tree

3 files changed

+170
-1
lines changed

3 files changed

+170
-1
lines changed

.changeset/kind-lobsters-grab.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@gitbook/react-openapi": patch
3+
---
4+
5+
Merge simple alternatives

packages/react-openapi/src/OpenAPISchema.test.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,86 @@ describe('getSchemaAlternatives', () => {
3535
]);
3636
});
3737

38+
it('merges string enum', () => {
39+
expect(
40+
getSchemaAlternatives({
41+
oneOf: [
42+
{
43+
oneOf: [
44+
{
45+
type: 'string',
46+
enum: ['a', 'b'],
47+
},
48+
{
49+
type: 'string',
50+
enum: ['c', 'd'],
51+
nullable: true,
52+
},
53+
],
54+
},
55+
],
56+
})
57+
).toEqual([
58+
{
59+
type: 'string',
60+
enum: ['a', 'b', 'c', 'd'],
61+
nullable: true,
62+
},
63+
]);
64+
});
65+
66+
it('merges objects with allOf', () => {
67+
expect(
68+
getSchemaAlternatives({
69+
allOf: [
70+
{
71+
type: 'object',
72+
properties: {
73+
name: {
74+
type: 'string',
75+
},
76+
map: {
77+
type: 'string',
78+
},
79+
description: {
80+
type: 'string',
81+
},
82+
},
83+
required: ['name'],
84+
},
85+
{
86+
type: 'object',
87+
properties: {
88+
externalId: {
89+
type: 'string',
90+
},
91+
},
92+
required: ['map', 'externalId'],
93+
},
94+
],
95+
})
96+
).toEqual([
97+
{
98+
type: 'object',
99+
properties: {
100+
name: {
101+
type: 'string',
102+
},
103+
map: {
104+
type: 'string',
105+
},
106+
description: {
107+
type: 'string',
108+
},
109+
externalId: {
110+
type: 'string',
111+
},
112+
},
113+
required: ['name', 'map', 'externalId'],
114+
},
115+
]);
116+
});
117+
38118
it('should not flatten oneOf and allOf', () => {
39119
expect(
40120
getSchemaAlternatives({

packages/react-openapi/src/OpenAPISchema.tsx

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,91 @@ export function getSchemaAlternatives(
450450
}
451451

452452
const [type, schemas] = alternatives;
453-
return flattenAlternatives(type, schemas, new Set(ancestors).add(schema));
453+
return mergeAlternatives(
454+
type,
455+
flattenAlternatives(type, schemas, new Set(ancestors).add(schema))
456+
);
457+
}
458+
459+
/**
460+
* Merge alternatives of the same type into a single schema.
461+
* - Merge string enums
462+
*/
463+
function mergeAlternatives(
464+
alternativeType: AlternativeType,
465+
schemasOrRefs: OpenAPIV3.SchemaObject[]
466+
): OpenAPIV3.SchemaObject[] | null {
467+
switch (alternativeType) {
468+
case 'oneOf': {
469+
return schemasOrRefs.reduce<OpenAPIV3.SchemaObject[]>((acc, schemaOrRef) => {
470+
const latest = acc.at(-1);
471+
472+
if (
473+
latest &&
474+
latest.type === 'string' &&
475+
latest.enum &&
476+
schemaOrRef.type === 'string' &&
477+
schemaOrRef.enum
478+
) {
479+
latest.enum = Array.from(new Set([...latest.enum, ...schemaOrRef.enum]));
480+
latest.nullable = latest.nullable || schemaOrRef.nullable;
481+
return acc;
482+
}
483+
484+
acc.push(schemaOrRef);
485+
return acc;
486+
}, []);
487+
}
488+
case 'allOf': {
489+
return schemasOrRefs.reduce<OpenAPIV3.SchemaObject[]>((acc, schemaOrRef) => {
490+
const latest = acc.at(-1);
491+
492+
if (
493+
latest &&
494+
latest.type === 'string' &&
495+
latest.enum &&
496+
schemaOrRef.type === 'string' &&
497+
schemaOrRef.enum
498+
) {
499+
const keys = Object.keys(schemaOrRef);
500+
if (keys.every((key) => ['type', 'enum', 'nullable'].includes(key))) {
501+
latest.enum = Array.from(new Set([...latest.enum, ...schemaOrRef.enum]));
502+
latest.nullable = latest.nullable || schemaOrRef.nullable;
503+
return acc;
504+
}
505+
}
506+
507+
if (latest && latest.type === 'object' && schemaOrRef.type === 'object') {
508+
const keys = Object.keys(schemaOrRef);
509+
if (
510+
keys.every((key) =>
511+
['type', 'properties', 'required', 'nullable'].includes(key)
512+
)
513+
) {
514+
latest.properties = {
515+
...latest.properties,
516+
...schemaOrRef.properties,
517+
};
518+
latest.required = Array.from(
519+
new Set([
520+
...(Array.isArray(latest.required) ? latest.required : []),
521+
...(Array.isArray(schemaOrRef.required)
522+
? schemaOrRef.required
523+
: []),
524+
])
525+
);
526+
latest.nullable = latest.nullable || schemaOrRef.nullable;
527+
return acc;
528+
}
529+
}
530+
531+
acc.push(schemaOrRef);
532+
return acc;
533+
}, []);
534+
}
535+
default:
536+
return schemasOrRefs;
537+
}
454538
}
455539

456540
function flattenAlternatives(

0 commit comments

Comments
 (0)