Skip to content

Commit 3abe602

Browse files
author
Dennis Ameling
committed
Add discriminator property to sealed class
1 parent a4777ba commit 3abe602

5 files changed

Lines changed: 64 additions & 30 deletions

File tree

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

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -549,20 +549,46 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
549549
if (fixJacksonJsonTypeInfoInheritance) {
550550
// When fixJacksonJsonTypeInfoInheritance is true:
551551
// 1. Always set visible=true so Jackson can read the discriminator
552-
// 2. For oneOf/anyOf patterns: ensure children have the discriminator property with default values
552+
// 2. For oneOf/anyOf patterns: add discriminator property to parent and children
553553
visibleTrue = true;
554554

555-
// Only add/modify discriminator property for oneOf/anyOf patterns
556-
// For allOf patterns, the property is already inherited and handled elsewhere
555+
// For oneOf/anyOf patterns, add the discriminator property to the parent sealed class
556+
// This allows accessing the discriminator value from the parent type directly
557557
if (isOneOfOrAnyOfPattern) {
558+
String discriminatorVarName = toVarName(discriminatorPropBaseName);
559+
560+
// Clear all merged properties from the oneOf parent - they belong to children only
561+
// We'll add back just the discriminator property
562+
owner.getVars().clear();
563+
owner.getRequiredVars().clear();
564+
owner.getOptionalVars().clear();
565+
owner.getAllVars().clear();
566+
567+
// Add discriminator property to parent
568+
CodegenProperty parentDiscriminatorProp = new CodegenProperty();
569+
parentDiscriminatorProp.baseName = discriminatorPropBaseName;
570+
parentDiscriminatorProp.name = discriminatorVarName;
571+
parentDiscriminatorProp.dataType = "kotlin.String";
572+
parentDiscriminatorProp.datatypeWithEnum = "kotlin.String";
573+
parentDiscriminatorProp.required = true;
574+
parentDiscriminatorProp.isNullable = false;
575+
parentDiscriminatorProp.isReadOnly = false;
576+
577+
owner.getVars().add(parentDiscriminatorProp);
578+
owner.getRequiredVars().add(parentDiscriminatorProp);
579+
owner.getAllVars().add(parentDiscriminatorProp);
580+
581+
// Parent now has properties (just the discriminator)
582+
hasParentProperties = true;
583+
584+
// Process children: mark discriminator as inherited and set default values
558585
for (CodegenDiscriminator.MappedModel mappedModel : owner.getDiscriminator().getMappedModels()) {
559586
CodegenModel childModel = allModelsMap.get(mappedModel.getModelName());
560587
if (childModel != null) {
561-
// Check if child already has the discriminator property and set default value
562588
boolean hasDiscriminatorProp = false;
563589
String discriminatorDefault = "\"" + mappedModel.getMappingName() + "\"";
564590

565-
// Update existing discriminator property in all lists
591+
// Update existing discriminator property in all lists - mark as inherited
566592
for (CodegenProperty prop : childModel.getVars()) {
567593
if (prop.getBaseName().equals(discriminatorPropBaseName)) {
568594
hasDiscriminatorProp = true;
@@ -571,6 +597,7 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
571597
prop.datatypeWithEnum = "kotlin.String";
572598
prop.required = true;
573599
prop.isNullable = false;
600+
prop.isInherited = true;
574601
}
575602
}
576603
for (CodegenProperty prop : childModel.getAllVars()) {
@@ -580,6 +607,7 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
580607
prop.datatypeWithEnum = "kotlin.String";
581608
prop.required = true;
582609
prop.isNullable = false;
610+
prop.isInherited = true;
583611
}
584612
}
585613

@@ -592,6 +620,7 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
592620
prop.datatypeWithEnum = "kotlin.String";
593621
prop.required = true;
594622
prop.isNullable = false;
623+
prop.isInherited = true;
595624
propToMove = prop;
596625
break;
597626
}
@@ -608,26 +637,31 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
608637
prop.dataType = "kotlin.String";
609638
prop.datatypeWithEnum = "kotlin.String";
610639
prop.isNullable = false;
640+
prop.isInherited = true;
611641
}
612642
}
613643

614-
// If child doesn't have the discriminator property, add it as required
644+
// If child doesn't have the discriminator property, add it as required and inherited
615645
if (!hasDiscriminatorProp) {
616646
CodegenProperty discriminatorProp = new CodegenProperty();
617647
discriminatorProp.baseName = discriminatorPropBaseName;
618-
discriminatorProp.name = toVarName(discriminatorPropBaseName);
648+
discriminatorProp.name = discriminatorVarName;
619649
discriminatorProp.dataType = "kotlin.String";
620650
discriminatorProp.datatypeWithEnum = "kotlin.String";
621651
discriminatorProp.defaultValue = discriminatorDefault;
622652
discriminatorProp.required = true;
623653
discriminatorProp.isNullable = false;
624654
discriminatorProp.isReadOnly = false;
655+
discriminatorProp.isInherited = true;
625656

626-
// Add to vars and requiredVars (required property with default value)
627657
childModel.getVars().add(discriminatorProp);
628658
childModel.getRequiredVars().add(discriminatorProp);
629659
childModel.getAllVars().add(discriminatorProp);
630660
}
661+
662+
// Set parent constructor args for the discriminator property
663+
childModel.getVendorExtensions().put("x-parent-ctor-args",
664+
discriminatorVarName + " = " + discriminatorVarName);
631665
}
632666
}
633667
}

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

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ public void givenSchemaObjectPropertyNameContainsDollarSignWhenGenerateThenDolla
314314
// ==================== Polymorphism and Discriminator Tests ====================
315315

316316
@Test
317-
public void oneOfWithDiscriminator_generatesEmptySealedClassWithJacksonAnnotations() throws IOException {
317+
public void oneOfWithDiscriminator_generatesSealedClassWithDiscriminatorProperty() throws IOException {
318318
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
319319
output.deleteOnExit();
320320

@@ -330,26 +330,20 @@ public void oneOfWithDiscriminator_generatesEmptySealedClassWithJacksonAnnotatio
330330
String outputPath = output.getAbsolutePath() + "/src/main/kotlin/org/openapitools/server";
331331
Path petModel = Paths.get(outputPath + "/models/Pet.kt");
332332

333-
// Pet should be an empty sealed class with Jackson polymorphism annotations
333+
// Pet should be a sealed class with Jackson polymorphism annotations and discriminator property
334334
assertFileContains(
335335
petModel,
336-
"sealed class Pet",
336+
"sealed class Pet(",
337+
"open val petType: kotlin.String",
337338
"@com.fasterxml.jackson.annotation.JsonTypeInfo",
338339
"property = \"petType\"",
339340
"visible = true",
340341
"@com.fasterxml.jackson.annotation.JsonSubTypes"
341342
);
342-
343-
// Pet should NOT have properties in its constructor (oneOf pattern)
344-
assertFileNotContains(
345-
petModel,
346-
"sealed class Pet(",
347-
"open val"
348-
);
349343
}
350344

351345
@Test
352-
public void oneOfWithDiscriminator_generatesChildrenWithDiscriminatorPropertyAsRequiredString() throws IOException {
346+
public void oneOfWithDiscriminator_generatesChildrenWithOverrideDiscriminatorProperty() throws IOException {
353347
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
354348
output.deleteOnExit();
355349

@@ -364,13 +358,13 @@ public void oneOfWithDiscriminator_generatesChildrenWithDiscriminatorPropertyAsR
364358

365359
String outputPath = output.getAbsolutePath() + "/src/main/kotlin/org/openapitools/server";
366360

367-
// Cat should have petType as non-nullable String with default value
361+
// Cat should have petType as overridden non-nullable String with default value
368362
Path catModel = Paths.get(outputPath + "/models/Cat.kt");
369363
assertFileContains(
370364
catModel,
371365
"data class Cat(",
372-
"val petType: kotlin.String = \"cat\"",
373-
") : Pet()"
366+
"override val petType: kotlin.String = \"cat\"",
367+
") : Pet(petType = petType)"
374368
);
375369
// Should NOT be nullable
376370
assertFileNotContains(
@@ -379,13 +373,13 @@ public void oneOfWithDiscriminator_generatesChildrenWithDiscriminatorPropertyAsR
379373
"petType: kotlin.Any"
380374
);
381375

382-
// Dog should have petType as non-nullable String with default value
376+
// Dog should have petType as overridden non-nullable String with default value
383377
Path dogModel = Paths.get(outputPath + "/models/Dog.kt");
384378
assertFileContains(
385379
dogModel,
386380
"data class Dog(",
387-
"val petType: kotlin.String = \"dog\"",
388-
") : Pet()"
381+
"override val petType: kotlin.String = \"dog\"",
382+
") : Pet(petType = petType)"
389383
);
390384
// Should NOT be nullable
391385
assertFileNotContains(

samples/server/others/kotlin-server/polymorphism-and-discriminator/src/main/kotlin/org/openapitools/server/models/Cat.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ data class Cat(
2424
val huntingSkill: Cat.HuntingSkill,
2525

2626
@field:com.fasterxml.jackson.annotation.JsonProperty("petType")
27-
val petType: kotlin.String = "cat",
27+
override val petType: kotlin.String = "cat",
2828

29-
) : Pet()
29+
) : Pet(petType = petType)
3030
{
3131
/**
3232
* The measured skill for hunting

samples/server/others/kotlin-server/polymorphism-and-discriminator/src/main/kotlin/org/openapitools/server/models/Dog.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ package org.openapitools.server.models
2020
data class Dog(
2121

2222
@field:com.fasterxml.jackson.annotation.JsonProperty("petType")
23-
val petType: kotlin.String = "dog",
23+
override val petType: kotlin.String = "dog",
2424
/* the size of the pack the dog is from */
2525

2626
@field:com.fasterxml.jackson.annotation.JsonProperty("packSize")
2727
val packSize: kotlin.Int = 0
28-
) : Pet()
28+
) : Pet(petType = petType)
2929

samples/server/others/kotlin-server/polymorphism-and-discriminator/src/main/kotlin/org/openapitools/server/models/Pet.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,17 @@ import org.openapitools.server.models.Dog
1616

1717
/**
1818
*
19+
* @param petType
1920
*/
2021
@com.fasterxml.jackson.annotation.JsonTypeInfo(use = com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME, include = com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY, property = "petType", visible = true)
2122
@com.fasterxml.jackson.annotation.JsonSubTypes(
2223
com.fasterxml.jackson.annotation.JsonSubTypes.Type(value = Cat::class, name = "cat"),
2324
com.fasterxml.jackson.annotation.JsonSubTypes.Type(value = Dog::class, name = "dog")
2425
)
25-
sealed class Pet
26+
sealed class Pet(
27+
28+
@field:com.fasterxml.jackson.annotation.JsonProperty("petType")
29+
open val petType: kotlin.String
30+
31+
)
2632

0 commit comments

Comments
 (0)