diff --git a/src/openApi/v3/parser/getModel.ts b/src/openApi/v3/parser/getModel.ts index 0393cc999..8c79d8397 100644 --- a/src/openApi/v3/parser/getModel.ts +++ b/src/openApi/v3/parser/getModel.ts @@ -6,7 +6,7 @@ import { extendEnum } from './extendEnum'; import { getEnum } from './getEnum'; import { findModelComposition, getModelComposition } from './getModelComposition'; import { getModelDefault } from './getModelDefault'; -import { getModelProperties } from './getModelProperties'; +import { getAdditionalPropertiesModel, getModelProperties } from './getModelProperties'; import { getType } from './getType'; export const getModel = ( @@ -103,33 +103,6 @@ export const getModel = ( return model; } - if ( - definition.type === 'object' && - (typeof definition.additionalProperties === 'object' || definition.additionalProperties === true) - ) { - const ap = typeof definition.additionalProperties === 'object' ? definition.additionalProperties : {}; - if (ap.$ref) { - const additionalProperties = getType(ap.$ref); - model.export = 'dictionary'; - model.type = additionalProperties.type; - model.base = additionalProperties.base; - model.template = additionalProperties.template; - model.imports.push(...additionalProperties.imports); - model.default = getModelDefault(definition, model); - return model; - } else { - const additionalProperties = getModel(openApi, ap); - model.export = 'dictionary'; - model.type = additionalProperties.type; - model.base = additionalProperties.base; - model.template = additionalProperties.template; - model.link = additionalProperties; - model.imports.push(...additionalProperties.imports); - model.default = getModelDefault(definition, model); - return model; - } - } - const foundComposition = findModelComposition(definition); if (foundComposition) { const composition = getModelComposition({ @@ -158,18 +131,16 @@ export const getModel = ( model.enums.push(modelProperty); } }); - return model; - } else { - const additionalProperties = getModel(openApi, {}); - model.export = 'dictionary'; - model.type = additionalProperties.type; - model.base = additionalProperties.base; - model.template = additionalProperties.template; - model.link = additionalProperties; - model.imports.push(...additionalProperties.imports); - model.default = getModelDefault(definition, model); + + if (definition.additionalProperties === true) { + const modelProperty = getAdditionalPropertiesModel(openApi, definition, getModel, model); + model.properties.push(modelProperty); + } + return model; } + + return getAdditionalPropertiesModel(openApi, definition, getModel, model); } if (definition.const !== undefined) { diff --git a/src/openApi/v3/parser/getModelProperties.ts b/src/openApi/v3/parser/getModelProperties.ts index 6e25ca833..e054bf9ed 100644 --- a/src/openApi/v3/parser/getModelProperties.ts +++ b/src/openApi/v3/parser/getModelProperties.ts @@ -5,11 +5,50 @@ import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; import { escapeName } from './escapeName'; import type { getModel } from './getModel'; +import { getModelDefault } from './getModelDefault'; import { getType } from './getType'; // Fix for circular dependency export type GetModelFn = typeof getModel; +export const getAdditionalPropertiesModel = ( + openApi: OpenApi, + definition: OpenApiSchema, + getModel: GetModelFn, + model: Model +): Model => { + const ap = typeof definition.additionalProperties === 'object' ? definition.additionalProperties : {}; + const apModel = getModel(openApi, ap); + + if (definition.additionalProperties === true && definition.properties) { + apModel.default = getModelDefault(definition, model); + apModel.export = 'generic'; + apModel.isRequired = true; + apModel.name = '[key: string]'; + return apModel; + } + + if (ap.$ref) { + const apType = getType(ap.$ref); + model.base = apType.base; + model.default = getModelDefault(definition, model); + model.export = 'dictionary'; + model.imports.push(...apType.imports); + model.template = apType.template; + model.type = apType.type; + return model; + } + + model.base = apModel.base; + model.default = getModelDefault(definition, model); + model.export = 'dictionary'; + model.imports.push(...apModel.imports); + model.link = apModel; + model.template = apModel.template; + model.type = apModel.type; + return model; +}; + export const getModelProperties = ( openApi: OpenApi, definition: OpenApiSchema, diff --git a/test/__snapshots__/index.spec.ts.snap b/test/__snapshots__/index.spec.ts.snap index 10e585282..a2b043972 100644 --- a/test/__snapshots__/index.spec.ts.snap +++ b/test/__snapshots__/index.spec.ts.snap @@ -3740,6 +3740,7 @@ export type { ModelCircle } from './models/ModelCircle'; export type { ModelSquare } from './models/ModelSquare'; export type { ModelThatExtends } from './models/ModelThatExtends'; export type { ModelThatExtendsExtends } from './models/ModelThatExtendsExtends'; +export type { ModelWithAdditionalPropertiesEqTrue } from './models/ModelWithAdditionalPropertiesEqTrue'; export type { ModelWithArray } from './models/ModelWithArray'; export type { ModelWithBoolean } from './models/ModelWithBoolean'; export type { ModelWithCircularReference } from './models/ModelWithCircularReference'; @@ -3819,6 +3820,7 @@ export { $ModelCircle } from './schemas/$ModelCircle'; export { $ModelSquare } from './schemas/$ModelSquare'; export { $ModelThatExtends } from './schemas/$ModelThatExtends'; export { $ModelThatExtendsExtends } from './schemas/$ModelThatExtendsExtends'; +export { $ModelWithAdditionalPropertiesEqTrue } from './schemas/$ModelWithAdditionalPropertiesEqTrue'; export { $ModelWithArray } from './schemas/$ModelWithArray'; export { $ModelWithBoolean } from './schemas/$ModelWithBoolean'; export { $ModelWithCircularReference } from './schemas/$ModelWithCircularReference'; @@ -4650,6 +4652,25 @@ export type ModelThatExtendsExtends = (ModelWithString & ModelThatExtends & { " `; +exports[`v3 should generate: test/generated/v3/models/ModelWithAdditionalPropertiesEqTrue.ts 1`] = ` +"/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * This is a model with one property and additionalProperties: true + */ +export type ModelWithAdditionalPropertiesEqTrue = { + /** + * This is a simple string property + */ + prop?: string; + [key: string]: any; +}; + +" +`; + exports[`v3 should generate: test/generated/v3/models/ModelWithArray.ts 1`] = ` "/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ @@ -6143,6 +6164,27 @@ export const $ModelThatExtendsExtends = { " `; +exports[`v3 should generate: test/generated/v3/schemas/$ModelWithAdditionalPropertiesEqTrue.ts 1`] = ` +"/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $ModelWithAdditionalPropertiesEqTrue = { + description: \`This is a model with one property and additionalProperties: true\`, + properties: { + prop: { + type: 'string', + description: \`This is a simple string property\`, + }, + [key: string]: { + type: 'any', + isRequired: true, + }, + }, +} as const; +" +`; + exports[`v3 should generate: test/generated/v3/schemas/$ModelWithArray.ts 1`] = ` "/* generated using openapi-typescript-codegen -- do no edit */ /* istanbul ignore file */ diff --git a/test/spec/v3.json b/test/spec/v3.json index 70376f1ab..f1bb32e34 100644 --- a/test/spec/v3.json +++ b/test/spec/v3.json @@ -2737,6 +2737,17 @@ } } }, + "ModelWithAdditionalPropertiesEqTrue": { + "description": "This is a model with one property and additionalProperties: true", + "type": "object", + "properties": { + "prop": { + "description": "This is a simple string property", + "type": "string" + } + }, + "additionalProperties": true + }, "NestedAnyOfArraysNullable": { "properties": { "nullableArray": {