Skip to content

Commit

Permalink
fix(parser): parse array items only if parent definition has type
Browse files Browse the repository at this point in the history
  • Loading branch information
mrlubos committed Feb 4, 2024
1 parent 20dac04 commit c1d8979
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 77 deletions.
2 changes: 1 addition & 1 deletion bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const params = program
.option('--name <value>', 'Custom client class name')
.option('--useOptions', 'Use options instead of arguments')
.option('--useUnionTypes', 'Use union types instead of enums')
.option('--autoformat <value>', 'Process generated files with autoformatter', false)
.option('--autoformat', 'Process generated files with autoformatter', false)
.option('--exportCore <value>', 'Write core files to disk', true)
.option('--exportServices <value>', 'Write services to disk', true)
.option('--exportModels <value>', 'Write models to disk', true)
Expand Down
1 change: 0 additions & 1 deletion bin/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ describe('bin', () => {
'--output',
'./test/generated/bin',
'--autoformat',
'true',
]);
expect(result.stdout.toString()).toBe('');
expect(result.stderr.toString()).toBe('');
Expand Down
4 changes: 2 additions & 2 deletions src/client/interfaces/ModelComposition.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { Model } from './Model';

export interface ModelComposition {
type: 'one-of' | 'any-of' | 'all-of';
imports: string[];
enums: Model[];
export: 'one-of' | 'any-of' | 'all-of';
imports: string[];
properties: Model[];
}
2 changes: 1 addition & 1 deletion src/openApi/v2/parser/getModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export const getModel = (

if (definition.allOf?.length) {
const composition = getModelComposition(openApi, definition, definition.allOf, 'all-of', getModel);
model.export = composition.type;
model.export = composition.export;
model.imports.push(...composition.imports);
model.properties.push(...composition.properties);
model.enums.push(...composition.enums);
Expand Down
4 changes: 2 additions & 2 deletions src/openApi/v2/parser/getModelComposition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ export const getModelComposition = (
getModel: GetModelFn
): ModelComposition => {
const composition: ModelComposition = {
type,
imports: [],
enums: [],
export: type,
imports: [],
properties: [],
};

Expand Down
2 changes: 1 addition & 1 deletion src/openApi/v3/interfaces/OpenApiSchema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export interface OpenApiSchema extends OpenApiReference, WithEnumExtension {
required?: string[];
enum?: (string | number)[];
type?: string | string[];
const?: string | number | null;
const?: string | number | boolean | null;
allOf?: OpenApiSchema[];
oneOf?: OpenApiSchema[];
anyOf?: OpenApiSchema[];
Expand Down
66 changes: 28 additions & 38 deletions src/openApi/v3/parser/getModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { OpenApi } from '../interfaces/OpenApi';
import type { OpenApiSchema } from '../interfaces/OpenApiSchema';
import { extendEnum } from './extendEnum';
import { getEnum } from './getEnum';
import { getModelComposition } from './getModelComposition';
import { findModelComposition, getModelComposition } from './getModelComposition';
import { getModelDefault } from './getModelDefault';
import { getModelProperties } from './getModelProperties';
import { getType } from './getType';
Expand Down Expand Up @@ -83,19 +83,24 @@ export const getModel = (
model.imports.push(...arrayItems.imports);
model.default = getModelDefault(definition, model);
return model;
} else if (definition.items.anyOf && parentDefinition) {
return getModel(openApi, definition.items);
} else {
const arrayItems = getModel(openApi, definition.items);
model.export = 'array';
model.type = arrayItems.type;
model.base = arrayItems.base;
model.template = arrayItems.template;
model.link = arrayItems;
model.imports.push(...arrayItems.imports);
model.default = getModelDefault(definition, model);
return model;
}

if (definition.items.anyOf && parentDefinition && parentDefinition.type) {
const foundComposition = findModelComposition(parentDefinition);
if (foundComposition && foundComposition.definitions.some(definition => definition.type !== 'array')) {
return getModel(openApi, definition.items);
}
}

const arrayItems = getModel(openApi, definition.items);
model.export = 'array';
model.type = arrayItems.type;
model.base = arrayItems.base;
model.template = arrayItems.template;
model.link = arrayItems;
model.imports.push(...arrayItems.imports);
model.default = getModelDefault(definition, model);
return model;
}

if (
Expand Down Expand Up @@ -125,31 +130,16 @@ export const getModel = (
}
}

if (definition.oneOf?.length) {
const composition = getModelComposition(openApi, definition, definition.oneOf, 'one-of', getModel);
model.export = composition.type;
model.imports.push(...composition.imports);
model.properties.push(...composition.properties);
model.enums.push(...composition.enums);
return model;
}

if (definition.anyOf?.length) {
const composition = getModelComposition(openApi, definition, definition.anyOf, 'any-of', getModel);
model.export = composition.type;
model.imports.push(...composition.imports);
model.properties.push(...composition.properties);
model.enums.push(...composition.enums);
return model;
}

if (definition.allOf?.length) {
const composition = getModelComposition(openApi, definition, definition.allOf, 'all-of', getModel);
model.export = composition.type;
model.imports.push(...composition.imports);
model.properties.push(...composition.properties);
model.enums.push(...composition.enums);
return model;
const foundComposition = findModelComposition(definition);
if (foundComposition) {
const composition = getModelComposition({
...foundComposition,
definition,
getModel,
model,
openApi,
});
return { ...model, ...composition };
}

if (definition.type === 'object') {
Expand Down
54 changes: 43 additions & 11 deletions src/openApi/v3/parser/getModelComposition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,50 @@ import { getRequiredPropertiesFromComposition } from './getRequiredPropertiesFro
// Fix for circular dependency
export type GetModelFn = typeof getModel;

export const getModelComposition = (
openApi: OpenApi,
definition: OpenApiSchema,
definitions: OpenApiSchema[],
type: 'one-of' | 'any-of' | 'all-of',
getModel: GetModelFn
): ModelComposition => {
type Composition = {
definitions: OpenApiSchema[];
type: ModelComposition['export'];
};

export const findModelComposition = (definition: OpenApiSchema): Composition | undefined => {
const compositions: ReadonlyArray<{
definitions: Composition['definitions'] | undefined;
type: Composition['type'];
}> = [
{
definitions: definition.allOf,
type: 'all-of',
},
{
definitions: definition.anyOf,
type: 'any-of',
},
{
definitions: definition.oneOf,
type: 'one-of',
},
];
return compositions.find(composition => composition.definitions?.length) as ReturnType<typeof findModelComposition>;
};

export const getModelComposition = ({
definition,
definitions,
getModel,
model,
openApi,
type,
}: Composition & {
definition: OpenApiSchema;
getModel: GetModelFn;
model: Model;
openApi: OpenApi;
}): ModelComposition => {
const composition: ModelComposition = {
type,
imports: [],
enums: [],
properties: [],
enums: model.enums,
export: type,
imports: model.imports,
properties: model.properties,
};

const properties: Model[] = [];
Expand Down
91 changes: 71 additions & 20 deletions test/__snapshots__/index.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3758,6 +3758,7 @@ export type { ModelWithPattern } from './models/ModelWithPattern';
export type { ModelWithProperties } from './models/ModelWithProperties';
export type { ModelWithReference } from './models/ModelWithReference';
export type { ModelWithString } from './models/ModelWithString';
export type { NestedAnyOfArraysNullable } from './models/NestedAnyOfArraysNullable';
export type { Pageable } from './models/Pageable';
export type { SimpleBoolean } from './models/SimpleBoolean';
export type { SimpleFile } from './models/SimpleFile';
Expand Down Expand Up @@ -3836,6 +3837,7 @@ export { $ModelWithPattern } from './schemas/$ModelWithPattern';
export { $ModelWithProperties } from './schemas/$ModelWithProperties';
export { $ModelWithReference } from './schemas/$ModelWithReference';
export { $ModelWithString } from './schemas/$ModelWithString';
export { $NestedAnyOfArraysNullable } from './schemas/$NestedAnyOfArraysNullable';
export { $Pageable } from './schemas/$Pageable';
export { $SimpleBoolean } from './schemas/$SimpleBoolean';
export { $SimpleFile } from './schemas/$SimpleFile';
Expand Down Expand Up @@ -4201,7 +4203,7 @@ import type { ModelWithDictionary } from './ModelWithDictionary';
* This is a model with nested 'any of' property with a type null
*/
export type CompositionWithNestedAnyAndTypeNull = {
propA?: ((ModelWithDictionary | null) | (ModelWithArray | null));
propA?: (Array<(ModelWithDictionary | null)> | Array<(ModelWithArray | null)>);
};

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

"
Expand Down Expand Up @@ -5012,6 +5014,18 @@ export type ModelWithString = {
"
`;

exports[`v3 should generate: test/generated/v3/models/NestedAnyOfArraysNullable.ts 1`] = `
"/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type NestedAnyOfArraysNullable = {
nullableArray?: (Array<(string | boolean)> | null);
};

"
`;

exports[`v3 should generate: test/generated/v3/models/Pageable.ts 1`] = `
"/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
Expand Down Expand Up @@ -5549,19 +5563,25 @@ export const $CompositionWithNestedAnyAndTypeNull = {
propA: {
type: 'any-of',
contains: [{
type: 'any-of',
contains: [{
type: 'ModelWithDictionary',
}, {
type: 'null',
}],
type: 'array',
contains: {
type: 'any-of',
contains: [{
type: 'ModelWithDictionary',
}, {
type: 'null',
}],
},
}, {
type: 'any-of',
contains: [{
type: 'ModelWithArray',
}, {
type: 'null',
}],
type: 'array',
contains: {
type: 'any-of',
contains: [{
type: 'ModelWithArray',
}, {
type: 'null',
}],
},
}],
},
},
Expand All @@ -5580,12 +5600,15 @@ export const $CompositionWithNestedAnyOfAndNull = {
propA: {
type: 'any-of',
contains: [{
type: 'any-of',
contains: [{
type: 'Enum1',
}, {
type: 'ConstValue',
}],
type: 'array',
contains: {
type: 'any-of',
contains: [{
type: 'Enum1',
}, {
type: 'ConstValue',
}],
},
}, {
type: 'null',
}],
Expand Down Expand Up @@ -6585,6 +6608,34 @@ export const $ModelWithString = {
"
`;

exports[`v3 should generate: test/generated/v3/schemas/$NestedAnyOfArraysNullable.ts 1`] = `
"/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export const $NestedAnyOfArraysNullable = {
properties: {
nullableArray: {
type: 'any-of',
contains: [{
type: 'array',
contains: {
type: 'any-of',
contains: [{
type: 'string',
}, {
type: 'boolean',
}],
},
}, {
type: 'null',
}],
},
},
} as const;
"
`;

exports[`v3 should generate: test/generated/v3/schemas/$Pageable.ts 1`] = `
"/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
Expand Down
25 changes: 25 additions & 0 deletions test/spec/v3.json
Original file line number Diff line number Diff line change
Expand Up @@ -2736,6 +2736,31 @@
"const": "Some string"
}
}
},
"NestedAnyOfArraysNullable": {
"properties": {
"nullableArray": {
"anyOf": [
{
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "boolean"
}
]
},
"type": "array"
},
{
"type": "null"
}
]
}
},
"type": "object"
}
}
}
Expand Down

0 comments on commit c1d8979

Please sign in to comment.