springfox: Duplicated enumerations in swagger definitions
Version 2.6.1
Enums are currently being listed inline in the swagger output, causing enums to be generated as inner class. They should be referenced via "$ref"
like classes are. Attached are two swagger json definitions, one provided by springfox and the other fixed by hand.
This is problematic for clients that attempt to use the model files, as they cannot be reused across api endpoints, as each generated model will contain a copy of the enum. This is especially problematic in apis that define enums with large sets of values, such as an enum set of the 249 ISO 3166 alpha 2 or 3 letter country codes.
Example enum:
public enum Crest { ONE, TWO, THREE, }
Example class:
public class Obj {
private Crest crest;
public Crest getCrest() {
return crest;
}
public void setCrest(Crest crest) {
this.crest = crest;
}
}
Incorrect swagger:
"definitions": {
"Obj": {
"type": "object",
"properties": {
"crest": {
"type": "string",
"enum": [
"ONE",
"TWO",
"THREE"
]
}
}
},
}
Resulting codegen:
// ... snip ...
public class Obj {
/**
* Gets or Sets crest
*/
public enum CrestEnum {
ONE("ONE"),
TWO("TWO"),
THREE("THREE");
private String value;
CrestEnum(String value) {
this.value = value;
}
}
// ... snip ...
}
Correct swagger:
"definitions": {
"Obj": {
"type": "object",
"properties": {
"crest": {
"$ref": "#/definitions/Crest"
}
}
},
"Crest": {
"type": "string",
"enum": [
"ONE",
"TWO",
"THREE"
]
},
}
Resulting codegen:
// ... snip ...
public enum Crest {
ONE("ONE"),
TWO("TWO"),
THREE("THREE");
private String value;
Crest(String value) {
this.value = value;
}
// ... snip ...
}
// ... snip ...
public class Obj {
@JsonProperty("crest")
private Crest crest = null;
// ... snip ...
public Crest getCrest() {
return crest;
}
public void setCrest(Crest crest) {
this.crest = crest;
}
// ... snip ...
}
About this issue
- Original URL
- State: open
- Created 7 years ago
- Reactions: 16
- Comments: 34 (16 by maintainers)
Commits related to this issue
- feat(enum-ref): open class for extension so it would be possible to implement enum-refs by subclassing (so in case of enum, it would be possible to resolve type name the same way as for classes, inste... — committed to atsu85/springfox by deleted user 7 years ago
- feat(enum-ref): open class for extension so it would be possible to implement enum-refs by subclassing (in case of type having field with enum type, it would be possible to add enums of fields to depe... — committed to atsu85/springfox by deleted user 7 years ago
- feat(enum-ref): open class for extension so it would be possible to implement enum-refs by subclassing (so in subclass it would be possible to add enum type definitions to swagger definitions, like wi... — committed to atsu85/springfox by deleted user 7 years ago
- feat(enum-ref): open class for extension so it would be possible to implement enum-refs by subclassing (so in subclass it would be possible to change enum type definitions in generated swagger.json to... — committed to atsu85/springfox by deleted user 7 years ago
- test(enum-ref): test that classes are open for extending (so enum references could be implemented instead of inline enum types) #1738 — committed to atsu85/springfox by deleted user 7 years ago
Similar use case for us: we now have a number of places where we need to cast generated TS enums, to a different instance of that same Java enum, because they’ve been generated using this class-specific naming. Would love to hear if this change is thought to be manageable
This one is a toughie 😳 Its not easy to isolate enumerations easily the way things have been structured. I’ll take a look at how I can tease out this feature incrementally so that its possible
@toddbyrne I see where you are coming from. However I contend that it is not in scope of this library.
In the above example
Obj1
andObj2
are pretty much equivalent. Just that one hascrest
which is anystring
that you provide (Obj2) and the other is astring
with a fixed set of valuesONE
,TWO
andTHREE
(Obj1). Semantically, no different that one having maxlength and one not having any size limit.string
.Hi, has there been any progress on this? This effects us generating TypeScript code where we can’t use namespaces (React Native), meaning that in order to prevent name clashes in the reexport we need to prepend the class name to each enum declaration.
If you think this is now doable then I’m happy to have a look, but there is quite a lot to learn, so I want to make sure that it’s achievable before I commit the time.
Is there any updates after 3.0 released half a year?
Any updates on this now that 3.0.0 is out?
I am also struggling with this issue in a commercial production setting after reverse generating model classes from Swagger. The duplicate enums does cause unnecessary code in client systems.
Also clojure appears to be a moot point because no model files are generated regardless.
@dilipkrish How is the codegen supposed to de-duplicate enums? Having spring fox regenerate the same enum multiple times is incorrect. If you have three objects that reference the same enum then spring fox generates three enums.
@Sineaggi Hey this is perfectly valid swagger. The swagger-codegen is incorrect in its rendering of the enum.
I’ve just dropped
io.springfox
in favour oforg.springdoc
. Lack of enum refs is unacceptable in my case. I’ve just replacedwith
additionally i’ve tweaked some settings in my
application.yml
, added bean to describe my API and I finally got enums as refs!!!If some of your enums are still inlined (it was in my case) then add following to you Application class:
source: https://github.com/springdoc/springdoc-openapi/issues/232#issuecomment-748607672
@Knut-Fjellheim understandable. I should perhaps be easier to fix once we support oas 3.x
@dilipkrish, oh, now i remember one restriction that i found out much later after implementing enum references - enum references are valid in types (for example http request or response body has enum field), but not in method parameters - so You are right that Your example api-doc (that references enums from http GET method parameter) isn’t passing validation, but that doesn’t mean that springfox shouldn’t support enum references in all other cases where enum references are allowed.
Just to clarify for future posters. The reason this is difficult
That definitions of enums as a reference is not valid. It will be inlined.
Hi, @dilipkrish. Haven’t heard Your reply - I hope You are still live and kicking 😉
As a first step forward to allow implementing enum references (as proposed by this issue), i created a PR that makes 4 classes extendable - this was enough for me to implement enum references in my own project. It contains really trivial and unharmful changes with tests - it would be fantastic, if that PR could be accepted. Then i wouldn’t have to use custom build of this library with the patches included in the PR.
As a next step I’d like to contribute enum references implementation back to this project - but i guess creating/accepting that PR could take much more time. I’d appreciate Your thoughts on it.
@dilipkrish, i’ve implemented what @Sineaggi proposed - instead of inline enum declarations, referencing them using ref. For now i patched some classes locally (for now, to make them extendable, so i could replace some beans that make it possible to create enum refs - to avoid delays in my work related to the PR i’d like to make). I’d like to contribute it back, but i have a question. I think i shouldn’t just change the schema generation, but allow the developer to decide if it should be generated using enum references. For that purpose i could add if statements to the code to check if enum references are desired. I guess i could introduce new spring configuration property (smth like
springfox.schema.enumrefs=true
) to allow generating enum references. But first of all i suspect You could come up with better name and secondly, i’m not sure if i should inject it to all classes that should use it, or should i create a configuration properties class (didn’t find one so far) and inject that instead.@dilipkrish, I’d appreciate if You could add Your thoughts before i start working on the PR. PS. The classes involved with this change are DefaultModelDependencyProvider, DefaultModelProvider, TypeNameExtractor and ModelMapper.