Skip to content

Commit f6e425b

Browse files
committed
feat: Improve support for allOf, oneOf, and anyOf
1 parent 53136ad commit f6e425b

File tree

10 files changed

+358
-92
lines changed

10 files changed

+358
-92
lines changed

src/client/interfaces/Model.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { Schema } from './Schema';
33

44
export interface Model extends Schema {
55
name: string;
6-
export: 'reference' | 'generic' | 'enum' | 'array' | 'dictionary' | 'interface';
6+
export: 'reference' | 'generic' | 'enum' | 'array' | 'dictionary' | 'interface' | 'one-of' | 'any-of' | 'all-of';
77
type: string;
88
base: string;
99
template: string | null;

src/openApi/v3/parser/getModel.ts

Lines changed: 26 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -126,74 +126,42 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, isDefiniti
126126
return model;
127127
}
128128
}
129-
130129
// TODO:
131-
// Add correct support for oneOf, anyOf, allOf
130+
// Add correct support for oneOf
132131
// https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/
133132

134-
if (definition.anyOf && definition.anyOf.length && !definition.properties) {
135-
model.export = 'generic';
136-
const compositionTypes = definition.anyOf.filter(type => type.$ref).map(type => getType(type.$ref));
137-
const composition = compositionTypes
138-
.map(type => type.type)
139-
.sort()
140-
.join(' | ');
141-
model.imports.push(...compositionTypes.map(type => type.base));
142-
model.type = composition;
143-
model.base = composition;
144-
return model;
145-
}
146-
147-
if (definition.oneOf && definition.oneOf.length && !definition.properties) {
148-
model.export = 'generic';
149-
const compositionTypes = definition.oneOf.filter(type => type.$ref).map(type => getType(type.$ref));
150-
const composition = compositionTypes
151-
.map(type => type.type)
152-
.sort()
153-
.join(' | ');
154-
model.imports.push(...compositionTypes.map(type => type.base));
155-
model.type = composition;
156-
model.base = composition;
133+
if (definition.oneOf?.length || definition.anyOf?.length || definition.allOf?.length) {
134+
let types: OpenApiSchema[] = [];
135+
if (definition.oneOf?.length) {
136+
model.export = 'one-of';
137+
types = definition.oneOf;
138+
} else if (definition.anyOf?.length) {
139+
model.export = 'any-of';
140+
types = definition.anyOf;
141+
} else if (definition.allOf?.length) {
142+
model.export = 'all-of';
143+
types = definition.allOf;
144+
}
145+
const compositionTypes = types.map(model => getModel(openApi, model));
146+
model.properties = compositionTypes;
147+
model.imports.push(...compositionTypes.reduce((acc: string[], type) => acc.concat(type.imports), []));
148+
model.enums.push(...compositionTypes.reduce((acc: Model[], type) => acc.concat(type.enums), []));
157149
return model;
158150
}
159151

160-
if (definition.type === 'object' || definition.allOf) {
152+
if (definition.type === 'object') {
161153
model.export = 'interface';
162154
model.type = PrimaryType.OBJECT;
163155
model.base = PrimaryType.OBJECT;
164156
model.default = getModelDefault(definition, model);
165-
166-
if (definition.allOf && definition.allOf.length) {
167-
definition.allOf.forEach(parent => {
168-
if (parent.$ref) {
169-
const parentRef = getType(parent.$ref);
170-
model.extends.push(parentRef.base);
171-
model.imports.push(parentRef.base);
172-
}
173-
if (parent.type === 'object' && parent.properties) {
174-
const properties = getModelProperties(openApi, parent, getModel);
175-
properties.forEach(property => {
176-
model.properties.push(property);
177-
model.imports.push(...property.imports);
178-
if (property.export === 'enum') {
179-
model.enums.push(property);
180-
}
181-
});
182-
}
183-
});
184-
}
185-
186-
if (definition.properties) {
187-
const properties = getModelProperties(openApi, definition, getModel);
188-
properties.forEach(property => {
189-
model.properties.push(property);
190-
model.imports.push(...property.imports);
191-
if (property.export === 'enum') {
192-
model.enums.push(property);
193-
}
194-
});
195-
}
196-
157+
const properties = getModelProperties(openApi, definition, getModel);
158+
properties.forEach(property => {
159+
model.properties.push(property);
160+
model.imports.push(...property.imports);
161+
if (property.export === 'enum') {
162+
model.enums.push(property);
163+
}
164+
});
197165
return model;
198166
}
199167

src/templates/partials/schema.hbs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
{{>schemaArray}}
77
{{else equals export 'dictionary'}}
88
{{>schemaDictionary}}
9+
{{else equals export 'any-of'}}
10+
{{>schemaComposition}}
11+
{{else equals export 'all-of'}}
12+
{{>schemaComposition}}
13+
{{else equals export 'one-of'}}
14+
{{>schemaComposition}}
915
{{else}}
1016
{{>schemaGeneric}}
1117
{{/equals}}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
type: '{{export}}',
3+
4+
contains: [{{#each properties}}{{>schema}}, {{/each}}]
5+
{{#if isReadOnly}}
6+
isReadOnly: {{{isReadOnly}}},
7+
{{/if}}
8+
{{#if isRequired}}
9+
isRequired: {{{isRequired}}},
10+
{{/if}}
11+
{{#if isNullable}}
12+
isNullable: {{{isNullable}}},
13+
{{/if}}
14+
}

src/templates/partials/type.hbs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
{{>typeArray}}
99
{{else equals export 'dictionary'}}
1010
{{>typeDictionary}}
11+
{{else equals export 'one-of'}}
12+
{{>typeUnion}}
13+
{{else equals export 'any-of'}}
14+
{{>typeUnion}}
15+
{{else equals export 'all-of'}}
16+
{{>typeIntersection}}
1117
{{else}}
1218
{{>typeGeneric}}
1319
{{/equals}}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
({{#each properties}} & {{>type}}{{/each}}){{>isNullable}}

src/templates/partials/typeUnion.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
({{#each properties}} | {{>type}}{{/each}}){{>isNullable}}

src/utils/registerHandlebarTemplates.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import partialParameters from '../templates/partials/parameters.hbs';
5050
import partialResult from '../templates/partials/result.hbs';
5151
import partialSchema from '../templates/partials/schema.hbs';
5252
import partialSchemaArray from '../templates/partials/schemaArray.hbs';
53+
import partialSchemaComposition from '../templates/partials/schemaComposition.hbs';
5354
import partialSchemaDictionary from '../templates/partials/schemaDictionary.hbs';
5455
import partialSchemaEnum from '../templates/partials/schemaEnum.hbs';
5556
import partialSchemaGeneric from '../templates/partials/schemaGeneric.hbs';
@@ -60,7 +61,9 @@ import partialTypeDictionary from '../templates/partials/typeDictionary.hbs';
6061
import partialTypeEnum from '../templates/partials/typeEnum.hbs';
6162
import partialTypeGeneric from '../templates/partials/typeGeneric.hbs';
6263
import partialTypeInterface from '../templates/partials/typeInterface.hbs';
64+
import partialTypeIntersection from '../templates/partials/typeIntersection.hbs';
6365
import partialTypeReference from '../templates/partials/typeReference.hbs';
66+
import partialTypeUnion from '../templates/partials/typeUnion.hbs';
6467
import { registerHandlebarHelpers } from './registerHandlebarHelpers';
6568

6669
export interface Templates {
@@ -120,13 +123,16 @@ export function registerHandlebarTemplates(): Templates {
120123
Handlebars.registerPartial('schemaEnum', Handlebars.template(partialSchemaEnum));
121124
Handlebars.registerPartial('schemaGeneric', Handlebars.template(partialSchemaGeneric));
122125
Handlebars.registerPartial('schemaInterface', Handlebars.template(partialSchemaInterface));
126+
Handlebars.registerPartial('schemaComposition', Handlebars.template(partialSchemaComposition));
123127
Handlebars.registerPartial('type', Handlebars.template(partialType));
124128
Handlebars.registerPartial('typeArray', Handlebars.template(partialTypeArray));
125129
Handlebars.registerPartial('typeDictionary', Handlebars.template(partialTypeDictionary));
126130
Handlebars.registerPartial('typeEnum', Handlebars.template(partialTypeEnum));
127131
Handlebars.registerPartial('typeGeneric', Handlebars.template(partialTypeGeneric));
128132
Handlebars.registerPartial('typeInterface', Handlebars.template(partialTypeInterface));
129133
Handlebars.registerPartial('typeReference', Handlebars.template(partialTypeReference));
134+
Handlebars.registerPartial('typeUnion', Handlebars.template(partialTypeUnion));
135+
Handlebars.registerPartial('typeIntersection', Handlebars.template(partialTypeIntersection));
130136
Handlebars.registerPartial('base', Handlebars.template(partialBase));
131137

132138
// Generic functions used in 'request' file @see src/templates/core/request.hbs for more info

0 commit comments

Comments
 (0)