Skip to content

Commit 83b871c

Browse files
Merge with #24156
1 parent f330569 commit 83b871c

11 files changed

Lines changed: 390 additions & 96 deletions

File tree

modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,7 +1721,11 @@ public String toArrayModelParamName(String name) {
17211721
*/
17221722
@SuppressWarnings("static-method")
17231723
public String toEnumName(CodegenProperty property) {
1724-
return StringUtils.capitalize(property.name) + "Enum";
1724+
return toEnumName(property.name);
1725+
}
1726+
1727+
public String toEnumName(String propertyName) {
1728+
return StringUtils.capitalize(propertyName) + "Enum";
17251729
}
17261730

17271731
/**
@@ -3609,8 +3613,20 @@ protected CodegenDiscriminator createDiscriminator(String schemaName, Schema sch
36093613
// for example). Handling those scenarios is too complicated for me, I'm leaving it for
36103614
// the future..
36113615
String discriminatorType;
3612-
if (sourceDiscriminator.getPropertyType() != null) {
3613-
discriminatorType = toModelName(sourceDiscriminator.getPropertyType());
3616+
Schema discSchema = sourceDiscriminator.getDiscriminatorSchema();
3617+
if (discSchema != null) {
3618+
if (ModelUtils.hasRef(discSchema)) {
3619+
discriminatorType = toModelName(ModelUtils.getSimpleRef(discSchema.get$ref()));
3620+
} else {
3621+
// Inline schema (inline enum, uri, ...): resolve via the same pipeline the
3622+
// concrete model classes use so the oneOf interface getter type matches.
3623+
// Keep String for typeless const to avoid resolving to Object.
3624+
if (ModelUtils.getType(discSchema) == null || discSchema.getEnum() == null) {
3625+
discriminatorType = getTypeDeclaration(discSchema);
3626+
} else {
3627+
discriminatorType = toEnumName(discriminatorPropertyName);
3628+
}
3629+
}
36143630
} else {
36153631
discriminatorType = getDiscriminatorPropertyType(schema, discriminatorPropertyName);
36163632
}

modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/DiscriminatorUtils.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public static Optional<String> getDiscriminatorPropertyType(Schema schema, Strin
3838
* An example of a sibling is an enum-ref that has its own description. This will lead to the enum being
3939
* referenced as an allOf that in turn has a ref, rather than a regular ref directly to the enum.
4040
*
41-
* @param schema The input OAS schema.
41+
* @param schema The input OAS schema that has the discriminator as a property.
4242
* @param discriminatorName The name of the discriminator property.
4343
*/
4444
public static Schema getDiscriminatorSchema(Schema schema, String discriminatorName) {
@@ -71,8 +71,7 @@ public static DiscriminatorData recursiveGetDiscriminator(
7171
String discriminatorPropertyName = foundDisc.getDiscriminator().getPropertyName();
7272
return new DiscriminatorData(
7373
foundDisc.getDiscriminator(),
74-
DiscriminatorUtils.getDiscriminatorPropertyType(refSchema, discriminatorPropertyName)
75-
.orElse(null)
74+
DiscriminatorUtils.getDiscriminatorSchema(refSchema, discriminatorPropertyName)
7675
);
7776
}
7877

@@ -97,7 +96,7 @@ public static DiscriminatorData recursiveGetDiscriminator(
9796
if (foundDisc != null) {
9897
disc.setPropertyName(foundDisc.getPropertyName());
9998
disc.setMapping(foundDisc.getMapping());
100-
return new DiscriminatorData(disc, foundDisc.getPropertyType());
99+
return new DiscriminatorData(disc, foundDisc.getDiscriminatorSchema());
101100
}
102101
}
103102
}
@@ -133,7 +132,7 @@ private static DiscriminatorData getDiscriminatorFromAlternatives(
133132
Integer hasDiscriminatorCnt = 0;
134133
Integer hasNullTypeCnt = 0;
135134
Set<String> discriminatorsPropNames = new HashSet<>();
136-
Set<String> discriminatorTypes = new HashSet<>();
135+
Set<Schema> discriminatorTypes = new HashSet<>();
137136
for (Object alternative : alternativeSchemas) {
138137
if (ModelUtils.isNullType((Schema) alternative)) {
139138
// The null type does not have a discriminator. Skip.
@@ -144,22 +143,22 @@ private static DiscriminatorData getDiscriminatorFromAlternatives(
144143
if (foundDisc != null) {
145144
discriminatorsPropNames.add(foundDisc.getPropertyName());
146145
hasDiscriminatorCnt++;
147-
if (foundDisc.getPropertyType() != null) {
148-
discriminatorTypes.add(foundDisc.getPropertyType());
146+
if (foundDisc.getDiscriminatorSchema() != null) {
147+
discriminatorTypes.add(foundDisc.getDiscriminatorSchema());
149148
}
150149
}
151150
}
152151
if (discriminatorsPropNames.size() > 1) {
153152
once(LOGGER).warn(CONFLICTING_DISCRIMINATOR_NAMES, String.join(", ", discriminatorsPropNames));
154153
}
155154
if (discriminatorTypes.size() > 1) {
156-
once(LOGGER).warn(CONFLICTING_DISCRIMINATOR_TYPES, String.join(", ", discriminatorTypes));
155+
once(LOGGER).warn(CONFLICTING_DISCRIMINATOR_TYPES, String.join(", ", discriminatorTypes.toString()));
157156
}
158157
boolean allAlternativesHaveADiscriminator = hasDiscriminatorCnt + hasNullTypeCnt == alternativeSchemas.size();
159158
if (foundDisc != null && allAlternativesHaveADiscriminator && discriminatorsPropNames.size() == 1) {
160159
discriminator.setPropertyName(foundDisc.getPropertyName());
161160
discriminator.setMapping(foundDisc.getMapping());
162-
String uniqueDiscriminatorType = discriminatorTypes.size() == 1 ? discriminatorTypes.iterator().next() : null;
161+
Schema uniqueDiscriminatorType = discriminatorTypes.size() == 1 ? discriminatorTypes.iterator().next() : null;
163162
return new DiscriminatorData(discriminator, uniqueDiscriminatorType);
164163
}
165164
// If the scenario when composite schema has two children and one of them is the 'null' type,
@@ -171,19 +170,19 @@ public static class DiscriminatorData {
171170
private final Discriminator discriminator;
172171

173172
@Nullable
174-
private final String propertyType;
173+
private final Schema discriminatorSchema;
175174

176-
public DiscriminatorData(Discriminator discriminator, @Nullable String propertyType) {
175+
public DiscriminatorData(Discriminator discriminator, @Nullable Schema discriminatorSchema) {
177176
this.discriminator = discriminator;
178-
this.propertyType = propertyType;
177+
this.discriminatorSchema = discriminatorSchema;
179178
}
180179

181180
public Discriminator getDiscriminator() {
182181
return discriminator;
183182
}
184183

185-
public @Nullable String getPropertyType() {
186-
return propertyType;
184+
public @Nullable Schema getDiscriminatorSchema() {
185+
return discriminatorSchema;
187186
}
188187

189188
public String getPropertyName() {

modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2392,6 +2392,16 @@ public static boolean hasAnyOf(Schema schema) {
23922392
return false;
23932393
}
23942394

2395+
/**
2396+
* Returns true if the schema contains a $ref
2397+
*
2398+
* @param schema the schema
2399+
* @return true if $ref is set
2400+
*/
2401+
public static boolean hasRef(Schema schema) {
2402+
return schema != null && schema.get$ref() != null;
2403+
}
2404+
23952405
/**
23962406
* Returns schema type.
23972407
* For 3.1 spec, return the first one.

modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,32 +1211,84 @@ public void testEnumDiscriminatorWithDescriptionOverridden3_1() {
12111211
}
12121212

12131213
@Test
1214-
public void testOneOfAllOfEnumDiscriminatorInheritance() {
1215-
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_1/polymorphism-oneof-allof-and-enum-discriminator.yaml");
1214+
public void testOneOfAllOfEnumRefDiscriminatorInheritance() {
1215+
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/oneOfDiscriminator.yaml");
12161216
new OpenAPINormalizer(openAPI, Map.of()).normalize();
12171217
DefaultCodegen codegen = new DefaultCodegen();
12181218
codegen.setUseOneOfInterfaces(true);
12191219
codegen.setLegacyDiscriminatorBehavior(false);
12201220

1221-
Schema inner = openAPI.getComponents().getSchemas().get("getAllPets_200_response_inner");
1221+
Schema inner = openAPI.getComponents().getSchemas().get("VehicleResponse");
12221222
codegen.setOpenAPI(openAPI);
1223-
CodegenModel innerModel = codegen.fromModel("getAllPets_200_response_inner", inner);
1223+
CodegenModel innerModel = codegen.fromModel("VehicleResponse", inner);
12241224
assertTrue(innerModel.getHasDiscriminatorWithNonEmptyMapping());
12251225
assertTrue(innerModel.discriminator.getIsEnum());
1226-
assertEquals("PetType", innerModel.discriminator.getPropertyType());
1226+
assertEquals("VehicleType", innerModel.discriminator.getPropertyType());
12271227
assertTrue(innerModel.getVars().get(0).isEnumRef);
12281228

1229-
Schema cat = openAPI.getComponents().getSchemas().get("Cat");
1230-
CodegenModel catModel = codegen.fromModel("Cat", cat);
1231-
assertTrue(catModel.discriminator.getIsEnum());
1232-
assertEquals("PetType", catModel.discriminator.getPropertyType());
1233-
assertTrue(catModel.getVars().get(0).isEnumRef);
1229+
Schema car = openAPI.getComponents().getSchemas().get("Car");
1230+
CodegenModel carModel = codegen.fromModel("Car", car);
1231+
assertTrue(carModel.discriminator.getIsEnum());
1232+
assertEquals("VehicleType", carModel.discriminator.getPropertyType());
1233+
assertTrue(carModel.getVars().get(0).isEnumRef);
1234+
1235+
Schema bike = openAPI.getComponents().getSchemas().get("Bike");
1236+
CodegenModel bikeModel = codegen.fromModel("Bike", bike);
1237+
assertTrue(bikeModel.discriminator.getIsEnum());
1238+
assertEquals("VehicleType", bikeModel.discriminator.getPropertyType());
1239+
assertTrue(bikeModel.getVars().get(0).isEnumRef);
1240+
}
1241+
1242+
@Test
1243+
public void testOneOfAllOfInlineEnumDiscriminatorInheritance() {
1244+
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/oneOfDiscriminator.yaml");
1245+
new OpenAPINormalizer(openAPI, Map.of()).normalize();
1246+
DefaultCodegen codegen = new DefaultCodegen();
1247+
codegen.setUseOneOfInterfaces(true);
1248+
codegen.setLegacyDiscriminatorBehavior(false);
1249+
1250+
Schema inner = openAPI.getComponents().getSchemas().get("PetResponseEnumDisc");
1251+
codegen.setOpenAPI(openAPI);
1252+
CodegenModel innerModel = codegen.fromModel("PetResponseEnumDisc", inner);
1253+
assertTrue(innerModel.getHasDiscriminatorWithNonEmptyMapping());
1254+
assertTrue(innerModel.discriminator.getIsEnum());
1255+
assertEquals("PetTypeEnum", innerModel.discriminator.getPropertyType());
1256+
assertFalse(innerModel.getVars().get(0).isEnumRef);
12341257

1235-
Schema dog = openAPI.getComponents().getSchemas().get("Dog");
1236-
CodegenModel dogModel = codegen.fromModel("Dog", dog);
1258+
Schema dog = openAPI.getComponents().getSchemas().get("DogEnumDisc");
1259+
CodegenModel dogModel = codegen.fromModel("DogEnumDisc", dog);
12371260
assertTrue(dogModel.discriminator.getIsEnum());
1238-
assertEquals("PetType", dogModel.discriminator.getPropertyType());
1239-
assertTrue(dogModel.getVars().get(0).isEnumRef);
1261+
assertEquals("PetTypeEnum", dogModel.discriminator.getPropertyType());
1262+
assertFalse(dogModel.getVars().get(0).isEnumRef);
1263+
1264+
Schema cat = openAPI.getComponents().getSchemas().get("CatEnumDisc");
1265+
CodegenModel catModel = codegen.fromModel("CatEnumDisc", cat);
1266+
assertTrue(catModel.discriminator.getIsEnum());
1267+
assertEquals("PetTypeEnum", catModel.discriminator.getPropertyType());
1268+
assertFalse(catModel.getVars().get(0).isEnumRef);
1269+
}
1270+
1271+
@Test
1272+
public void testOneOfAllOfInlineStringWithFormatDiscriminatorInheritance() {
1273+
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/oneOfDiscriminator.yaml");
1274+
new OpenAPINormalizer(openAPI, Map.of()).normalize();
1275+
DefaultCodegen codegen = new DefaultCodegen();
1276+
codegen.setUseOneOfInterfaces(true);
1277+
codegen.setLegacyDiscriminatorBehavior(false);
1278+
1279+
Schema inner = openAPI.getComponents().getSchemas().get("PetResponseUriDisc");
1280+
codegen.setOpenAPI(openAPI);
1281+
CodegenModel innerModel = codegen.fromModel("PetResponseUriDisc", inner);
1282+
assertTrue(innerModel.getHasDiscriminatorWithNonEmptyMapping());
1283+
assertEquals("URI", innerModel.discriminator.getPropertyType());
1284+
1285+
Schema dog = openAPI.getComponents().getSchemas().get("DogUriDisc");
1286+
CodegenModel dogModel = codegen.fromModel("DogUriDisc", dog);
1287+
assertEquals("URI", dogModel.discriminator.getPropertyType());
1288+
1289+
Schema cat = openAPI.getComponents().getSchemas().get("CatUriDisc");
1290+
CodegenModel catModel = codegen.fromModel("CatUriDisc", cat);
1291+
assertEquals("URI", catModel.discriminator.getPropertyType());
12401292
}
12411293

12421294
@Test

modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2038,6 +2038,37 @@ public void testDiscriminatorWithMappingIssue14731() {
20382038
.content().doesNotContain("@JsonTypeName");
20392039
}
20402040

2041+
@Test
2042+
public void testOneOfInterfaceInheritedEnumDiscriminator() {
2043+
final Path output = newTempFolder();
2044+
final OpenAPI openAPI = new OpenAPIParser()
2045+
.readLocation("src/test/resources/3_0/oneOfDiscriminator.yaml", null, new ParseOptions())
2046+
.getOpenAPI();
2047+
2048+
// resttemplate (unlike the default okhttp-gson) renders oneOf as interfaces, so it uses the
2049+
// base Java/oneof_interface template that emits the discriminator getter type.
2050+
final JavaClientCodegen codegen = new JavaClientCodegen();
2051+
codegen.setLibrary("resttemplate");
2052+
codegen.setOutputDir(output.toString());
2053+
codegen.setUseOneOfInterfaces(true);
2054+
codegen.setLegacyDiscriminatorBehavior(false);
2055+
2056+
final ClientOptInput input = new ClientOptInput().openAPI(openAPI).config(codegen);
2057+
2058+
DefaultGenerator generator = new DefaultGenerator();
2059+
generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true");
2060+
generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "false");
2061+
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
2062+
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
2063+
generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "false");
2064+
generator.opts(input).generate();
2065+
2066+
// Issue #22541: the inline-enum discriminator inherited from a base schema via allOf must
2067+
// resolve to the enum type in the oneOf interface getter, not String.
2068+
assertThat(output.resolve("src/main/java/org/openapitools/client/model/PetResponseEnumDisc.java"))
2069+
.content().contains("public PetTypeEnum getPetType();");
2070+
}
2071+
20412072
@Test
20422073
public void testDiscriminatorWithoutMappingIssue14731() {
20432074
final Path output = newTempFolder();

modules/openapi-generator/src/test/java/org/openapitools/codegen/java/helidon/JavaHelidonCommonCodegenTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.openapitools.codegen.DefaultGenerator;
2222
import org.openapitools.codegen.TestUtils;
2323
import org.openapitools.codegen.config.CodegenConfigurator;
24+
import org.openapitools.codegen.languages.AbstractJavaCodegen;
2425
import org.openapitools.codegen.languages.JavaHelidonCommonCodegen;
2526
import org.testng.Assert;
2627
import org.testng.annotations.BeforeMethod;
@@ -70,6 +71,35 @@ public void setup() throws IOException {
7071
mpClientGenerator = new DefaultGenerator();
7172
}
7273

74+
@Test
75+
public void testOneOfInterfaceInheritedEnumDiscriminator() throws IOException {
76+
File output = Files.createTempDirectory("testHelidonEnumDisc").toFile();
77+
output.deleteOnExit();
78+
String outputDir = output.getAbsolutePath().replace('\\', '/');
79+
80+
CodegenConfigurator configurator = new CodegenConfigurator()
81+
.setGeneratorName("java-helidon-client")
82+
.setLibrary("mp")
83+
.setInputSpec("src/test/resources/3_0/oneOfDiscriminator.yaml")
84+
.addAdditionalProperty(AbstractJavaCodegen.USE_ONE_OF_INTERFACES, true)
85+
.addAdditionalProperty(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR, false)
86+
.setOutputDir(outputDir);
87+
88+
DefaultGenerator generator = new DefaultGenerator();
89+
generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true");
90+
generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "false");
91+
generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "false");
92+
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
93+
generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
94+
generator.opts(configurator.toClientOptInput()).generate();
95+
96+
// Issue #22541: the inline-enum discriminator inherited from a base schema via allOf must
97+
// resolve to the enum type in the oneOf interface getter, not String.
98+
TestUtils.assertFileContains(
99+
Paths.get(outputDir + "/src/main/java/org/openapitools/client/model/PetResponseEnumDisc.java"),
100+
"PetTypeEnum getPetType()");
101+
}
102+
73103
@Test
74104
public void defaultVersionTest() {
75105
runServerVersionTest(null, null);

modules/openapi-generator/src/test/java/org/openapitools/codegen/java/micronaut/JavaMicronautClientCodegenTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,21 @@ public void testInitialConfigValues() throws Exception {
4747
configAssert.assertValue(CodegenConstants.INVOKER_PACKAGE, codegen::getInvokerPackage, "org.openapitools");
4848
}
4949

50+
@Test
51+
public void testOneOfInterfaceInheritedEnumDiscriminator() {
52+
JavaMicronautClientCodegen codegen = new JavaMicronautClientCodegen();
53+
codegen.setUseOneOfInterfaces(true);
54+
codegen.setLegacyDiscriminatorBehavior(false);
55+
String outputPath = generateFiles(codegen, "src/test/resources/3_0/oneOfDiscriminator.yaml",
56+
CodegenConstants.MODELS);
57+
58+
// Issue #22541: the inline-enum discriminator is inherited from a base schema via allOf, so
59+
// the oneOf interface getter must resolve to the enum type, not String
60+
// (DefaultCodegen.getDiscriminatorPropertyType).
61+
assertFileContains(outputPath + "src/main/java/org/openapitools/model/PetResponseEnumDisc.java",
62+
"public PetTypeEnum getPetType();");
63+
}
64+
5065
@Test
5166
public void testApiAndModelFilesPresent() {
5267
JavaMicronautClientCodegen codegen = new JavaMicronautClientCodegen();

0 commit comments

Comments
 (0)