Skip to content

Commit 492ef4a

Browse files
Preserve oneOf wrapper's own properties; keep union constructors public
1 parent 2cf1fdb commit 492ef4a

94 files changed

Lines changed: 282 additions & 208 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
@@ -2802,23 +2802,31 @@ protected void updateModelForComposedSchema(CodegenModel m, Schema schema, Map<S
28022802
addImport(composed, refSchema, m, modelName);
28032803

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

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
@@ -707,6 +707,13 @@ private boolean modelIsMutable(CodegenModel model, Set<String> processed) {
707707
processed = new HashSet<String>();
708708
}
709709
boolean isMutable = model.allVars.stream().anyMatch(v -> !v.isReadOnly);
710+
// oneOf/anyOf union wrappers expose a constructor per variant; the variants
711+
// are not tracked in allVars, so fall back to the composed schema lists.
712+
if (!isMutable && model.getComposedSchemas() != null) {
713+
List<CodegenProperty> oneOf = model.getComposedSchemas().getOneOf();
714+
List<CodegenProperty> anyOf = model.getComposedSchemas().getAnyOf();
715+
isMutable = (oneOf != null && !oneOf.isEmpty()) || (anyOf != null && !anyOf.isEmpty());
716+
}
710717
if (!isMutable && !processed.contains(model.classname) && model.getDiscriminator() != null && model.getDiscriminator().getMappedModels() != null) {
711718
processed.add(model.classname);
712719
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
@@ -1216,6 +1216,34 @@ public void testAllOfWithOneOfRefNoDiscriminatorNoInheritance() {
12161216
assertFalse(varNames.contains("zOnlyField"), "vars should not contain variant-specific 'zOnlyField'");
12171217
}
12181218

1219+
@Test
1220+
public void testAllOfWithOneOfRefPreservesWrapperProperties() {
1221+
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/allOf_oneOf_noDiscriminator.yaml");
1222+
DefaultCodegen codegen = new DefaultCodegen();
1223+
codegen.supportsInheritance = true;
1224+
codegen.setOpenAPI(openAPI);
1225+
1226+
// ChildWithSharedFields is a oneOf wrapper that also declares its own
1227+
// properties (wrapperOptionalField, wrapperRequiredField). When ParentWithSharedFields
1228+
// inherits from it via allOf, those wrapper-level properties must still flow
1229+
// through to allProperties/allRequired — only the variant-specific properties
1230+
// should be suppressed.
1231+
Schema parentSchema = openAPI.getComponents().getSchemas().get("ParentWithSharedFields");
1232+
CodegenModel parentModel = codegen.fromModel("ParentWithSharedFields", parentSchema);
1233+
1234+
List<String> allVarNames = parentModel.allVars.stream().map(v -> v.name).collect(Collectors.toList());
1235+
assertTrue(allVarNames.contains("wrapperOptionalField"), "allVars should contain wrapper-level 'wrapperOptionalField'");
1236+
assertTrue(allVarNames.contains("wrapperRequiredField"), "allVars should contain wrapper-level 'wrapperRequiredField'");
1237+
assertTrue(allVarNames.contains("extraField"), "allVars should contain inline allOf 'extraField'");
1238+
assertFalse(allVarNames.contains("xOnlyField"), "allVars should not contain variant-specific 'xOnlyField'");
1239+
assertFalse(allVarNames.contains("yOnlyField"), "allVars should not contain variant-specific 'yOnlyField'");
1240+
assertFalse(allVarNames.contains("zOnlyField"), "allVars should not contain variant-specific 'zOnlyField'");
1241+
1242+
List<String> requiredVarNames = parentModel.requiredVars.stream().map(v -> v.name).collect(Collectors.toList());
1243+
assertTrue(requiredVarNames.contains("wrapperRequiredField"), "requiredVars should include wrapper-level required 'wrapperRequiredField'");
1244+
assertTrue(requiredVarNames.contains("extraField"), "requiredVars should include inline allOf required 'extraField'");
1245+
}
1246+
12191247
@Test
12201248
public void testAllOfSingleAndDoubleRefWithOwnPropsNoDiscriminator() {
12211249
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/NullableShape.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public partial class NullableShape : IValidatableObject
3434
/// Initializes a new instance of the <see cref="NullableShape" /> class.
3535
/// </summary>
3636
/// <param name="triangle"></param>
37-
internal NullableShape(Triangle triangle)
37+
public NullableShape(Triangle triangle)
3838
{
3939
Triangle = triangle;
4040
OnCreated();
@@ -44,7 +44,7 @@ internal NullableShape(Triangle triangle)
4444
/// Initializes a new instance of the <see cref="NullableShape" /> class.
4545
/// </summary>
4646
/// <param name="quadrilateral"></param>
47-
internal NullableShape(Quadrilateral quadrilateral)
47+
public NullableShape(Quadrilateral quadrilateral)
4848
{
4949
Quadrilateral = quadrilateral;
5050
OnCreated();

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/latest/UseDateTimeOffset/src/Org.OpenAPITools/Model/Shape.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public partial class Shape : IValidatableObject
3434
/// Initializes a new instance of the <see cref="Shape" /> class.
3535
/// </summary>
3636
/// <param name="triangle"></param>
37-
internal Shape(Triangle triangle)
37+
public Shape(Triangle triangle)
3838
{
3939
Triangle = triangle;
4040
OnCreated();
@@ -44,7 +44,7 @@ internal Shape(Triangle triangle)
4444
/// Initializes a new instance of the <see cref="Shape" /> class.
4545
/// </summary>
4646
/// <param name="quadrilateral"></param>
47-
internal Shape(Quadrilateral quadrilateral)
47+
public Shape(Quadrilateral quadrilateral)
4848
{
4949
Quadrilateral = quadrilateral;
5050
OnCreated();

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public partial class ShapeOrNull : IValidatableObject
3434
/// Initializes a new instance of the <see cref="ShapeOrNull" /> class.
3535
/// </summary>
3636
/// <param name="triangle"></param>
37-
internal ShapeOrNull(Triangle triangle)
37+
public ShapeOrNull(Triangle triangle)
3838
{
3939
Triangle = triangle;
4040
OnCreated();
@@ -44,7 +44,7 @@ internal ShapeOrNull(Triangle triangle)
4444
/// Initializes a new instance of the <see cref="ShapeOrNull" /> class.
4545
/// </summary>
4646
/// <param name="quadrilateral"></param>
47-
internal ShapeOrNull(Quadrilateral quadrilateral)
47+
public ShapeOrNull(Quadrilateral quadrilateral)
4848
{
4949
Quadrilateral = quadrilateral;
5050
OnCreated();

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public partial class NullableShape : IValidatableObject
3333
/// Initializes a new instance of the <see cref="NullableShape" /> class.
3434
/// </summary>
3535
/// <param name="triangle"></param>
36-
internal NullableShape(Triangle triangle)
36+
public NullableShape(Triangle triangle)
3737
{
3838
Triangle = triangle;
3939
OnCreated();
@@ -43,7 +43,7 @@ internal NullableShape(Triangle triangle)
4343
/// Initializes a new instance of the <see cref="NullableShape" /> class.
4444
/// </summary>
4545
/// <param name="quadrilateral"></param>
46-
internal NullableShape(Quadrilateral quadrilateral)
46+
public NullableShape(Quadrilateral quadrilateral)
4747
{
4848
Quadrilateral = quadrilateral;
4949
OnCreated();

0 commit comments

Comments
 (0)