Created by: fantavlik
Description of the PR
Core Changes
- Refactor InlineModelResolver.java to recursively identify inline schemas that require their own model definitions. Examples of inline schemas that were not being generated that need their own models: enums, objects with defined properties, and composed schemas. Examples of inline locations that are now being checked recursively for inline models: within object properties, within object additionalProperties, within definitions of composed schemas (similar approach to https://github.com/OpenAPITools/openapi-generator/pull/2112), within definitions of "not", and within array items.
- Add more contextual naming rather than InlineObject, schemas defined inline within an operation section will get prefixes with the operation id, schemas within an array items section will get the parent model name +
Items
suffix, etc. Schemas with "title" fields will still use the title if defined.
Go changes (affecting all Go-based generators as changes were made within AbstractGoCodegen.java)
- Prefix all enum values with model name, this is needed to avoid collisions within the package
- Fix issue where numerical enums were quoted
- Fix issue where numerical enums would begin with underscore
This is a followup from https://github.com/OpenAPITools/openapi-generator/pull/2494 - I attempted to fix this issue using Go templates at first but realized that a core change could fix these kinds of issues across all languages by refactoring the InlineModelResolver.java.
This also takes the support added by https://github.com/OpenAPITools/openapi-generator/pull/2112 and extends that implementation to be recursive (I changed the suffixes slightly to be the suffixes now match AllOf
, rather than _allOf
but otherwise the support should be the same)AllOf
, OneOf
, AnyOf
since this seems like the better convention.
Models like this which have many inline schemas that should be resolved into their own separate schemas/models were losing information (e.g. declaring enums as simple strings or declaring arrays of models arrays of simple objects).
Example:
Cat:
description: Complex model with many inline models defined within it.
type: object
properties:
myFood:
oneOf: # inline oneOf
- type: object
title: Food
properties:
ingredients:
type: array
items:
type: string
pounds:
type: integer
required:
- fooProp
myHabitat:
allOf: # inline allOf
- type: object
title: HabitatGeography
properties:
lat:
type: number
format: float
long:
type: number
format: float
continent:
type: string
enum:
- Africa
- Antarctica
- Asia
- Europe
- North America
- Oceania
- South America
required: [lat, long]
- type: object
description: Weather of the habitat defined here
properties:
rainfallInches:
type: number
averageTemperatureCelsius:
type: number
myTaxonomy:
anyOf: # inline anyOf
- description: Referenced schema
type: object
title: Species
properties:
name:
type: string
genus:
type: string
karyotype:
type: string
- description: Referenced schema
type: object
title: Order
properties:
name:
type: string
class:
type: string
cuteness: # inline enum
type: integer
enum: [1,3,5]
kittens:
type: array
items: # inline array where the items are inline models
allOf:
- $ref: '#/components/schemas/Cat'
preferences:
type: object
properties:
favoriteToy: # inline object with properties defined
type: string
additionalProperties: # inline object with additionalProperties containing inline models
title: Metadata
type: string
enum: [hidden,createdOn,createdBy,modifiedOn,modifiedBy]
Sample output before these changes (Go):
// Complex model with many inline models defined within it.
type Cat struct {
MyFood OneOfobject `json:"myFood,omitempty"`
MyHabitat map[string]interface{} `json:"myHabitat,omitempty"`
MyTaxonomy AnyOfobjectobject `json:"myTaxonomy,omitempty"`
Cuteness int32 `json:"cuteness,omitempty"`
Kittens []Cat `json:"kittens,omitempty"`
Preferences map[string]string `json:"preferences,omitempty"`
}
...
// OneOfobject does not exist - not generated
// AnyOfobjectobject does not exist - not generated
New output (Go):
// Complex model with many inline models defined within it.
type Cat struct {
MyFood CatMyFood `json:"myFood,omitempty"`
MyHabitat CatMyHabitat `json:"myHabitat,omitempty"`
MyTaxonomy CatMyTaxonomy `json:"myTaxonomy,omitempty"`
Cuteness CatCuteness `json:"cuteness,omitempty"`
Kittens []CatKittensItems `json:"kittens,omitempty"`
Preferences CatPreferences `json:"preferences,omitempty"`
}
...
type CatMyFood struct {
Ingredients []string `json:"ingredients,omitempty"`
Pounds int32 `json:"pounds,omitempty"`
}
...
type CatMyHabitat struct {
Lat float32 `json:"lat"`
Long float32 `json:"long"`
Continent CatMyHabitatAllOfContinent `json:"continent,omitempty"`
RainfallInches float32 `json:"rainfallInches,omitempty"`
AverageTemperatureCelsius float32 `json:"averageTemperatureCelsius,omitempty"`
}
...
type CatMyHabitatAllOfContinent string
// List of CatMyHabitatAllOfContinent
const (
CAT_MY_HABITAT_ALL_OF_CONTINENT_AFRICA CatMyHabitatAllOfContinent = "Africa"
CAT_MY_HABITAT_ALL_OF_CONTINENT_ANTARCTICA CatMyHabitatAllOfContinent = "Antarctica"
CAT_MY_HABITAT_ALL_OF_CONTINENT_ASIA CatMyHabitatAllOfContinent = "Asia"
CAT_MY_HABITAT_ALL_OF_CONTINENT_EUROPE CatMyHabitatAllOfContinent = "Europe"
CAT_MY_HABITAT_ALL_OF_CONTINENT_NORTH_AMERICA CatMyHabitatAllOfContinent = "North America"
CAT_MY_HABITAT_ALL_OF_CONTINENT_OCEANIA CatMyHabitatAllOfContinent = "Oceania"
CAT_MY_HABITAT_ALL_OF_CONTINENT_SOUTH_AMERICA CatMyHabitatAllOfContinent = "South America"
)
...
type CatMyTaxonomy struct {
Name string `json:"name,omitempty"`
Genus string `json:"genus,omitempty"`
Karyotype string `json:"karyotype,omitempty"`
Class string `json:"class,omitempty"`
}
...
type CatCuteness int32
// List of CatCuteness
const (
CAT_CUTENESS_1 CatCuteness = 1
CAT_CUTENESS_3 CatCuteness = 3
CAT_CUTENESS_5 CatCuteness = 5
)
...
type CatKittensItems struct {
MyFood CatMyFood `json:"myFood,omitempty"`
MyHabitat CatMyHabitat `json:"myHabitat,omitempty"`
MyTaxonomy CatMyTaxonomy `json:"myTaxonomy,omitempty"`
Cuteness CatCuteness `json:"cuteness,omitempty"`
Kittens []CatKittensItems `json:"kittens,omitempty"`
Preferences CatPreferences `json:"preferences,omitempty"`
}
...
type CatPreferences struct {
FavoriteToy string `json:"favoriteToy,omitempty"`
}
...
// Note: Go doesn't currently support complex additionalProperties, but this model is still generated:
type CatPreferencesMetadata string
// List of CatPreferencesMetadata
const (
CAT_PREFERENCES_METADATA_HIDDEN CatPreferencesMetadata = "hidden"
CAT_PREFERENCES_METADATA_CREATED_ON CatPreferencesMetadata = "createdOn"
CAT_PREFERENCES_METADATA_CREATED_BY CatPreferencesMetadata = "createdBy"
CAT_PREFERENCES_METADATA_MODIFIED_ON CatPreferencesMetadata = "modifiedOn"
CAT_PREFERENCES_METADATA_MODIFIED_BY CatPreferencesMetadata = "modifiedBy"
)
I've only regenerated samples for Go so far but pretty much every language will have new models defined that were missing before.