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 @@ -2802,7 +2802,12 @@ protected void updateModelForComposedSchema(CodegenModel m, Schema schema, Map<S
addImport(composed, refSchema, m, modelName);

if (allDefinitions != null && refSchema != null) {
if (allParents.contains(ref) && supportsMultipleInheritance) {
if (ModelUtils.hasOneOf(refSchema)) {
// Do not flatten oneOf variant properties into this model.
// addProperties() would recurse into each variant and merge all
// their properties, producing an impossible conjunction. The oneOf
// ref is already tracked via m.interfaces.
} else if (allParents.contains(ref) && supportsMultipleInheritance) {
// multiple inheritance
addProperties(allProperties, allRequired, refSchema, new HashSet<>());
} else if (parentName != null && parentName.equals(ref) && supportsInheritance) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,11 @@ public static String getParentName(Schema composedSchema, Map<String, Schema> al
} else if (hasOrInheritsDiscriminator(s, allSchemas, new ArrayList<Schema>())) {
// discriminator.propertyName is used or x-parent is used
parentNameCandidates.add(parentName);
} else if (hasOneOf(s)) {
// a $ref to a oneOf schema is treated as a parent even without
// a discriminator, to avoid flattening union type variants into
// an impossible conjunction of all variant properties
parentNameCandidates.add(parentName);
} else {
// not a parent since discriminator.propertyName or x-parent is not set
hasAmbiguousParents = true;
Expand Down Expand Up @@ -1658,6 +1663,13 @@ private static List<String> getAllParentsName(
if (includeAncestors && isComposedSchema(s)) {
names.addAll(getAllParentsName(s, allSchemas, true, seenNames));
}
} else if (hasOneOf(s)) {
// a $ref to a oneOf schema is treated as a parent even without
// a discriminator
names.add(parentName);
if (includeAncestors && isComposedSchema(s)) {
names.addAll(getAllParentsName(s, allSchemas, true, seenNames));
}
} else {
// not a parent since discriminator.propertyName is not set
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1171,6 +1171,51 @@ public void testAllOfRequired() {
assertEquals(getRequiredVars(childModel), Collections.singletonList("name"));
}

@Test
public void testAllOfWithOneOfRefNoDiscriminator() {
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/allOf_oneOf_noDiscriminator.yaml");
DefaultCodegen codegen = new DefaultCodegen();
codegen.supportsInheritance = true;
codegen.setOpenAPI(openAPI);

// ParentC uses allOf with a $ref to Child (a oneOf without discriminator).
// Child should be recognized as a parent, not flattened.
Schema parentCSchema = openAPI.getComponents().getSchemas().get("ParentC");
CodegenModel parentCModel = codegen.fromModel("ParentC", parentCSchema);
assertEquals("Child", parentCModel.parentSchema);
assertEquals("Child", parentCModel.parent);

// Only the own property "type" should be in vars, not the oneOf variant properties
List<String> varNames = parentCModel.vars.stream().map(v -> v.name).collect(Collectors.toList());
assertTrue(varNames.contains("type"), "vars should contain 'type'");
assertFalse(varNames.contains("xOnlyField"), "vars should not contain variant-specific 'xOnlyField'");
assertFalse(varNames.contains("yOnlyField"), "vars should not contain variant-specific 'yOnlyField'");
assertFalse(varNames.contains("zOnlyField"), "vars should not contain variant-specific 'zOnlyField'");

// allVars should also not contain flattened variant properties
List<String> allVarNames = parentCModel.allVars.stream().map(v -> v.name).collect(Collectors.toList());
assertFalse(allVarNames.contains("xOnlyField"), "allVars should not contain variant-specific 'xOnlyField'");
assertFalse(allVarNames.contains("yOnlyField"), "allVars should not contain variant-specific 'yOnlyField'");
assertFalse(allVarNames.contains("zOnlyField"), "allVars should not contain variant-specific 'zOnlyField'");
}

@Test
public void testAllOfWithOneOfRefNoDiscriminatorNoInheritance() {
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/allOf_oneOf_noDiscriminator.yaml");
DefaultCodegen codegen = new DefaultCodegen();
codegen.supportsInheritance = false;
codegen.setOpenAPI(openAPI);

// Even without supportsInheritance, variant properties should not be flattened
Schema parentCSchema = openAPI.getComponents().getSchemas().get("ParentC");
CodegenModel parentCModel = codegen.fromModel("ParentC", parentCSchema);

List<String> varNames = parentCModel.vars.stream().map(v -> v.name).collect(Collectors.toList());
assertFalse(varNames.contains("xOnlyField"), "vars should not contain variant-specific 'xOnlyField'");
assertFalse(varNames.contains("yOnlyField"), "vars should not contain variant-specific 'yOnlyField'");
assertFalse(varNames.contains("zOnlyField"), "vars should not contain variant-specific 'zOnlyField'");
}

@Test
public void testAllOfSingleAndDoubleRefWithOwnPropsNoDiscriminator() {
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/allOf_composition.yaml");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
openapi: 3.0.2
info:
title: allOf with oneOf (no discriminator) test
version: 1.0.0
paths:
/parent:
get:
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ParentC'
components:
schemas:
ParentC:
allOf:
- type: object
required: [type]
properties:
type:
type: string
- $ref: "#/components/schemas/Child"

Child:
oneOf:
- $ref: "#/components/schemas/ChildX"
- $ref: "#/components/schemas/ChildY"
- $ref: "#/components/schemas/ChildZ"

ChildX:
type: object
required: [kind, sharedField, xOnlyField]
properties:
kind:
type: string
sharedField:
type: string
xOnlyField:
type: string

ChildY:
type: object
required: [kind, sharedField, yOnlyField]
properties:
kind:
type: string
sharedField:
type: string
yOnlyField:
type: string

ChildZ:
type: object
required: [kind, sharedField, zOnlyField]
properties:
kind:
type: string
sharedField:
type: string
zOnlyField:
type: integer
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ The value may be a shape or the 'null' value. The 'nullable' attribute was intro

Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**ShapeType** | **string** | |

[[Back to Model list]](../../README.md#documentation-for-models) [[Back to API list]](../../README.md#documentation-for-api-endpoints) [[Back to README]](../../README.md)

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**ShapeType** | **string** | |

[[Back to Model list]](../../README.md#documentation-for-models) [[Back to API list]](../../README.md#documentation-for-api-endpoints) [[Back to README]](../../README.md)

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ The value may be a shape or the 'null' value. This is introduced in OAS schema >

Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**ShapeType** | **string** | |

[[Back to Model list]](../../README.md#documentation-for-models) [[Back to API list]](../../README.md#documentation-for-api-endpoints) [[Back to README]](../../README.md)

Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public partial class NullableShape : IValidatableObject
/// Initializes a new instance of the <see cref="NullableShape" /> class.
/// </summary>
/// <param name="triangle"></param>
public NullableShape(Triangle triangle)
internal NullableShape(Triangle triangle)
{
Triangle = triangle;
OnCreated();
Expand All @@ -44,7 +44,7 @@ public NullableShape(Triangle triangle)
/// Initializes a new instance of the <see cref="NullableShape" /> class.
/// </summary>
/// <param name="quadrilateral"></param>
public NullableShape(Quadrilateral quadrilateral)
internal NullableShape(Quadrilateral quadrilateral)
{
Quadrilateral = quadrilateral;
OnCreated();
Expand Down Expand Up @@ -117,8 +117,6 @@ public override NullableShape Read(ref Utf8JsonReader utf8JsonReader, Type typeT

JsonTokenType startingTokenType = utf8JsonReader.TokenType;

Option<string?> shapeType = default;

Quadrilateral? quadrilateral = null;
Triangle? triangle = null;

Expand Down Expand Up @@ -167,21 +165,12 @@ public override NullableShape Read(ref Utf8JsonReader utf8JsonReader, Type typeT

switch (localVarJsonPropertyName)
{
case "shapeType":
shapeType = new Option<string?>(utf8JsonReader.GetString()!);
break;
default:
break;
}
}
}

if (!shapeType.IsSet)
throw new ArgumentException("Property is required for class NullableShape.", nameof(shapeType));

if (shapeType.IsSet && shapeType.Value == null)
throw new ArgumentNullException(nameof(shapeType), "Property is not nullable for class NullableShape.");

if (quadrilateral != null)
return new NullableShape(quadrilateral);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public partial class Shape : IValidatableObject
/// Initializes a new instance of the <see cref="Shape" /> class.
/// </summary>
/// <param name="triangle"></param>
public Shape(Triangle triangle)
internal Shape(Triangle triangle)
{
Triangle = triangle;
OnCreated();
Expand All @@ -44,7 +44,7 @@ public Shape(Triangle triangle)
/// Initializes a new instance of the <see cref="Shape" /> class.
/// </summary>
/// <param name="quadrilateral"></param>
public Shape(Quadrilateral quadrilateral)
internal Shape(Quadrilateral quadrilateral)
{
Quadrilateral = quadrilateral;
OnCreated();
Expand Down Expand Up @@ -117,8 +117,6 @@ public override Shape Read(ref Utf8JsonReader utf8JsonReader, Type typeToConvert

JsonTokenType startingTokenType = utf8JsonReader.TokenType;

Option<string?> shapeType = default;

Quadrilateral? quadrilateral = null;
Triangle? triangle = null;

Expand Down Expand Up @@ -167,21 +165,12 @@ public override Shape Read(ref Utf8JsonReader utf8JsonReader, Type typeToConvert

switch (localVarJsonPropertyName)
{
case "shapeType":
shapeType = new Option<string?>(utf8JsonReader.GetString()!);
break;
default:
break;
}
}
}

if (!shapeType.IsSet)
throw new ArgumentException("Property is required for class Shape.", nameof(shapeType));

if (shapeType.IsSet && shapeType.Value == null)
throw new ArgumentNullException(nameof(shapeType), "Property is not nullable for class Shape.");

if (quadrilateral != null)
return new Shape(quadrilateral);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public partial class ShapeOrNull : IValidatableObject
/// Initializes a new instance of the <see cref="ShapeOrNull" /> class.
/// </summary>
/// <param name="triangle"></param>
public ShapeOrNull(Triangle triangle)
internal ShapeOrNull(Triangle triangle)
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: ShapeOrNull no longer has any public constructor or factory; changing the union constructors to internal makes the public model non-instantiable for SDK consumers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/csharp/generichost/latest/UseDateTimeOffset/src/Org.OpenAPITools/Model/ShapeOrNull.cs, line 37:

<comment>ShapeOrNull no longer has any public constructor or factory; changing the union constructors to internal makes the public model non-instantiable for SDK consumers.</comment>

<file context>
@@ -34,7 +34,7 @@ public partial class ShapeOrNull : IValidatableObject
         /// </summary>
         /// <param name="triangle"></param>
-        public ShapeOrNull(Triangle triangle)
+        internal ShapeOrNull(Triangle triangle)
         {
             Triangle = triangle;
</file context>
Fix with Cubic

{
Triangle = triangle;
OnCreated();
Expand All @@ -44,7 +44,7 @@ public ShapeOrNull(Triangle triangle)
/// Initializes a new instance of the <see cref="ShapeOrNull" /> class.
/// </summary>
/// <param name="quadrilateral"></param>
public ShapeOrNull(Quadrilateral quadrilateral)
internal ShapeOrNull(Quadrilateral quadrilateral)
{
Quadrilateral = quadrilateral;
OnCreated();
Expand Down Expand Up @@ -117,8 +117,6 @@ public override ShapeOrNull Read(ref Utf8JsonReader utf8JsonReader, Type typeToC

JsonTokenType startingTokenType = utf8JsonReader.TokenType;

Option<string?> shapeType = default;

Quadrilateral? quadrilateral = null;
Triangle? triangle = null;

Expand Down Expand Up @@ -167,21 +165,12 @@ public override ShapeOrNull Read(ref Utf8JsonReader utf8JsonReader, Type typeToC

switch (localVarJsonPropertyName)
{
case "shapeType":
shapeType = new Option<string?>(utf8JsonReader.GetString()!);
break;
default:
break;
}
}
}

if (!shapeType.IsSet)
throw new ArgumentException("Property is required for class ShapeOrNull.", nameof(shapeType));

if (shapeType.IsSet && shapeType.Value == null)
throw new ArgumentNullException(nameof(shapeType), "Property is not nullable for class ShapeOrNull.");

if (quadrilateral != null)
return new ShapeOrNull(quadrilateral);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ The value may be a shape or the 'null' value. The 'nullable' attribute was intro

Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**ShapeType** | **string** | |

[[Back to Model list]](../../README.md#documentation-for-models) [[Back to API list]](../../README.md#documentation-for-api-endpoints) [[Back to README]](../../README.md)

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**ShapeType** | **string** | |

[[Back to Model list]](../../README.md#documentation-for-models) [[Back to API list]](../../README.md#documentation-for-api-endpoints) [[Back to README]](../../README.md)

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ The value may be a shape or the 'null' value. This is introduced in OAS schema >

Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**ShapeType** | **string** | |

[[Back to Model list]](../../README.md#documentation-for-models) [[Back to API list]](../../README.md#documentation-for-api-endpoints) [[Back to README]](../../README.md)

Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public partial class NullableShape : IValidatableObject
/// Initializes a new instance of the <see cref="NullableShape" /> class.
/// </summary>
/// <param name="triangle"></param>
public NullableShape(Triangle triangle)
internal NullableShape(Triangle triangle)
{
Triangle = triangle;
OnCreated();
Expand All @@ -43,7 +43,7 @@ public NullableShape(Triangle triangle)
/// Initializes a new instance of the <see cref="NullableShape" /> class.
/// </summary>
/// <param name="quadrilateral"></param>
public NullableShape(Quadrilateral quadrilateral)
internal NullableShape(Quadrilateral quadrilateral)
{
Quadrilateral = quadrilateral;
OnCreated();
Expand Down Expand Up @@ -123,8 +123,6 @@ public override NullableShape Read(ref Utf8JsonReader utf8JsonReader, Type typeT

JsonTokenType startingTokenType = utf8JsonReader.TokenType;

Option<string> shapeType = default;

Quadrilateral quadrilateral = null;
Triangle triangle = null;

Expand Down Expand Up @@ -173,21 +171,12 @@ public override NullableShape Read(ref Utf8JsonReader utf8JsonReader, Type typeT

switch (localVarJsonPropertyName)
{
case "shapeType":
shapeType = new Option<string>(utf8JsonReader.GetString());
break;
default:
break;
}
}
}

if (!shapeType.IsSet)
throw new ArgumentException("Property is required for class NullableShape.", nameof(shapeType));

if (shapeType.IsSet && shapeType.Value == null)
throw new ArgumentNullException(nameof(shapeType), "Property is not nullable for class NullableShape.");

if (quadrilateral != null)
return new NullableShape(quadrilateral);

Expand Down
Loading