From fa60cd0d5603b118ae5a262aa4be559cb2b26c54 Mon Sep 17 00:00:00 2001 From: Christopher Pruijsen Date: Mon, 26 Oct 2026 08:27:11 +0000 Subject: [PATCH 1/2] fix(java): declare the inline discriminator enum in oneOf interfaces A oneOf interface whose discriminator is an inline enum declared the getter but not the enum, while each implementor declared its own copy. The interface did not compile. The interface now declares the enum and the implementors reuse it. Fixes #25398 --- .../languages/AbstractJavaCodegen.java | 21 +++++++++++ .../resources/Java/oneof_interface.mustache | 7 ++++ .../src/main/resources/Java/pojo_doc.mustache | 6 ++-- .../JavaJaxRS/spec/oneof_interface.mustache | 25 +++++++++++++ .../JavaSpring/oneof_interface.mustache | 7 ++++ .../libraries/mp/oneof_interface.mustache | 7 ++++ .../client/libraries/mp/pojo_doc.mustache | 6 ++-- .../libraries/se/oneof_interface.mustache | 7 ++++ .../client/libraries/se/pojo_doc.mustache | 6 ++-- .../server/libraries/mp/pojo_doc.mustache | 6 ++-- .../common/doc/pojo_doc.mustache | 6 ++-- .../common/model/oneof_interface.mustache | 7 ++++ .../jaxrs/JavaJAXRSSpecServerCodegenTest.java | 15 ++++++++ .../java/spring/SpringCodegenTest.java | 11 ++++++ .../src/test/resources/bugs/issue_25398.yaml | 35 +++++++++++++++++++ 15 files changed, 157 insertions(+), 15 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/bugs/issue_25398.yaml diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java index b68ebf839729..40df487cdd90 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java @@ -832,6 +832,27 @@ public Map postProcessAllModels(Map objs) } } + // A oneOf interface declares the inline enum of its own discriminator (oneof_interface.mustache) and the + // implementors inherit it. A property in an implementor's allVars is its own and keeps its enum. + if (useOneOfInterfaces && !(this instanceof GroovyClientCodegen)) { + for (CodegenModel cm : allModels.values()) { + for (CodegenProperty disc : cm.vars) { + boolean fromImplementors = cm.interfaceModels != null && cm.interfaceModels.stream() + .flatMap(m -> m.allVars.stream()) + .anyMatch(v -> disc.baseName.equals(v.baseName)); + if (cm.oneOf.isEmpty() || !disc.isDiscriminator || !disc.isEnum) continue; + if (fromImplementors) { + disc.isEnum = false; + continue; + } + cm.oneOf.stream().map(allModels::get).filter(Objects::nonNull) + .flatMap(impl -> impl.vars.stream()) + .filter(v -> disc.baseName.equals(v.baseName)) + .forEach(v -> v.isEnum = false); + } + } + } + if (isGenerateConstructorWithAllArgs()) { // conditionally force the generation of all args constructor. for (CodegenModel cm : allModels.values()) { diff --git a/modules/openapi-generator/src/main/resources/Java/oneof_interface.mustache b/modules/openapi-generator/src/main/resources/Java/oneof_interface.mustache index 71888430c647..b5a030e2676b 100644 --- a/modules/openapi-generator/src/main/resources/Java/oneof_interface.mustache +++ b/modules/openapi-generator/src/main/resources/Java/oneof_interface.mustache @@ -3,6 +3,13 @@ {{{.}}} {{/vendorExtensions.x-class-extra-annotation}} public {{>sealed}}interface {{classname}} {{#vendorExtensions.x-implements}}{{#-first}}extends {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{#-last}} {{/-last}}{{/vendorExtensions.x-implements}}{{>permits}}{ + {{#vars}} + {{#isEnum}} + {{#isDiscriminator}} +{{>modelInnerEnum}} + {{/isDiscriminator}} + {{/isEnum}} + {{/vars}} {{#discriminator}} public {{propertyType}} {{propertyGetter}}(); {{/discriminator}} diff --git a/modules/openapi-generator/src/main/resources/Java/pojo_doc.mustache b/modules/openapi-generator/src/main/resources/Java/pojo_doc.mustache index bae0bc48cdd2..a846f94573ca 100644 --- a/modules/openapi-generator/src/main/resources/Java/pojo_doc.mustache +++ b/modules/openapi-generator/src/main/resources/Java/pojo_doc.mustache @@ -8,9 +8,9 @@ | Name | Type | Description | Notes | |------------ | ------------- | ------------- | -------------| -{{#vars}}|**{{name}}** | {{#isEnum}}[**{{datatypeWithEnum}}**](#{{datatypeWithEnum}}){{/isEnum}}{{^isEnum}}{{#isContainer}}{{#isArray}}{{#items}}{{#isModel}}[{{/isModel}}{{/items}}**{{baseType}}{{#items}}<{{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isArray}}{{#isMap}}{{#items}}{{#isModel}}[{{/isModel}}**Map<String, {{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isMap}}{{/isContainer}}{{^isContainer}}{{#isModel}}[{{/isModel}}**{{dataType}}**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/isContainer}}{{/isEnum}} | {{description}} | {{^required}} [optional]{{/required}}{{#isReadOnly}} [readonly]{{/isReadOnly}} | +{{#vars}}|**{{name}}** | {{#isInnerEnum}}[**{{datatypeWithEnum}}**](#{{datatypeWithEnum}}){{/isInnerEnum}}{{^isInnerEnum}}{{#isContainer}}{{#isArray}}{{#items}}{{#isModel}}[{{/isModel}}{{/items}}**{{baseType}}{{#items}}<{{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isArray}}{{#isMap}}{{#items}}{{#isModel}}[{{/isModel}}**Map<String, {{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isMap}}{{/isContainer}}{{^isContainer}}{{#isModel}}[{{/isModel}}**{{dataType}}**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/isContainer}}{{/isInnerEnum}} | {{description}} | {{^required}} [optional]{{/required}}{{#isReadOnly}} [readonly]{{/isReadOnly}} | {{/vars}} -{{#vars}}{{#isEnum}} +{{#vars}}{{#isInnerEnum}} ## Enum: {{datatypeWithEnum}} @@ -18,7 +18,7 @@ | Name | Value | |---- | -----|{{#allowableValues}}{{#enumVars}} | {{name}} | {{value}} |{{/enumVars}}{{/allowableValues}} -{{/isEnum}}{{/vars}} +{{/isInnerEnum}}{{/vars}} {{#vendorExtensions.x-implements.0}} ## Implemented Interfaces diff --git a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/oneof_interface.mustache b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/oneof_interface.mustache index 34d601b12c5f..108d5ec854e9 100644 --- a/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/oneof_interface.mustache +++ b/modules/openapi-generator/src/main/resources/JavaJaxRS/spec/oneof_interface.mustache @@ -1,3 +1,21 @@ +{{#vars}} +{{#isEnum}} +{{#isDiscriminator}} +{{#jackson}} +import com.fasterxml.jackson.annotation.*; +{{/jackson}} +{{#jsonb}} +import {{javaxPackage}}.json.bind.annotation.*; +import {{javaxPackage}}.json.bind.serializer.*; +import {{javaxPackage}}.json.stream.*; +{{/jsonb}} +{{#withXml}} +import {{javaxPackage}}.xml.bind.annotation.*; +{{/withXml}} +{{/isDiscriminator}} +{{/isEnum}} +{{/vars}} + {{>additionalOneOfTypeAnnotations}} {{#discriminator}} {{>typeInfoAnnotation}} @@ -15,6 +33,13 @@ {{>generatedAnnotation}} public {{>sealed}}interface {{classname}}{{#vendorExtensions.x-implements}}{{#-first}} extends {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} {{>permits}}{ + {{#vars}} + {{#isEnum}} + {{#isDiscriminator}} +{{>enumClass}} + {{/isDiscriminator}} + {{/isEnum}} + {{/vars}} {{#discriminator}} {{propertyType}} {{propertyGetter}}(); {{/discriminator}} diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/oneof_interface.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/oneof_interface.mustache index 68cb9b85910c..8df13f595d96 100644 --- a/modules/openapi-generator/src/main/resources/JavaSpring/oneof_interface.mustache +++ b/modules/openapi-generator/src/main/resources/JavaSpring/oneof_interface.mustache @@ -21,6 +21,13 @@ {{>generatedAnnotation}} public {{>sealed}}interface {{classname}}{{#vendorExtensions.x-implements}}{{#-first}} extends {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} {{>permits}}{ + {{#vars}} + {{#isEnum}} + {{#isDiscriminator}} +{{>enumClass}} + {{/isDiscriminator}} + {{/isEnum}} + {{/vars}} {{#discriminator}} public {{propertyType}} {{propertyGetter}}(); {{/discriminator}} diff --git a/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/mp/oneof_interface.mustache b/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/mp/oneof_interface.mustache index d6727727460d..da030963405b 100644 --- a/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/mp/oneof_interface.mustache +++ b/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/mp/oneof_interface.mustache @@ -1,5 +1,12 @@ {{>additionalOneOfTypeAnnotations}}{{>generatedAnnotation}}{{>typeInfoAnnotation}}{{>xmlAnnotation}} public interface {{classname}} {{#vendorExtensions.x-implements}}{{#-first}}extends {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} { + {{#vars}} + {{#isEnum}} + {{#isDiscriminator}} +{{>enumClass}} + {{/isDiscriminator}} + {{/isEnum}} + {{/vars}} {{#discriminator}} public {{propertyType}} {{propertyGetter}}(); {{/discriminator}} diff --git a/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/mp/pojo_doc.mustache b/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/mp/pojo_doc.mustache index bae0bc48cdd2..a846f94573ca 100644 --- a/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/mp/pojo_doc.mustache +++ b/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/mp/pojo_doc.mustache @@ -8,9 +8,9 @@ | Name | Type | Description | Notes | |------------ | ------------- | ------------- | -------------| -{{#vars}}|**{{name}}** | {{#isEnum}}[**{{datatypeWithEnum}}**](#{{datatypeWithEnum}}){{/isEnum}}{{^isEnum}}{{#isContainer}}{{#isArray}}{{#items}}{{#isModel}}[{{/isModel}}{{/items}}**{{baseType}}{{#items}}<{{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isArray}}{{#isMap}}{{#items}}{{#isModel}}[{{/isModel}}**Map<String, {{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isMap}}{{/isContainer}}{{^isContainer}}{{#isModel}}[{{/isModel}}**{{dataType}}**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/isContainer}}{{/isEnum}} | {{description}} | {{^required}} [optional]{{/required}}{{#isReadOnly}} [readonly]{{/isReadOnly}} | +{{#vars}}|**{{name}}** | {{#isInnerEnum}}[**{{datatypeWithEnum}}**](#{{datatypeWithEnum}}){{/isInnerEnum}}{{^isInnerEnum}}{{#isContainer}}{{#isArray}}{{#items}}{{#isModel}}[{{/isModel}}{{/items}}**{{baseType}}{{#items}}<{{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isArray}}{{#isMap}}{{#items}}{{#isModel}}[{{/isModel}}**Map<String, {{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isMap}}{{/isContainer}}{{^isContainer}}{{#isModel}}[{{/isModel}}**{{dataType}}**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/isContainer}}{{/isInnerEnum}} | {{description}} | {{^required}} [optional]{{/required}}{{#isReadOnly}} [readonly]{{/isReadOnly}} | {{/vars}} -{{#vars}}{{#isEnum}} +{{#vars}}{{#isInnerEnum}} ## Enum: {{datatypeWithEnum}} @@ -18,7 +18,7 @@ | Name | Value | |---- | -----|{{#allowableValues}}{{#enumVars}} | {{name}} | {{value}} |{{/enumVars}}{{/allowableValues}} -{{/isEnum}}{{/vars}} +{{/isInnerEnum}}{{/vars}} {{#vendorExtensions.x-implements.0}} ## Implemented Interfaces diff --git a/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/se/oneof_interface.mustache b/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/se/oneof_interface.mustache index d6727727460d..da030963405b 100644 --- a/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/se/oneof_interface.mustache +++ b/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/se/oneof_interface.mustache @@ -1,5 +1,12 @@ {{>additionalOneOfTypeAnnotations}}{{>generatedAnnotation}}{{>typeInfoAnnotation}}{{>xmlAnnotation}} public interface {{classname}} {{#vendorExtensions.x-implements}}{{#-first}}extends {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} { + {{#vars}} + {{#isEnum}} + {{#isDiscriminator}} +{{>enumClass}} + {{/isDiscriminator}} + {{/isEnum}} + {{/vars}} {{#discriminator}} public {{propertyType}} {{propertyGetter}}(); {{/discriminator}} diff --git a/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/se/pojo_doc.mustache b/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/se/pojo_doc.mustache index bae0bc48cdd2..a846f94573ca 100644 --- a/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/se/pojo_doc.mustache +++ b/modules/openapi-generator/src/main/resources/java-helidon/client/libraries/se/pojo_doc.mustache @@ -8,9 +8,9 @@ | Name | Type | Description | Notes | |------------ | ------------- | ------------- | -------------| -{{#vars}}|**{{name}}** | {{#isEnum}}[**{{datatypeWithEnum}}**](#{{datatypeWithEnum}}){{/isEnum}}{{^isEnum}}{{#isContainer}}{{#isArray}}{{#items}}{{#isModel}}[{{/isModel}}{{/items}}**{{baseType}}{{#items}}<{{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isArray}}{{#isMap}}{{#items}}{{#isModel}}[{{/isModel}}**Map<String, {{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isMap}}{{/isContainer}}{{^isContainer}}{{#isModel}}[{{/isModel}}**{{dataType}}**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/isContainer}}{{/isEnum}} | {{description}} | {{^required}} [optional]{{/required}}{{#isReadOnly}} [readonly]{{/isReadOnly}} | +{{#vars}}|**{{name}}** | {{#isInnerEnum}}[**{{datatypeWithEnum}}**](#{{datatypeWithEnum}}){{/isInnerEnum}}{{^isInnerEnum}}{{#isContainer}}{{#isArray}}{{#items}}{{#isModel}}[{{/isModel}}{{/items}}**{{baseType}}{{#items}}<{{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isArray}}{{#isMap}}{{#items}}{{#isModel}}[{{/isModel}}**Map<String, {{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isMap}}{{/isContainer}}{{^isContainer}}{{#isModel}}[{{/isModel}}**{{dataType}}**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/isContainer}}{{/isInnerEnum}} | {{description}} | {{^required}} [optional]{{/required}}{{#isReadOnly}} [readonly]{{/isReadOnly}} | {{/vars}} -{{#vars}}{{#isEnum}} +{{#vars}}{{#isInnerEnum}} ## Enum: {{datatypeWithEnum}} @@ -18,7 +18,7 @@ | Name | Value | |---- | -----|{{#allowableValues}}{{#enumVars}} | {{name}} | {{value}} |{{/enumVars}}{{/allowableValues}} -{{/isEnum}}{{/vars}} +{{/isInnerEnum}}{{/vars}} {{#vendorExtensions.x-implements.0}} ## Implemented Interfaces diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/pojo_doc.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/pojo_doc.mustache index bae0bc48cdd2..a846f94573ca 100644 --- a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/pojo_doc.mustache +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/pojo_doc.mustache @@ -8,9 +8,9 @@ | Name | Type | Description | Notes | |------------ | ------------- | ------------- | -------------| -{{#vars}}|**{{name}}** | {{#isEnum}}[**{{datatypeWithEnum}}**](#{{datatypeWithEnum}}){{/isEnum}}{{^isEnum}}{{#isContainer}}{{#isArray}}{{#items}}{{#isModel}}[{{/isModel}}{{/items}}**{{baseType}}{{#items}}<{{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isArray}}{{#isMap}}{{#items}}{{#isModel}}[{{/isModel}}**Map<String, {{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isMap}}{{/isContainer}}{{^isContainer}}{{#isModel}}[{{/isModel}}**{{dataType}}**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/isContainer}}{{/isEnum}} | {{description}} | {{^required}} [optional]{{/required}}{{#isReadOnly}} [readonly]{{/isReadOnly}} | +{{#vars}}|**{{name}}** | {{#isInnerEnum}}[**{{datatypeWithEnum}}**](#{{datatypeWithEnum}}){{/isInnerEnum}}{{^isInnerEnum}}{{#isContainer}}{{#isArray}}{{#items}}{{#isModel}}[{{/isModel}}{{/items}}**{{baseType}}{{#items}}<{{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isArray}}{{#isMap}}{{#items}}{{#isModel}}[{{/isModel}}**Map<String, {{dataType}}>**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/items}}{{/isMap}}{{/isContainer}}{{^isContainer}}{{#isModel}}[{{/isModel}}**{{dataType}}**{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{baseType}}.md){{/isModel}}{{/isContainer}}{{/isInnerEnum}} | {{description}} | {{^required}} [optional]{{/required}}{{#isReadOnly}} [readonly]{{/isReadOnly}} | {{/vars}} -{{#vars}}{{#isEnum}} +{{#vars}}{{#isInnerEnum}} ## Enum: {{datatypeWithEnum}} @@ -18,7 +18,7 @@ | Name | Value | |---- | -----|{{#allowableValues}}{{#enumVars}} | {{name}} | {{value}} |{{/enumVars}}{{/allowableValues}} -{{/isEnum}}{{/vars}} +{{/isInnerEnum}}{{/vars}} {{#vendorExtensions.x-implements.0}} ## Implemented Interfaces diff --git a/modules/openapi-generator/src/main/resources/java-micronaut/common/doc/pojo_doc.mustache b/modules/openapi-generator/src/main/resources/java-micronaut/common/doc/pojo_doc.mustache index 05d13ee4b59a..ccb109d0480f 100644 --- a/modules/openapi-generator/src/main/resources/java-micronaut/common/doc/pojo_doc.mustache +++ b/modules/openapi-generator/src/main/resources/java-micronaut/common/doc/pojo_doc.mustache @@ -11,11 +11,11 @@ The class is defined in **[{{classname}}.java](../../{{{modelFolder}}}/{{classna Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -{{#vars}}**{{name}}** | {{#isEnum}}[**{{datatypeWithEnum}}**](#{{datatypeWithEnum}}){{/isEnum}}{{^isEnum}}{{#isContainer}}{{#isArray}}{{#items}}{{#isModel}}[{{/isModel}}{{/items}}`{{baseType}}{{#items}}<{{dataType}}>`{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{#baseType}}{{baseType}}{{/baseType}}.md){{/isModel}}{{/items}}{{/isArray}}{{#isMap}}{{#items}}{{#isModel}}[{{/isModel}}`Map<String, {{dataType}}>`{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{#baseType}}{{baseType}}{{/baseType}}.md){{/isModel}}{{/items}}{{/isMap}}{{/isContainer}}{{^isContainer}}{{#isModel}}[{{/isModel}}`{{dataType}}`{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{#baseType}}{{baseType}}{{/baseType}}.md){{/isModel}}{{/isContainer}}{{/isEnum}} | {{description}} | {{^required}} [optional property]{{/required}}{{#isReadOnly}} [readonly property]{{/isReadOnly}} +{{#vars}}**{{name}}** | {{#isInnerEnum}}[**{{datatypeWithEnum}}**](#{{datatypeWithEnum}}){{/isInnerEnum}}{{^isInnerEnum}}{{#isContainer}}{{#isArray}}{{#items}}{{#isModel}}[{{/isModel}}{{/items}}`{{baseType}}{{#items}}<{{dataType}}>`{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{#baseType}}{{baseType}}{{/baseType}}.md){{/isModel}}{{/items}}{{/isArray}}{{#isMap}}{{#items}}{{#isModel}}[{{/isModel}}`Map<String, {{dataType}}>`{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{#baseType}}{{baseType}}{{/baseType}}.md){{/isModel}}{{/items}}{{/isMap}}{{/isContainer}}{{^isContainer}}{{#isModel}}[{{/isModel}}`{{dataType}}`{{#isModel}}]({{^baseType}}{{dataType}}{{/baseType}}{{#baseType}}{{baseType}}{{/baseType}}.md){{/isModel}}{{/isContainer}}{{/isInnerEnum}} | {{description}} | {{^required}} [optional property]{{/required}}{{#isReadOnly}} [readonly property]{{/isReadOnly}} {{/vars}} {{#vars}} - {{#isEnum}} + {{#isInnerEnum}} ## {{datatypeWithEnum}} Name | Value @@ -25,7 +25,7 @@ Name | Value {{name}} | `{{{value}}}` {{/enumVars}} {{/allowableValues}} - {{/isEnum}} + {{/isInnerEnum}} {{/vars}} {{#vendorExtensions.x-implements.0}} diff --git a/modules/openapi-generator/src/main/resources/java-micronaut/common/model/oneof_interface.mustache b/modules/openapi-generator/src/main/resources/java-micronaut/common/model/oneof_interface.mustache index fbf0a8340cfb..4089cdc3751e 100644 --- a/modules/openapi-generator/src/main/resources/java-micronaut/common/model/oneof_interface.mustache +++ b/modules/openapi-generator/src/main/resources/java-micronaut/common/model/oneof_interface.mustache @@ -3,6 +3,13 @@ {{/additionalOneOfTypeAnnotations}} {{>common/generatedAnnotation}}{{>common/model/typeInfoAnnotation}}{{>common/model/xmlAnnotation}} public interface {{classname}} {{#vendorExtensions.x-implements}}{{#-first}}extends {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} { + {{#vars}} + {{#isEnum}} + {{#isDiscriminator}} +{{>common/model/modelInnerEnum}} + {{/isDiscriminator}} + {{/isEnum}} + {{/vars}} {{#discriminator}} public {{propertyType}} {{propertyGetter}}(); {{/discriminator}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java index 690755198af7..20abb6de23f9 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java @@ -2481,6 +2481,21 @@ public void testOneOfInterfaceGeneration() throws Exception { assertFileNotContains(Paths.get(modelDir + "DogRequest.java"), "@JsonTypeName(\"DogRequest\")"); } + @Test + public void testOneOfInterfaceInlineDiscriminatorEnum() throws Exception { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + OpenAPI openAPI = new OpenAPIParser() + .readLocation("src/test/resources/bugs/issue_25398.yaml", null, new ParseOptions()).getOpenAPI(); + codegen.setOutputDir(output.getAbsolutePath()); + codegen.setUseOneOfInterfaces(true); + new DefaultGenerator().opts(new ClientOptInput().openAPI(openAPI).config(codegen)).generate(); + + Path pet = output.toPath().resolve("src/gen/java/org/openapitools/model/Pet.java"); + assertFileContains(pet, "public enum PetTypeEnum {", "import com.fasterxml.jackson.annotation.*;"); + assertFileNotContains(output.toPath().resolve("src/gen/java/org/openapitools/model/Dog.java"), "public enum PetTypeEnum {"); + } + @Test public void generatesEmailAnnotationWhenBeanValidationEnabled() throws IOException { File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index 144bb6927b96..d6256b2e7f2b 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -2565,6 +2565,17 @@ void testOneOfWithEnumDiscriminator() throws IOException { ); } + @Test + public void testOneOfInterfaceDeclaresInlineDiscriminatorEnum() throws IOException { + Map files = generateFromContract("src/test/resources/bugs/issue_25398.yaml", SPRING_BOOT); + + assertThat(files.get("Pet.java").toPath()).content() + .contains("public enum PetTypeEnum {", "public PetTypeEnum getPetType();"); + assertThat(files.get("Dog.java").toPath()).content() + .contains("private PetTypeEnum petType;") + .doesNotContain("enum PetTypeEnum"); + } + @Test public void testTypeMappings() { final SpringCodegen codegen = new SpringCodegen(); diff --git a/modules/openapi-generator/src/test/resources/bugs/issue_25398.yaml b/modules/openapi-generator/src/test/resources/bugs/issue_25398.yaml new file mode 100644 index 000000000000..720f9345d14b --- /dev/null +++ b/modules/openapi-generator/src/test/resources/bugs/issue_25398.yaml @@ -0,0 +1,35 @@ +openapi: 3.0.1 +info: + title: oneOf interface with an inline enum discriminator + version: 0.0.1 +paths: {} +components: + schemas: + Pet: + type: object + required: + - petType + properties: + petType: + type: string + enum: + - dog + - cat + oneOf: + - $ref: '#/components/schemas/Dog' + - $ref: '#/components/schemas/Cat' + discriminator: + propertyName: petType + mapping: + dog: Dog + cat: Cat + Dog: + type: object + properties: + breed: + type: string + Cat: + type: object + properties: + declawed: + type: boolean From 38c8c71d9e06e476c5f9731012ae47b740ef9759 Mon Sep 17 00:00:00 2001 From: Christopher Pruijsen Date: Tue, 3 Nov 2026 14:18:51 +0000 Subject: [PATCH 2/2] test(java): assert implementor and property-level output for the oneOf discriminator enum --- .../jaxrs/JavaJAXRSSpecServerCodegenTest.java | 24 ++++++++++- .../bugs/issue_25398_property_level.yaml | 40 +++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/bugs/issue_25398_property_level.yaml diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java index 20abb6de23f9..15489502025a 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java @@ -2492,8 +2492,28 @@ public void testOneOfInterfaceInlineDiscriminatorEnum() throws Exception { new DefaultGenerator().opts(new ClientOptInput().openAPI(openAPI).config(codegen)).generate(); Path pet = output.toPath().resolve("src/gen/java/org/openapitools/model/Pet.java"); - assertFileContains(pet, "public enum PetTypeEnum {", "import com.fasterxml.jackson.annotation.*;"); - assertFileNotContains(output.toPath().resolve("src/gen/java/org/openapitools/model/Dog.java"), "public enum PetTypeEnum {"); + assertFileContains(pet, "public enum PetTypeEnum {", "import com.fasterxml.jackson.annotation.*;", + "PetTypeEnum getPetType();"); + Path dog = output.toPath().resolve("src/gen/java/org/openapitools/model/Dog.java"); + assertFileContains(dog, "implements Pet", "public PetTypeEnum getPetType()"); + assertFileNotContains(dog, "public enum PetTypeEnum {"); + } + + @Test + public void testPropertyLevelOneOfInterfaceInlineDiscriminatorEnum() throws Exception { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + OpenAPI openAPI = new OpenAPIParser() + .readLocation("src/test/resources/bugs/issue_25398_property_level.yaml", null, new ParseOptions()).getOpenAPI(); + codegen.setOutputDir(output.getAbsolutePath()); + codegen.setUseOneOfInterfaces(true); + new DefaultGenerator().opts(new ClientOptInput().openAPI(openAPI).config(codegen)).generate(); + + Path ownerPet = output.toPath().resolve("src/gen/java/org/openapitools/model/OwnerPet.java"); + assertFileContains(ownerPet, "public enum PetTypeEnum {", "PetTypeEnum getPetType();"); + Path dog = output.toPath().resolve("src/gen/java/org/openapitools/model/Dog.java"); + assertFileContains(dog, "implements OwnerPet", "public PetTypeEnum getPetType()"); + assertFileNotContains(dog, "public enum PetTypeEnum {"); } @Test diff --git a/modules/openapi-generator/src/test/resources/bugs/issue_25398_property_level.yaml b/modules/openapi-generator/src/test/resources/bugs/issue_25398_property_level.yaml new file mode 100644 index 000000000000..79a604cef17c --- /dev/null +++ b/modules/openapi-generator/src/test/resources/bugs/issue_25398_property_level.yaml @@ -0,0 +1,40 @@ +openapi: 3.0.1 +info: + title: property-level oneOf interface with an inline enum discriminator + version: 0.0.1 +paths: {} +components: + schemas: + Owner: + type: object + required: + - pet + properties: + pet: + type: object + required: + - petType + properties: + petType: + type: string + enum: + - dog + - cat + oneOf: + - $ref: '#/components/schemas/Dog' + - $ref: '#/components/schemas/Cat' + discriminator: + propertyName: petType + mapping: + dog: Dog + cat: Cat + Dog: + type: object + properties: + breed: + type: string + Cat: + type: object + properties: + declawed: + type: boolean