Skip to content

Commit 22eef6c

Browse files
authored
Merge pull request ferdikoomen#11 from nicolas-chaulet/fix/any-of-arrays
fix(parser): parse array items only if parent has no other item type
2 parents 20dac04 + c1d8979 commit 22eef6c

File tree

10 files changed

+174
-77
lines changed

10 files changed

+174
-77
lines changed

bin/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const params = program
1616
.option('--name <value>', 'Custom client class name')
1717
.option('--useOptions', 'Use options instead of arguments')
1818
.option('--useUnionTypes', 'Use union types instead of enums')
19-
.option('--autoformat <value>', 'Process generated files with autoformatter', false)
19+
.option('--autoformat', 'Process generated files with autoformatter', false)
2020
.option('--exportCore <value>', 'Write core files to disk', true)
2121
.option('--exportServices <value>', 'Write services to disk', true)
2222
.option('--exportModels <value>', 'Write models to disk', true)

bin/index.spec.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ describe('bin', () => {
6767
'--output',
6868
'./test/generated/bin',
6969
'--autoformat',
70-
'true',
7170
]);
7271
expect(result.stdout.toString()).toBe('');
7372
expect(result.stderr.toString()).toBe('');
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type { Model } from './Model';
22

33
export interface ModelComposition {
4-
type: 'one-of' | 'any-of' | 'all-of';
5-
imports: string[];
64
enums: Model[];
5+
export: 'one-of' | 'any-of' | 'all-of';
6+
imports: string[];
77
properties: Model[];
88
}

src/openApi/v2/parser/getModel.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export const getModel = (
112112

113113
if (definition.allOf?.length) {
114114
const composition = getModelComposition(openApi, definition, definition.allOf, 'all-of', getModel);
115-
model.export = composition.type;
115+
model.export = composition.export;
116116
model.imports.push(...composition.imports);
117117
model.properties.push(...composition.properties);
118118
model.enums.push(...composition.enums);

src/openApi/v2/parser/getModelComposition.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ export const getModelComposition = (
1717
getModel: GetModelFn
1818
): ModelComposition => {
1919
const composition: ModelComposition = {
20-
type,
21-
imports: [],
2220
enums: [],
21+
export: type,
22+
imports: [],
2323
properties: [],
2424
};
2525

src/openApi/v3/interfaces/OpenApiSchema.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export interface OpenApiSchema extends OpenApiReference, WithEnumExtension {
2626
required?: string[];
2727
enum?: (string | number)[];
2828
type?: string | string[];
29-
const?: string | number | null;
29+
const?: string | number | boolean | null;
3030
allOf?: OpenApiSchema[];
3131
oneOf?: OpenApiSchema[];
3232
anyOf?: OpenApiSchema[];

src/openApi/v3/parser/getModel.ts

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { OpenApi } from '../interfaces/OpenApi';
44
import type { OpenApiSchema } from '../interfaces/OpenApiSchema';
55
import { extendEnum } from './extendEnum';
66
import { getEnum } from './getEnum';
7-
import { getModelComposition } from './getModelComposition';
7+
import { findModelComposition, getModelComposition } from './getModelComposition';
88
import { getModelDefault } from './getModelDefault';
99
import { getModelProperties } from './getModelProperties';
1010
import { getType } from './getType';
@@ -83,19 +83,24 @@ export const getModel = (
8383
model.imports.push(...arrayItems.imports);
8484
model.default = getModelDefault(definition, model);
8585
return model;
86-
} else if (definition.items.anyOf && parentDefinition) {
87-
return getModel(openApi, definition.items);
88-
} else {
89-
const arrayItems = getModel(openApi, definition.items);
90-
model.export = 'array';
91-
model.type = arrayItems.type;
92-
model.base = arrayItems.base;
93-
model.template = arrayItems.template;
94-
model.link = arrayItems;
95-
model.imports.push(...arrayItems.imports);
96-
model.default = getModelDefault(definition, model);
97-
return model;
9886
}
87+
88+
if (definition.items.anyOf && parentDefinition && parentDefinition.type) {
89+
const foundComposition = findModelComposition(parentDefinition);
90+
if (foundComposition && foundComposition.definitions.some(definition => definition.type !== 'array')) {
91+
return getModel(openApi, definition.items);
92+
}
93+
}
94+
95+
const arrayItems = getModel(openApi, definition.items);
96+
model.export = 'array';
97+
model.type = arrayItems.type;
98+
model.base = arrayItems.base;
99+
model.template = arrayItems.template;
100+
model.link = arrayItems;
101+
model.imports.push(...arrayItems.imports);
102+
model.default = getModelDefault(definition, model);
103+
return model;
99104
}
100105

101106
if (
@@ -125,31 +130,16 @@ export const getModel = (
125130
}
126131
}
127132

128-
if (definition.oneOf?.length) {
129-
const composition = getModelComposition(openApi, definition, definition.oneOf, 'one-of', getModel);
130-
model.export = composition.type;
131-
model.imports.push(...composition.imports);
132-
model.properties.push(...composition.properties);
133-
model.enums.push(...composition.enums);
134-
return model;
135-
}
136-
137-
if (definition.anyOf?.length) {
138-
const composition = getModelComposition(openApi, definition, definition.anyOf, 'any-of', getModel);
139-
model.export = composition.type;
140-
model.imports.push(...composition.imports);
141-
model.properties.push(...composition.properties);
142-
model.enums.push(...composition.enums);
143-
return model;
144-
}
145-
146-
if (definition.allOf?.length) {
147-
const composition = getModelComposition(openApi, definition, definition.allOf, 'all-of', getModel);
148-
model.export = composition.type;
149-
model.imports.push(...composition.imports);
150-
model.properties.push(...composition.properties);
151-
model.enums.push(...composition.enums);
152-
return model;
133+
const foundComposition = findModelComposition(definition);
134+
if (foundComposition) {
135+
const composition = getModelComposition({
136+
...foundComposition,
137+
definition,
138+
getModel,
139+
model,
140+
openApi,
141+
});
142+
return { ...model, ...composition };
153143
}
154144

155145
if (definition.type === 'object') {

src/openApi/v3/parser/getModelComposition.ts

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,50 @@ import { getRequiredPropertiesFromComposition } from './getRequiredPropertiesFro
99
// Fix for circular dependency
1010
export type GetModelFn = typeof getModel;
1111

12-
export const getModelComposition = (
13-
openApi: OpenApi,
14-
definition: OpenApiSchema,
15-
definitions: OpenApiSchema[],
16-
type: 'one-of' | 'any-of' | 'all-of',
17-
getModel: GetModelFn
18-
): ModelComposition => {
12+
type Composition = {
13+
definitions: OpenApiSchema[];
14+
type: ModelComposition['export'];
15+
};
16+
17+
export const findModelComposition = (definition: OpenApiSchema): Composition | undefined => {
18+
const compositions: ReadonlyArray<{
19+
definitions: Composition['definitions'] | undefined;
20+
type: Composition['type'];
21+
}> = [
22+
{
23+
definitions: definition.allOf,
24+
type: 'all-of',
25+
},
26+
{
27+
definitions: definition.anyOf,
28+
type: 'any-of',
29+
},
30+
{
31+
definitions: definition.oneOf,
32+
type: 'one-of',
33+
},
34+
];
35+
return compositions.find(composition => composition.definitions?.length) as ReturnType<typeof findModelComposition>;
36+
};
37+
38+
export const getModelComposition = ({
39+
definition,
40+
definitions,
41+
getModel,
42+
model,
43+
openApi,
44+
type,
45+
}: Composition & {
46+
definition: OpenApiSchema;
47+
getModel: GetModelFn;
48+
model: Model;
49+
openApi: OpenApi;
50+
}): ModelComposition => {
1951
const composition: ModelComposition = {
20-
type,
21-
imports: [],
22-
enums: [],
23-
properties: [],
52+
enums: model.enums,
53+
export: type,
54+
imports: model.imports,
55+
properties: model.properties,
2456
};
2557

2658
const properties: Model[] = [];

test/__snapshots__/index.spec.ts.snap

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3758,6 +3758,7 @@ export type { ModelWithPattern } from './models/ModelWithPattern';
37583758
export type { ModelWithProperties } from './models/ModelWithProperties';
37593759
export type { ModelWithReference } from './models/ModelWithReference';
37603760
export type { ModelWithString } from './models/ModelWithString';
3761+
export type { NestedAnyOfArraysNullable } from './models/NestedAnyOfArraysNullable';
37613762
export type { Pageable } from './models/Pageable';
37623763
export type { SimpleBoolean } from './models/SimpleBoolean';
37633764
export type { SimpleFile } from './models/SimpleFile';
@@ -3836,6 +3837,7 @@ export { $ModelWithPattern } from './schemas/$ModelWithPattern';
38363837
export { $ModelWithProperties } from './schemas/$ModelWithProperties';
38373838
export { $ModelWithReference } from './schemas/$ModelWithReference';
38383839
export { $ModelWithString } from './schemas/$ModelWithString';
3840+
export { $NestedAnyOfArraysNullable } from './schemas/$NestedAnyOfArraysNullable';
38393841
export { $Pageable } from './schemas/$Pageable';
38403842
export { $SimpleBoolean } from './schemas/$SimpleBoolean';
38413843
export { $SimpleFile } from './schemas/$SimpleFile';
@@ -4201,7 +4203,7 @@ import type { ModelWithDictionary } from './ModelWithDictionary';
42014203
* This is a model with nested 'any of' property with a type null
42024204
*/
42034205
export type CompositionWithNestedAnyAndTypeNull = {
4204-
propA?: ((ModelWithDictionary | null) | (ModelWithArray | null));
4206+
propA?: (Array<(ModelWithDictionary | null)> | Array<(ModelWithArray | null)>);
42054207
};
42064208

42074209
"
@@ -4218,7 +4220,7 @@ import type { Enum1 } from './Enum1';
42184220
* This is a model with one property with a 'any of' relationship where the options are not $ref
42194221
*/
42204222
export type CompositionWithNestedAnyOfAndNull = {
4221-
propA?: ((Enum1 | ConstValue) | null);
4223+
propA?: (Array<(Enum1 | ConstValue)> | null);
42224224
};
42234225

42244226
"
@@ -5012,6 +5014,18 @@ export type ModelWithString = {
50125014
"
50135015
`;
50145016

5017+
exports[`v3 should generate: test/generated/v3/models/NestedAnyOfArraysNullable.ts 1`] = `
5018+
"/* generated using openapi-typescript-codegen -- do no edit */
5019+
/* istanbul ignore file */
5020+
/* tslint:disable */
5021+
/* eslint-disable */
5022+
export type NestedAnyOfArraysNullable = {
5023+
nullableArray?: (Array<(string | boolean)> | null);
5024+
};
5025+
5026+
"
5027+
`;
5028+
50155029
exports[`v3 should generate: test/generated/v3/models/Pageable.ts 1`] = `
50165030
"/* generated using openapi-typescript-codegen -- do no edit */
50175031
/* istanbul ignore file */
@@ -5549,19 +5563,25 @@ export const $CompositionWithNestedAnyAndTypeNull = {
55495563
propA: {
55505564
type: 'any-of',
55515565
contains: [{
5552-
type: 'any-of',
5553-
contains: [{
5554-
type: 'ModelWithDictionary',
5555-
}, {
5556-
type: 'null',
5557-
}],
5566+
type: 'array',
5567+
contains: {
5568+
type: 'any-of',
5569+
contains: [{
5570+
type: 'ModelWithDictionary',
5571+
}, {
5572+
type: 'null',
5573+
}],
5574+
},
55585575
}, {
5559-
type: 'any-of',
5560-
contains: [{
5561-
type: 'ModelWithArray',
5562-
}, {
5563-
type: 'null',
5564-
}],
5576+
type: 'array',
5577+
contains: {
5578+
type: 'any-of',
5579+
contains: [{
5580+
type: 'ModelWithArray',
5581+
}, {
5582+
type: 'null',
5583+
}],
5584+
},
55655585
}],
55665586
},
55675587
},
@@ -5580,12 +5600,15 @@ export const $CompositionWithNestedAnyOfAndNull = {
55805600
propA: {
55815601
type: 'any-of',
55825602
contains: [{
5583-
type: 'any-of',
5584-
contains: [{
5585-
type: 'Enum1',
5586-
}, {
5587-
type: 'ConstValue',
5588-
}],
5603+
type: 'array',
5604+
contains: {
5605+
type: 'any-of',
5606+
contains: [{
5607+
type: 'Enum1',
5608+
}, {
5609+
type: 'ConstValue',
5610+
}],
5611+
},
55895612
}, {
55905613
type: 'null',
55915614
}],
@@ -6585,6 +6608,34 @@ export const $ModelWithString = {
65856608
"
65866609
`;
65876610

6611+
exports[`v3 should generate: test/generated/v3/schemas/$NestedAnyOfArraysNullable.ts 1`] = `
6612+
"/* generated using openapi-typescript-codegen -- do no edit */
6613+
/* istanbul ignore file */
6614+
/* tslint:disable */
6615+
/* eslint-disable */
6616+
export const $NestedAnyOfArraysNullable = {
6617+
properties: {
6618+
nullableArray: {
6619+
type: 'any-of',
6620+
contains: [{
6621+
type: 'array',
6622+
contains: {
6623+
type: 'any-of',
6624+
contains: [{
6625+
type: 'string',
6626+
}, {
6627+
type: 'boolean',
6628+
}],
6629+
},
6630+
}, {
6631+
type: 'null',
6632+
}],
6633+
},
6634+
},
6635+
} as const;
6636+
"
6637+
`;
6638+
65886639
exports[`v3 should generate: test/generated/v3/schemas/$Pageable.ts 1`] = `
65896640
"/* generated using openapi-typescript-codegen -- do no edit */
65906641
/* istanbul ignore file */

test/spec/v3.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2736,6 +2736,31 @@
27362736
"const": "Some string"
27372737
}
27382738
}
2739+
},
2740+
"NestedAnyOfArraysNullable": {
2741+
"properties": {
2742+
"nullableArray": {
2743+
"anyOf": [
2744+
{
2745+
"items": {
2746+
"anyOf": [
2747+
{
2748+
"type": "string"
2749+
},
2750+
{
2751+
"type": "boolean"
2752+
}
2753+
]
2754+
},
2755+
"type": "array"
2756+
},
2757+
{
2758+
"type": "null"
2759+
}
2760+
]
2761+
}
2762+
},
2763+
"type": "object"
27392764
}
27402765
}
27412766
}

0 commit comments

Comments
 (0)