Skip to content

Commit 5f54c25

Browse files
Merge branch 'fix-allof-oneof-ref-flattening'
2 parents 5b9a9ce + 492ef4a commit 5f54c25

43 files changed

Lines changed: 174 additions & 109 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2807,23 +2807,31 @@ protected void updateModelForComposedSchema(CodegenModel m, Schema schema, Map<S
28072807
addImport(composed, refSchema, m, modelName);
28082808

28092809
if (allDefinitions != null && refSchema != null) {
2810-
if (ModelUtils.hasOneOf(refSchema)) {
2811-
// Do not flatten oneOf variant properties into this model.
2812-
// addProperties() would recurse into each variant and merge all
2813-
// their properties, producing an impossible conjunction. The oneOf
2814-
// ref is already tracked via m.interfaces.
2815-
} else if (allParents.contains(ref) && supportsMultipleInheritance) {
2810+
// When the refSchema is a oneOf wrapper, do not flatten the variant
2811+
// properties into this model — that would produce an impossible
2812+
// conjunction of all variants' properties. The wrapper's own
2813+
// properties/required (shared fields declared on the wrapper itself)
2814+
// should still be inherited. The oneOf variants are tracked via
2815+
// m.interfaces. Pre-seeding visitedSchemas with the variant schemas
2816+
// suppresses recursion into them inside addProperties().
2817+
Set<Schema> visited = new HashSet<>();
2818+
if (ModelUtils.hasOneOf(refSchema) && refSchema.getOneOf() != null) {
2819+
for (Object variant : refSchema.getOneOf()) {
2820+
visited.add((Schema) variant);
2821+
}
2822+
}
2823+
if (allParents.contains(ref) && supportsMultipleInheritance) {
28162824
// multiple inheritance
2817-
addProperties(allProperties, allRequired, refSchema, new HashSet<>());
2825+
addProperties(allProperties, allRequired, refSchema, visited);
28182826
} else if (parentName != null && parentName.equals(ref) && supportsInheritance) {
28192827
// single inheritance
2820-
addProperties(allProperties, allRequired, refSchema, new HashSet<>());
2828+
addProperties(allProperties, allRequired, refSchema, visited);
28212829
} else {
28222830
// composition
28232831
Map<String, Schema> newProperties = new LinkedHashMap<>();
2824-
addProperties(newProperties, required, refSchema, new HashSet<>());
2832+
addProperties(newProperties, required, refSchema, new HashSet<>(visited));
28252833
mergeProperties(properties, newProperties);
2826-
addProperties(allProperties, allRequired, refSchema, new HashSet<>());
2834+
addProperties(allProperties, allRequired, refSchema, visited);
28272835
}
28282836
}
28292837

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,13 @@ private boolean modelIsMutable(CodegenModel model, Set<String> processed) {
717717
processed = new HashSet<String>();
718718
}
719719
boolean isMutable = model.allVars.stream().anyMatch(v -> !v.isReadOnly);
720+
// oneOf/anyOf union wrappers expose a constructor per variant; the variants
721+
// are not tracked in allVars, so fall back to the composed schema lists.
722+
if (!isMutable && model.getComposedSchemas() != null) {
723+
List<CodegenProperty> oneOf = model.getComposedSchemas().getOneOf();
724+
List<CodegenProperty> anyOf = model.getComposedSchemas().getAnyOf();
725+
isMutable = (oneOf != null && !oneOf.isEmpty()) || (anyOf != null && !anyOf.isEmpty());
726+
}
720727
if (!isMutable && !processed.contains(model.classname) && model.getDiscriminator() != null && model.getDiscriminator().getMappedModels() != null) {
721728
processed.add(model.classname);
722729
isMutable = modelIsMutable(model, processed);

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,34 @@ public void testAllOfWithOneOfRefNoDiscriminatorNoInheritance() {
12361236
assertFalse(varNames.contains("zOnlyField"), "vars should not contain variant-specific 'zOnlyField'");
12371237
}
12381238

1239+
@Test
1240+
public void testAllOfWithOneOfRefPreservesWrapperProperties() {
1241+
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/allOf_oneOf_noDiscriminator.yaml");
1242+
DefaultCodegen codegen = new DefaultCodegen();
1243+
codegen.supportsInheritance = true;
1244+
codegen.setOpenAPI(openAPI);
1245+
1246+
// ChildWithSharedFields is a oneOf wrapper that also declares its own
1247+
// properties (wrapperOptionalField, wrapperRequiredField). When ParentWithSharedFields
1248+
// inherits from it via allOf, those wrapper-level properties must still flow
1249+
// through to allProperties/allRequired — only the variant-specific properties
1250+
// should be suppressed.
1251+
Schema parentSchema = openAPI.getComponents().getSchemas().get("ParentWithSharedFields");
1252+
CodegenModel parentModel = codegen.fromModel("ParentWithSharedFields", parentSchema);
1253+
1254+
List<String> allVarNames = parentModel.allVars.stream().map(v -> v.name).collect(Collectors.toList());
1255+
assertTrue(allVarNames.contains("wrapperOptionalField"), "allVars should contain wrapper-level 'wrapperOptionalField'");
1256+
assertTrue(allVarNames.contains("wrapperRequiredField"), "allVars should contain wrapper-level 'wrapperRequiredField'");
1257+
assertTrue(allVarNames.contains("extraField"), "allVars should contain inline allOf 'extraField'");
1258+
assertFalse(allVarNames.contains("xOnlyField"), "allVars should not contain variant-specific 'xOnlyField'");
1259+
assertFalse(allVarNames.contains("yOnlyField"), "allVars should not contain variant-specific 'yOnlyField'");
1260+
assertFalse(allVarNames.contains("zOnlyField"), "allVars should not contain variant-specific 'zOnlyField'");
1261+
1262+
List<String> requiredVarNames = parentModel.requiredVars.stream().map(v -> v.name).collect(Collectors.toList());
1263+
assertTrue(requiredVarNames.contains("wrapperRequiredField"), "requiredVars should include wrapper-level required 'wrapperRequiredField'");
1264+
assertTrue(requiredVarNames.contains("extraField"), "requiredVars should include inline allOf required 'extraField'");
1265+
}
1266+
12391267
@Test
12401268
public void testAllOfSingleAndDoubleRefWithOwnPropsNoDiscriminator() {
12411269
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/allOf_composition.yaml");

modules/openapi-generator/src/test/resources/3_0/allOf_oneOf_noDiscriminator.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ paths:
1212
application/json:
1313
schema:
1414
$ref: '#/components/schemas/ParentC'
15+
/parent-shared:
16+
get:
17+
responses:
18+
'200':
19+
description: OK
20+
content:
21+
application/json:
22+
schema:
23+
$ref: '#/components/schemas/ParentWithSharedFields'
1524
components:
1625
schemas:
1726
ParentC:
@@ -29,6 +38,28 @@ components:
2938
- $ref: "#/components/schemas/ChildY"
3039
- $ref: "#/components/schemas/ChildZ"
3140

41+
ParentWithSharedFields:
42+
allOf:
43+
- $ref: "#/components/schemas/ChildWithSharedFields"
44+
- type: object
45+
required: [extraField]
46+
properties:
47+
extraField:
48+
type: string
49+
50+
ChildWithSharedFields:
51+
type: object
52+
required: [wrapperRequiredField]
53+
properties:
54+
wrapperOptionalField:
55+
type: string
56+
wrapperRequiredField:
57+
type: string
58+
oneOf:
59+
- $ref: "#/components/schemas/ChildX"
60+
- $ref: "#/components/schemas/ChildY"
61+
- $ref: "#/components/schemas/ChildZ"
62+
3263
ChildX:
3364
type: object
3465
required: [kind, sharedField, xOnlyField]

samples/client/petstore/csharp/generichost/latest/UseDateTimeOffset/src/Org.OpenAPITools/Model/OneOfString.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public partial class OneOfString : IValidatableObject
3434
/// Initializes a new instance of the <see cref="OneOfString" /> class.
3535
/// </summary>
3636
/// <param name="string"></param>
37-
internal OneOfString(string @string)
37+
public OneOfString(string @string)
3838
{
3939
String = @string;
4040
OnCreated();

samples/client/petstore/csharp/generichost/latest/UseDateTimeOffset/src/Org.OpenAPITools/Model/PolymorphicProperty.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public partial class PolymorphicProperty : IValidatableObject
3434
/// Initializes a new instance of the <see cref="PolymorphicProperty" /> class.
3535
/// </summary>
3636
/// <param name="bool"></param>
37-
internal PolymorphicProperty(bool @bool)
37+
public PolymorphicProperty(bool @bool)
3838
{
3939
Bool = @bool;
4040
OnCreated();
@@ -44,7 +44,7 @@ internal PolymorphicProperty(bool @bool)
4444
/// Initializes a new instance of the <see cref="PolymorphicProperty" /> class.
4545
/// </summary>
4646
/// <param name="string"></param>
47-
internal PolymorphicProperty(string @string)
47+
public PolymorphicProperty(string @string)
4848
{
4949
String = @string;
5050
OnCreated();
@@ -54,7 +54,7 @@ internal PolymorphicProperty(string @string)
5454
/// Initializes a new instance of the <see cref="PolymorphicProperty" /> class.
5555
/// </summary>
5656
/// <param name="object"></param>
57-
internal PolymorphicProperty(Object @object)
57+
public PolymorphicProperty(Object @object)
5858
{
5959
Object = @object;
6060
OnCreated();
@@ -64,7 +64,7 @@ internal PolymorphicProperty(Object @object)
6464
/// Initializes a new instance of the <see cref="PolymorphicProperty" /> class.
6565
/// </summary>
6666
/// <param name="list"></param>
67-
internal PolymorphicProperty(List<string> list)
67+
public PolymorphicProperty(List<string> list)
6868
{
6969
List = list;
7070
OnCreated();

samples/client/petstore/csharp/generichost/net10/FormModels/src/Org.OpenAPITools/Model/OneOfString.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public partial class OneOfString : IValidatableObject
3333
/// Initializes a new instance of the <see cref="OneOfString" /> class.
3434
/// </summary>
3535
/// <param name="string"></param>
36-
internal OneOfString(string @string)
36+
public OneOfString(string @string)
3737
{
3838
String = @string;
3939
OnCreated();

samples/client/petstore/csharp/generichost/net10/FormModels/src/Org.OpenAPITools/Model/PolymorphicProperty.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public partial class PolymorphicProperty : IValidatableObject
3333
/// Initializes a new instance of the <see cref="PolymorphicProperty" /> class.
3434
/// </summary>
3535
/// <param name="bool"></param>
36-
internal PolymorphicProperty(bool @bool)
36+
public PolymorphicProperty(bool @bool)
3737
{
3838
Bool = @bool;
3939
OnCreated();
@@ -43,7 +43,7 @@ internal PolymorphicProperty(bool @bool)
4343
/// Initializes a new instance of the <see cref="PolymorphicProperty" /> class.
4444
/// </summary>
4545
/// <param name="string"></param>
46-
internal PolymorphicProperty(string @string)
46+
public PolymorphicProperty(string @string)
4747
{
4848
String = @string;
4949
OnCreated();
@@ -53,7 +53,7 @@ internal PolymorphicProperty(string @string)
5353
/// Initializes a new instance of the <see cref="PolymorphicProperty" /> class.
5454
/// </summary>
5555
/// <param name="object"></param>
56-
internal PolymorphicProperty(Object @object)
56+
public PolymorphicProperty(Object @object)
5757
{
5858
Object = @object;
5959
OnCreated();
@@ -63,7 +63,7 @@ internal PolymorphicProperty(Object @object)
6363
/// Initializes a new instance of the <see cref="PolymorphicProperty" /> class.
6464
/// </summary>
6565
/// <param name="list"></param>
66-
internal PolymorphicProperty(List<string> list)
66+
public PolymorphicProperty(List<string> list)
6767
{
6868
List = list;
6969
OnCreated();

samples/client/petstore/csharp/generichost/net10/NullReferenceTypes/src/Org.OpenAPITools/Model/OneOfString.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public partial class OneOfString : IValidatableObject
3535
/// Initializes a new instance of the <see cref="OneOfString" /> class.
3636
/// </summary>
3737
/// <param name="string"></param>
38-
internal OneOfString(string @string)
38+
public OneOfString(string @string)
3939
{
4040
String = @string;
4141
OnCreated();

samples/client/petstore/csharp/generichost/net10/NullReferenceTypes/src/Org.OpenAPITools/Model/PolymorphicProperty.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public partial class PolymorphicProperty : IValidatableObject
3535
/// Initializes a new instance of the <see cref="PolymorphicProperty" /> class.
3636
/// </summary>
3737
/// <param name="bool"></param>
38-
internal PolymorphicProperty(bool @bool)
38+
public PolymorphicProperty(bool @bool)
3939
{
4040
Bool = @bool;
4141
OnCreated();
@@ -45,7 +45,7 @@ internal PolymorphicProperty(bool @bool)
4545
/// Initializes a new instance of the <see cref="PolymorphicProperty" /> class.
4646
/// </summary>
4747
/// <param name="string"></param>
48-
internal PolymorphicProperty(string @string)
48+
public PolymorphicProperty(string @string)
4949
{
5050
String = @string;
5151
OnCreated();
@@ -55,7 +55,7 @@ internal PolymorphicProperty(string @string)
5555
/// Initializes a new instance of the <see cref="PolymorphicProperty" /> class.
5656
/// </summary>
5757
/// <param name="object"></param>
58-
internal PolymorphicProperty(Object @object)
58+
public PolymorphicProperty(Object @object)
5959
{
6060
Object = @object;
6161
OnCreated();
@@ -65,7 +65,7 @@ internal PolymorphicProperty(Object @object)
6565
/// Initializes a new instance of the <see cref="PolymorphicProperty" /> class.
6666
/// </summary>
6767
/// <param name="list"></param>
68-
internal PolymorphicProperty(List<string> list)
68+
public PolymorphicProperty(List<string> list)
6969
{
7070
List = list;
7171
OnCreated();

0 commit comments

Comments
 (0)