Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,17 @@ public static DiscriminatorData recursiveGetDiscriminator(
DiscriminatorData foundDisc = new DiscriminatorData(refSchema.getDiscriminator(), null);
if (foundDisc.getDiscriminator() != null) {
String discriminatorPropertyName = foundDisc.getDiscriminator().getPropertyName();
return new DiscriminatorData(
foundDisc.getDiscriminator(),
DiscriminatorUtils.getDiscriminatorSchema(refSchema, discriminatorPropertyName)
);
Schema discriminatorSchema = DiscriminatorUtils.getDiscriminatorSchema(refSchema, discriminatorPropertyName);
if (discriminatorSchema == null) {
// The schema declares a discriminator but does not itself define the discriminator
// property (e.g. a oneOf/anyOf interface whose property is inherited by each
// alternative from a shared base). Descend into the alternatives to discover the
// property type so the generated getter matches the concrete classes rather than
// falling back to String. Ref: issue #22636.
discriminatorSchema = findDiscriminatorSchemaInAlternatives(
openAPI, refSchema, discriminatorPropertyName, new ArrayList<>());
}
return new DiscriminatorData(foundDisc.getDiscriminator(), discriminatorSchema);
}

if (legacyDiscriminatorBehavior) {
Expand Down Expand Up @@ -199,6 +206,54 @@ private static Schema getDiscriminatorSchema(Schema schema, String discriminator
return discSchema;
}

/**
* Recursively look for the schema of the discriminator property within the alternatives (oneOf,
* anyOf, allOf) of a composed schema. This is used when a schema declares a discriminator but
* does not itself define the discriminator property, so its type must be discovered from the
* schemas that do (typically a shared base inherited via allOf by each alternative).
*
* @param openAPI the openAPI specification
* @param schema the schema whose alternatives should be searched
* @param discriminatorName the name of the discriminator property
* @param visitedSchemas the schemas already visited, to guard against recursive definitions
* @return the discriminator property schema if found, otherwise null
*/
private static Schema findDiscriminatorSchemaInAlternatives(OpenAPI openAPI,
Schema schema,
String discriminatorName,
List<Schema> visitedSchemas) {
Schema refSchema = ModelUtils.getReferencedSchema(openAPI, schema);
for (Schema s : visitedSchemas) {
if (s == refSchema) {
return null;
}
}
visitedSchemas.add(refSchema);

Schema discSchema = getDiscriminatorSchema(refSchema, discriminatorName);
if (discSchema != null) {
return discSchema;
}

List<Schema> alternatives = new ArrayList<>();
if (refSchema.getOneOf() != null) {
alternatives.addAll(refSchema.getOneOf());
}
if (refSchema.getAnyOf() != null) {
alternatives.addAll(refSchema.getAnyOf());
}
if (refSchema.getAllOf() != null) {
alternatives.addAll(refSchema.getAllOf());
}
for (Schema alternative : alternatives) {
Schema found = findDiscriminatorSchemaInAlternatives(openAPI, alternative, discriminatorName, visitedSchemas);
if (found != null) {
return found;
}
}
return null;
}

/**
* Check whether the alternative schemas share a discriminator, and if they do, return it
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,26 @@ public void testOneOfAllOfInlineStringWithFormatDiscriminatorInheritance() {
assertEquals("URI", catModel.discriminator.getPropertyType());
}

@Test
public void testOneOfDiscriminatorOnInterfacePropertyInAlternatives() {
// The oneOf interface declares the discriminator on itself but does not define the
// discriminator property locally; each alternative inherits it (an enum ref) from a shared
// base via allOf. The interface getter must resolve to the enum type, not String.
// Ref: issue #22636.
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/oneOfDiscriminator.yaml");
new OpenAPINormalizer(openAPI, Map.of()).normalize();
DefaultCodegen codegen = new DefaultCodegen();
codegen.setUseOneOfInterfaces(true);
codegen.setLegacyDiscriminatorBehavior(false);
codegen.setOpenAPI(openAPI);

Schema inner = openAPI.getComponents().getSchemas().get("PetRequestV3");
CodegenModel innerModel = codegen.fromModel("PetRequestV3", inner);
assertTrue(innerModel.getHasDiscriminatorWithNonEmptyMapping());
assertTrue(innerModel.discriminator.getIsEnum());
assertEquals("PetType", innerModel.discriminator.getPropertyType());
}

@Test
public void testParentName() {
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/allOf.yaml");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,4 +384,46 @@ components:
Bike:
type: object
allOf:
- $ref: '#/components/schemas/Vehicle'
- $ref: '#/components/schemas/Vehicle'
# oneOf interface that declares the discriminator on the interface schema ITSELF (not on the
# alternatives) and does not define the discriminator property locally. The property type is
# an enum ref inherited by each alternative from a shared base (PetV3Base) via allOf. The
# interface getter must use the enum type (PetType), not String. Ref: issue #22636.
PetRequestV3:
type: object
oneOf:
- $ref: '#/components/schemas/CatRequestV3'
- $ref: '#/components/schemas/DogRequestV3'
discriminator:
propertyName: petType
mapping:
CAT: '#/components/schemas/CatRequestV3'
DOG: '#/components/schemas/DogRequestV3'
PetType:
type: string
enum:
- CAT
- DOG
PetV3Base:
type: object
required:
- petType
properties:
petType:
$ref: '#/components/schemas/PetType'
CatRequestV3:
type: object
allOf:
- $ref: '#/components/schemas/PetV3Base'
- type: object
properties:
indoor:
type: boolean
DogRequestV3:
type: object
allOf:
- $ref: '#/components/schemas/PetV3Base'
- type: object
properties:
trained:
type: boolean
Loading