Skip to content

Commit 7d82d77

Browse files
authored
Merge pull request ferdikoomen#419 from budde377/feat-resolve-anonymous-types
feat: Improve support for allOf, oneOf, and anyOf
2 parents 2d8aa16 + 7615367 commit 7d82d77

File tree

10 files changed

+553
-178
lines changed

10 files changed

+553
-178
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: 29 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, isDefiniti
103103
}
104104
}
105105

106-
if (definition.type === 'object' && typeof definition.additionalProperties === 'object') {
106+
if (definition.type === 'object' && definition.additionalProperties && typeof definition.additionalProperties === 'object') {
107107
if (definition.additionalProperties.$ref) {
108108
const additionalProperties = getType(definition.additionalProperties.$ref);
109109
model.export = 'dictionary';
@@ -125,70 +125,42 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, isDefiniti
125125
return model;
126126
}
127127
}
128+
// TODO:
129+
// Add correct support for oneOf
130+
// https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/
128131

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

155-
if (definition.type === 'object' || definition.allOf) {
151+
if (definition.type === 'object') {
156152
model.export = 'interface';
157153
model.type = 'any';
158154
model.base = 'any';
159155
model.default = getModelDefault(definition, model);
160-
161-
if (definition.allOf?.length) {
162-
definition.allOf.forEach(parent => {
163-
if (parent.$ref) {
164-
const parentRef = getType(parent.$ref);
165-
model.extends.push(parentRef.base);
166-
model.imports.push(parentRef.base);
167-
}
168-
if (parent.type === 'object' && parent.properties) {
169-
const properties = getModelProperties(openApi, parent, getModel);
170-
properties.forEach(property => {
171-
model.properties.push(property);
172-
model.imports.push(...property.imports);
173-
if (property.export === 'enum') {
174-
model.enums.push(property);
175-
}
176-
});
177-
}
178-
});
179-
}
180-
181-
if (definition.properties) {
182-
const properties = getModelProperties(openApi, definition, getModel);
183-
properties.forEach(property => {
184-
model.properties.push(property);
185-
model.imports.push(...property.imports);
186-
if (property.export === 'enum') {
187-
model.enums.push(property);
188-
}
189-
});
190-
}
191-
156+
const properties = getModelProperties(openApi, definition, getModel);
157+
properties.forEach(property => {
158+
model.properties.push(property);
159+
model.imports.push(...property.imports);
160+
if (property.export === 'enum') {
161+
model.enums.push(property);
162+
}
163+
});
192164
return model;
193165
}
194166

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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
type: '{{export}}',
3+
contains: [{{#each properties}}{{>schema}}{{#unless @last}}, {{/unless}}{{/each}}],
4+
{{#if isReadOnly}}
5+
isReadOnly: {{{isReadOnly}}},
6+
{{/if}}
7+
{{#if isRequired}}
8+
isRequired: {{{isRequired}}},
9+
{{/if}}
10+
{{#if isNullable}}
11+
isNullable: {{{isNullable}}},
12+
{{/if}}
13+
}

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}}{{#unless @last}} & {{/unless}}{{/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}}{{#unless @last}} | {{/unless}}{{/each}}){{>isNullable}}

src/utils/registerHandlebarTemplates.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import partialParameters from '../templates/partials/parameters.hbs';
5151
import partialResult from '../templates/partials/result.hbs';
5252
import partialSchema from '../templates/partials/schema.hbs';
5353
import partialSchemaArray from '../templates/partials/schemaArray.hbs';
54+
import partialSchemaComposition from '../templates/partials/schemaComposition.hbs';
5455
import partialSchemaDictionary from '../templates/partials/schemaDictionary.hbs';
5556
import partialSchemaEnum from '../templates/partials/schemaEnum.hbs';
5657
import partialSchemaGeneric from '../templates/partials/schemaGeneric.hbs';
@@ -61,7 +62,9 @@ import partialTypeDictionary from '../templates/partials/typeDictionary.hbs';
6162
import partialTypeEnum from '../templates/partials/typeEnum.hbs';
6263
import partialTypeGeneric from '../templates/partials/typeGeneric.hbs';
6364
import partialTypeInterface from '../templates/partials/typeInterface.hbs';
65+
import partialTypeIntersection from '../templates/partials/typeIntersection.hbs';
6466
import partialTypeReference from '../templates/partials/typeReference.hbs';
67+
import partialTypeUnion from '../templates/partials/typeUnion.hbs';
6568
import { registerHandlebarHelpers } from './registerHandlebarHelpers';
6669

6770
export interface Templates {
@@ -121,13 +124,16 @@ export function registerHandlebarTemplates(): Templates {
121124
Handlebars.registerPartial('schemaEnum', Handlebars.template(partialSchemaEnum));
122125
Handlebars.registerPartial('schemaGeneric', Handlebars.template(partialSchemaGeneric));
123126
Handlebars.registerPartial('schemaInterface', Handlebars.template(partialSchemaInterface));
127+
Handlebars.registerPartial('schemaComposition', Handlebars.template(partialSchemaComposition));
124128
Handlebars.registerPartial('type', Handlebars.template(partialType));
125129
Handlebars.registerPartial('typeArray', Handlebars.template(partialTypeArray));
126130
Handlebars.registerPartial('typeDictionary', Handlebars.template(partialTypeDictionary));
127131
Handlebars.registerPartial('typeEnum', Handlebars.template(partialTypeEnum));
128132
Handlebars.registerPartial('typeGeneric', Handlebars.template(partialTypeGeneric));
129133
Handlebars.registerPartial('typeInterface', Handlebars.template(partialTypeInterface));
130134
Handlebars.registerPartial('typeReference', Handlebars.template(partialTypeReference));
135+
Handlebars.registerPartial('typeUnion', Handlebars.template(partialTypeUnion));
136+
Handlebars.registerPartial('typeIntersection', Handlebars.template(partialTypeIntersection));
131137
Handlebars.registerPartial('base', Handlebars.template(partialBase));
132138

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

0 commit comments

Comments
 (0)