diff --git a/CHANGELOG.md b/CHANGELOG.md
index 68607e75e9..1a4befd77e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed a bug in generation when a referenced schema in an allOf was a primitive [#5701](https://github.com/microsoft/kiota/issues/5701).
- Fixed a bug where inherited error models would be missing interface declarations. [#5888](https://github.com/microsoft/kiota/issues/5888)
+- Fixed a bug where oneOf/anyOf schemas with single references to inheritance or intersections would be missing properties. [#5921](https://github.com/microsoft/kiota/issues/5921)
## [1.21.0] - 2024-12-05
diff --git a/src/Kiota.Builder/Extensions/IListExtensions.cs b/src/Kiota.Builder/Extensions/IListExtensions.cs
new file mode 100644
index 0000000000..ec2aed7711
--- /dev/null
+++ b/src/Kiota.Builder/Extensions/IListExtensions.cs
@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+
+namespace Kiota.Builder.Extensions;
+
+public static class IListExtensions
+{
+ ///
+ /// Returns the only element of this list when it has count of exactly 1
+ ///
+ /// The contained item type.
+ /// The items.
+ /// The only element or null.
+ internal static T? OnlyOneOrDefault(this IList items) =>
+ items.Count == 1 ? items[0] : default;
+
+ ///
+ /// Adds the provided to this list.
+ ///
+ /// The contained item type.
+ /// The items.
+ /// The values to add.
+ internal static void AddRange(this IList items, IEnumerable values)
+ {
+ foreach (var item in values)
+ {
+ items.Add(item);
+ }
+ }
+}
diff --git a/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs b/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs
index cec1a3cf2b..ec9d9d2f6a 100644
--- a/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs
+++ b/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs
@@ -98,13 +98,7 @@ public static bool IsInherited(this OpenApiSchema? schema)
if (schema is null || !schema.IsInclusiveUnion(0)) return null;
var result = new OpenApiSchema(schema);
result.AnyOf.Clear();
- foreach (var subSchema in schema.AnyOf)
- {
- foreach (var property in subSchema.Properties)
- {
- result.Properties.TryAdd(property.Key, property.Value);
- }
- }
+ result.TryAddProperties(schema.AnyOf.SelectMany(static x => x.Properties));
return result;
}
@@ -113,14 +107,42 @@ public static bool IsInherited(this OpenApiSchema? schema)
if (schema is null || !schema.IsExclusiveUnion(0)) return null;
var result = new OpenApiSchema(schema);
result.OneOf.Clear();
- foreach (var subSchema in schema.OneOf)
+ result.TryAddProperties(schema.OneOf.SelectMany(static x => x.Properties));
+ return result;
+ }
+
+ internal static OpenApiSchema? MergeSingleInclusiveUnionInheritanceOrIntersectionSchemaEntries(this OpenApiSchema? schema)
+ {
+ if (schema is not null
+ && schema.IsInclusiveUnion(0)
+ && schema.AnyOf.OnlyOneOrDefault() is OpenApiSchema subSchema
+ && (subSchema.IsInherited() || subSchema.IsIntersection()))
{
- foreach (var property in subSchema.Properties)
- {
- result.Properties.TryAdd(property.Key, property.Value);
- }
+ var result = new OpenApiSchema(schema);
+ result.AnyOf.Clear();
+ result.TryAddProperties(subSchema.Properties);
+ result.AllOf.AddRange(subSchema.AllOf);
+ return result;
}
- return result;
+
+ return null;
+ }
+
+ internal static OpenApiSchema? MergeSingleExclusiveUnionInheritanceOrIntersectionSchemaEntries(this OpenApiSchema? schema)
+ {
+ if (schema is not null
+ && schema.IsExclusiveUnion(0)
+ && schema.OneOf.OnlyOneOrDefault() is OpenApiSchema subSchema
+ && (subSchema.IsInherited() || subSchema.IsIntersection()))
+ {
+ var result = new OpenApiSchema(schema);
+ result.OneOf.Clear();
+ result.TryAddProperties(subSchema.Properties);
+ result.AllOf.AddRange(subSchema.AllOf);
+ return result;
+ }
+
+ return null;
}
internal static OpenApiSchema? MergeIntersectionSchemaEntries(this OpenApiSchema? schema, HashSet? schemasToExclude = default, bool overrideIntersection = false, Func? filter = default)
@@ -144,11 +166,17 @@ public static bool IsInherited(this OpenApiSchema? schema)
else if (discriminator.Mapping?.Any() ?? false)
result.Discriminator.Mapping = discriminator.Mapping.ToDictionary(static x => x.Key, static x => x.Value);
- foreach (var propertyToMerge in entriesToMerge.SelectMany(static x => x.Properties))
+ result.TryAddProperties(entriesToMerge.SelectMany(static x => x.Properties));
+
+ return result;
+ }
+
+ internal static void TryAddProperties(this OpenApiSchema schema, IEnumerable> properties)
+ {
+ foreach (var property in properties)
{
- result.Properties.TryAdd(propertyToMerge.Key, propertyToMerge.Value);
+ schema.Properties.TryAdd(property.Key, property.Value);
}
- return result;
}
public static bool IsIntersection(this OpenApiSchema? schema)
diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs
index b4466a5bf0..450e392103 100644
--- a/src/Kiota.Builder/KiotaBuilder.cs
+++ b/src/Kiota.Builder/KiotaBuilder.cs
@@ -958,7 +958,7 @@ x.Parent is CodeIndexer ||
var parentNS = x.Parent?.Parent?.Parent as CodeNamespace;
CodeClass[] exceptions = x.Parent?.Parent is CodeClass parentClass ? [parentClass] : [];
x.TypeDefinition = parentNS?.FindChildrenByName(x.Name)
- .Except(exceptions)// the property method should not reference itself as a return type.
+ .Except(exceptions)// the property method should not reference itself as a return type.
.MinBy(shortestNamespaceOrder);
// searching down first because most request builder properties on a request builder are just sub paths on the API
if (x.TypeDefinition == null)
@@ -1833,6 +1833,18 @@ private CodeTypeBase CreateModelDeclarations(OpenApiUrlTreeNode currentNode, Ope
return CreateComposedModelDeclaration(currentNode, schema, operation, suffix, codeNamespace, isRequestBody, typeNameForInlineSchema);
}
+ // type: object with single oneOf referring to inheritance or intersection
+ if (schema.IsObjectType() && schema.MergeSingleExclusiveUnionInheritanceOrIntersectionSchemaEntries() is OpenApiSchema mergedExclusiveUnionSchema)
+ {
+ return CreateModelDeclarations(currentNode, mergedExclusiveUnionSchema, operation, parentElement, suffixForInlineSchema, response, typeNameForInlineSchema, isRequestBody);
+ }
+
+ // type: object with single anyOf referring to inheritance or intersection
+ if (schema.IsObjectType() && schema.MergeSingleInclusiveUnionInheritanceOrIntersectionSchemaEntries() is OpenApiSchema mergedInclusiveUnionSchema)
+ {
+ return CreateModelDeclarations(currentNode, mergedInclusiveUnionSchema, operation, parentElement, suffixForInlineSchema, response, typeNameForInlineSchema, isRequestBody);
+ }
+
if (schema.IsObjectType() || schema.HasAnyProperty() || schema.IsEnum() || !string.IsNullOrEmpty(schema.AdditionalProperties?.Type))
{
// no inheritance or union type, often empty definitions with only additional properties are used as property bags.
@@ -1840,7 +1852,7 @@ private CodeTypeBase CreateModelDeclarations(OpenApiUrlTreeNode currentNode, Ope
}
if (schema.IsArray() &&
- !schema.Items.IsArray()) // Only handle collections of primitives and complex types. Otherwise, multi-dimensional arrays would be recursively unwrapped undesirably to lead to incorrect serialization types.
+ !schema.Items.IsArray()) // Only handle collections of primitives and complex types. Otherwise, multi-dimensional arrays would be recursively unwrapped undesirably to lead to incorrect serialization types.
{
// collections at root
return CreateCollectionModelDeclaration(currentNode, schema, operation, codeNamespace, typeNameForInlineSchema, isRequestBody);
@@ -2230,7 +2242,7 @@ internal static void AddDiscriminatorMethod(CodeClass newClass, string discrimin
logger.LogWarning("Discriminator {ComponentKey} not found in the OpenAPI document.", componentKey);
return null;
}
- // Call CreateModelDeclarations with isViaDiscriminator=true. This is for a special case where we always generate a base class when types are referenced via a oneOf discriminator.
+ // Call CreateModelDeclarations with isViaDiscriminator=true. This is for a special case where we always generate a base class when types are referenced via a oneOf discriminator.
if (CreateModelDeclarations(currentNode, discriminatorSchema, currentOperation, GetShortestNamespace(currentNamespace, discriminatorSchema), string.Empty, null, string.Empty, false, true) is not CodeType result)
{
logger.LogWarning("Discriminator {ComponentKey} is not a valid model and points to a union type.", componentKey);
@@ -2238,7 +2250,7 @@ internal static void AddDiscriminatorMethod(CodeClass newClass, string discrimin
}
if (baseClass is not null && (result.TypeDefinition is not CodeClass codeClass || codeClass.StartBlock.Inherits is null))
{
- if (!baseClass.Equals(result.TypeDefinition))// don't log warning if the discriminator points to the base type itself as this is implicitly the default case.
+ if (!baseClass.Equals(result.TypeDefinition))// don't log warning if the discriminator points to the base type itself as this is implicitly the default case.
logger.LogWarning("Discriminator {ComponentKey} is not inherited from {ClassName}.", componentKey, baseClass.Name);
return null;
}
diff --git a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs
index a62089926f..bd7caa00c5 100644
--- a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs
+++ b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs
@@ -8619,11 +8619,266 @@ public async Task ExclusiveUnionSingleEntriesMergingAsync()
Assert.NotNull(oneProperty);
var withoutObjectClass = codeModel.FindChildByName("Component2");
- Assert.NotNull(withObjectClass);
+ Assert.NotNull(withoutObjectClass);
var twoProperty = withoutObjectClass.FindChildByName("two", false);
Assert.NotNull(twoProperty);
}
+ [Fact]
+ public async Task ExclusiveUnionInheritanceEntriesMergingAsync()
+ {
+ var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
+ await using var fs = await GetDocumentStreamAsync(
+"""
+openapi: 3.0.0
+info:
+ title: "Generator not generating oneOf if the containing schema has type: object"
+ version: "1.0.0"
+servers:
+ - url: https://mytodos.doesnotexist/
+paths:
+ /uses-components:
+ post:
+ description: Return something
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UsesComponents"
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UsesComponents"
+components:
+ schemas:
+ ExampleWithSingleOneOfWithTypeObject:
+ description: "ExampleWithSingleOneOfWithTypeObject"
+ type: object
+ oneOf:
+ - $ref: "#/components/schemas/Component1"
+ discriminator:
+ propertyName: objectType
+
+ ExampleWithSingleOneOfWithoutTypeObject:
+ description: "ExampleWithSingleOneOfWithoutTypeObject"
+ oneOf:
+ - $ref: "#/components/schemas/Component2"
+ discriminator:
+ propertyName: objectType
+
+ UsesComponents:
+ type: object
+ properties:
+ component_with_single_oneof_with_type_object:
+ $ref: "#/components/schemas/ExampleWithSingleOneOfWithTypeObject"
+ component_with_single_oneof_without_type_object:
+ $ref: "#/components/schemas/ExampleWithSingleOneOfWithoutTypeObject"
+
+ ComponentCommon:
+ description: "ComponentCommon"
+ type: object
+ required:
+ - objectType
+ properties:
+ objectType:
+ type: string
+ common:
+ type: string
+
+ Component1:
+ description: "Component1"
+ type: object
+ allOf:
+ - $ref: "#/components/schemas/ComponentCommon"
+ - type: object
+ description: "Component1Inner"
+ properties:
+ one:
+ type: string
+ properties:
+ anotherOne:
+ type: string
+
+ Component2:
+ description: "Component2"
+ type: object
+ allOf:
+ - $ref: "#/components/schemas/ComponentCommon"
+ - type: object
+ description: "Component2Inner"
+ properties:
+ two:
+ type: string
+ properties:
+ anotherTwo:
+ type: string
+""");
+ var mockLogger = new Mock>();
+ var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient);
+ var document = await builder.CreateOpenApiDocumentAsync(fs);
+ var node = builder.CreateUriSpace(document);
+ var codeModel = builder.CreateSourceModel(node);
+
+ // Verify that all three classes referenced by the discriminator inherit from baseDirectoryObject
+ var withObjectClass = codeModel.FindChildByName("ExampleWithSingleOneOfWithTypeObject");
+ Assert.NotNull(withObjectClass);
+ // ExampleWithSingleOneOfWithTypeObject inherits from ComponentCommon
+ Assert.Equal("ComponentCommon", withObjectClass.BaseClass?.Name);
+ var withObjectClassOneProperty = withObjectClass.FindChildByName("one", false);
+ Assert.NotNull(withObjectClassOneProperty);
+ var withObjectClassAnotherOneProperty = withObjectClass.FindChildByName("anotherOne", false);
+ Assert.NotNull(withObjectClassAnotherOneProperty);
+
+ var withoutObjectClass = codeModel.FindChildByName("Component2");
+ Assert.NotNull(withoutObjectClass);
+ // Component2 inherits from ComponentCommon
+ Assert.Equal("ComponentCommon", withoutObjectClass.BaseClass?.Name);
+ var withoutObjectClassTwoProperty = withoutObjectClass.FindChildByName("two", false);
+ Assert.NotNull(withoutObjectClassTwoProperty);
+ var withoutObjectClassClassAnotherTwoProperty = withoutObjectClass.FindChildByName("anotherTwo", false);
+ Assert.NotNull(withoutObjectClassClassAnotherTwoProperty);
+ }
+
+ [Fact]
+ public async Task ExclusiveUnionIntersectionEntriesMergingAsync()
+ {
+ var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
+ await using var fs = await GetDocumentStreamAsync(
+"""
+openapi: 3.0.0
+info:
+ title: "Generator not generating oneOf if the containing schema has type: object"
+ version: "1.0.0"
+servers:
+ - url: https://mytodos.doesnotexist/
+paths:
+ /uses-components:
+ post:
+ description: Return something
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UsesComponents"
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UsesComponents"
+components:
+ schemas:
+ ExampleWithSingleOneOfWithTypeObject:
+ description: "ExampleWithSingleOneOfWithTypeObject"
+ type: object
+ oneOf:
+ - $ref: "#/components/schemas/Component1"
+ discriminator:
+ propertyName: objectType
+
+ ExampleWithSingleOneOfWithoutTypeObject:
+ description: "ExampleWithSingleOneOfWithoutTypeObject"
+ oneOf:
+ - $ref: "#/components/schemas/Component2"
+ discriminator:
+ propertyName: objectType
+
+ UsesComponents:
+ type: object
+ properties:
+ component_with_single_oneof_with_type_object:
+ $ref: "#/components/schemas/ExampleWithSingleOneOfWithTypeObject"
+ component_with_single_oneof_without_type_object:
+ $ref: "#/components/schemas/ExampleWithSingleOneOfWithoutTypeObject"
+
+ ComponentCommon:
+ description: "ComponentCommon"
+ type: object
+ required:
+ - objectType
+ properties:
+ objectType:
+ type: string
+ common:
+ type: string
+
+ ComponentCommon2:
+ description: "ComponentCommon2"
+ type: object
+ properties:
+ common2:
+ type: string
+
+ Component1:
+ description: "Component1"
+ type: object
+ allOf:
+ - $ref: "#/components/schemas/ComponentCommon"
+ - $ref: "#/components/schemas/ComponentCommon2"
+ - type: object
+ description: "Component1Self"
+ properties:
+ one:
+ type: string
+ properties:
+ anotherOne:
+ type: string
+
+ Component2:
+ description: "Component2"
+ type: object
+ required:
+ - objectType
+ allOf:
+ - $ref: "#/components/schemas/ComponentCommon"
+ - $ref: "#/components/schemas/ComponentCommon2"
+ - type: object
+ description: "Component2Self"
+ properties:
+ two:
+ type: string
+ properties:
+ anotherTwo:
+ type: string
+""");
+ var mockLogger = new Mock>();
+ var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient);
+ var document = await builder.CreateOpenApiDocumentAsync(fs);
+ var node = builder.CreateUriSpace(document);
+ var codeModel = builder.CreateSourceModel(node);
+
+ // Verify both scenarios have all the properties available from all schemas
+ var withObjectClass = codeModel.FindChildByName("ExampleWithSingleOneOfWithTypeObject");
+ Assert.NotNull(withObjectClass);
+ var withObjectClassOneProperty = withObjectClass.FindChildByName("one", false);
+ Assert.NotNull(withObjectClassOneProperty);
+ var withObjectClassCommonProperty = withObjectClass.FindChildByName("common", false);
+ Assert.NotNull(withObjectClassCommonProperty);
+ var withObjectClassCommon2Property = withObjectClass.FindChildByName("common2", false);
+ Assert.NotNull(withObjectClassCommon2Property);
+ var withObjectClassObjectTypeProperty = withObjectClass.FindChildByName("objectType", false);
+ Assert.NotNull(withObjectClassObjectTypeProperty);
+ var withObjectClassAnotherOneProperty = withObjectClass.FindChildByName("anotherOne", false);
+ Assert.NotNull(withObjectClassAnotherOneProperty);
+
+ var withoutObjectClass = codeModel.FindChildByName("Component2");
+ Assert.NotNull(withoutObjectClass);
+ var withoutObjectClassTwoProperty = withoutObjectClass.FindChildByName("two", false);
+ Assert.NotNull(withoutObjectClassTwoProperty);
+ var withoutObjectClassCommonProperty = withoutObjectClass.FindChildByName("common", false);
+ Assert.NotNull(withoutObjectClassCommonProperty );
+ var withoutObjectClassCommon2Property = withoutObjectClass.FindChildByName("common2", false);
+ Assert.NotNull(withoutObjectClassCommon2Property);
+ var withoutObjectClassObjectTypeProperty = withoutObjectClass.FindChildByName("objectType", false);
+ Assert.NotNull(withoutObjectClassObjectTypeProperty);
+ var withoutObjectClassClassAnotherTwoProperty = withoutObjectClass.FindChildByName("anotherTwo", false);
+ Assert.NotNull(withoutObjectClassClassAnotherTwoProperty);
+ }
+
[Fact]
public async Task InclusiveUnionSingleEntriesMergingAsync()
{
@@ -8712,6 +8967,261 @@ public async Task InclusiveUnionSingleEntriesMergingAsync()
Assert.NotNull(twoProperty);
}
+ [Fact]
+ public async Task InclusiveUnionInheritanceEntriesMergingAsync()
+ {
+ var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
+ await using var fs = await GetDocumentStreamAsync(
+"""
+openapi: 3.0.0
+info:
+ title: "Generator not generating oneOf if the containing schema has type: object"
+ version: "1.0.0"
+servers:
+ - url: https://mytodos.doesnotexist/
+paths:
+ /uses-components:
+ post:
+ description: Return something
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UsesComponents"
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UsesComponents"
+components:
+ schemas:
+ ExampleWithSingleOneOfWithTypeObject:
+ description: "ExampleWithSingleOneOfWithTypeObject"
+ type: object
+ anyOf:
+ - $ref: "#/components/schemas/Component1"
+ discriminator:
+ propertyName: objectType
+
+ ExampleWithSingleOneOfWithoutTypeObject:
+ description: "ExampleWithSingleOneOfWithoutTypeObject"
+ anyOf:
+ - $ref: "#/components/schemas/Component2"
+ discriminator:
+ propertyName: objectType
+
+ UsesComponents:
+ type: object
+ properties:
+ component_with_single_oneof_with_type_object:
+ $ref: "#/components/schemas/ExampleWithSingleOneOfWithTypeObject"
+ component_with_single_oneof_without_type_object:
+ $ref: "#/components/schemas/ExampleWithSingleOneOfWithoutTypeObject"
+
+ ComponentCommon:
+ description: "ComponentCommon"
+ type: object
+ required:
+ - objectType
+ properties:
+ objectType:
+ type: string
+ common:
+ type: string
+
+ Component1:
+ description: "Component1"
+ type: object
+ allOf:
+ - $ref: "#/components/schemas/ComponentCommon"
+ - type: object
+ description: "Component1Inner"
+ properties:
+ one:
+ type: string
+ properties:
+ anotherOne:
+ type: string
+
+ Component2:
+ description: "Component2"
+ type: object
+ allOf:
+ - $ref: "#/components/schemas/ComponentCommon"
+ - type: object
+ description: "Component2Inner"
+ properties:
+ two:
+ type: string
+ properties:
+ anotherTwo:
+ type: string
+""");
+ var mockLogger = new Mock>();
+ var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient);
+ var document = await builder.CreateOpenApiDocumentAsync(fs);
+ var node = builder.CreateUriSpace(document);
+ var codeModel = builder.CreateSourceModel(node);
+
+ // Verify that all three classes referenced by the discriminator inherit from baseDirectoryObject
+ var withObjectClass = codeModel.FindChildByName("ExampleWithSingleOneOfWithTypeObject");
+ Assert.NotNull(withObjectClass);
+ // ExampleWithSingleOneOfWithTypeObject inherits from ComponentCommon
+ Assert.Equal("ComponentCommon", withObjectClass.BaseClass?.Name);
+ var withObjectClassOneProperty = withObjectClass.FindChildByName("one", false);
+ Assert.NotNull(withObjectClassOneProperty);
+ var withObjectClassAnotherOneProperty = withObjectClass.FindChildByName("anotherOne", false);
+ Assert.NotNull(withObjectClassAnotherOneProperty);
+
+ var withoutObjectClass = codeModel.FindChildByName("Component2");
+ Assert.NotNull(withoutObjectClass);
+ // Component2 inherits from ComponentCommon
+ Assert.Equal("ComponentCommon", withoutObjectClass.BaseClass?.Name);
+ var withoutObjectClassTwoProperty = withoutObjectClass.FindChildByName("two", false);
+ Assert.NotNull(withoutObjectClassTwoProperty);
+ var withoutObjectClassClassAnotherTwoProperty = withoutObjectClass.FindChildByName("anotherTwo", false);
+ Assert.NotNull(withoutObjectClassClassAnotherTwoProperty);
+ }
+
+ [Fact]
+ public async Task InclusiveUnionIntersectionEntriesMergingAsync()
+ {
+ var tempFilePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
+ await using var fs = await GetDocumentStreamAsync(
+"""
+openapi: 3.0.0
+info:
+ title: "Generator not generating oneOf if the containing schema has type: object"
+ version: "1.0.0"
+servers:
+ - url: https://mytodos.doesnotexist/
+paths:
+ /uses-components:
+ post:
+ description: Return something
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UsesComponents"
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/UsesComponents"
+components:
+ schemas:
+ ExampleWithSingleOneOfWithTypeObject:
+ description: "ExampleWithSingleOneOfWithTypeObject"
+ type: object
+ anyOf:
+ - $ref: "#/components/schemas/Component1"
+ discriminator:
+ propertyName: objectType
+
+ ExampleWithSingleOneOfWithoutTypeObject:
+ description: "ExampleWithSingleOneOfWithoutTypeObject"
+ anyOf:
+ - $ref: "#/components/schemas/Component2"
+ discriminator:
+ propertyName: objectType
+
+ UsesComponents:
+ type: object
+ properties:
+ component_with_single_oneof_with_type_object:
+ $ref: "#/components/schemas/ExampleWithSingleOneOfWithTypeObject"
+ component_with_single_oneof_without_type_object:
+ $ref: "#/components/schemas/ExampleWithSingleOneOfWithoutTypeObject"
+
+ ComponentCommon:
+ description: "ComponentCommon"
+ type: object
+ required:
+ - objectType
+ properties:
+ objectType:
+ type: string
+ common:
+ type: string
+
+ ComponentCommon2:
+ description: "ComponentCommon2"
+ type: object
+ properties:
+ common2:
+ type: string
+
+ Component1:
+ description: "Component1"
+ type: object
+ allOf:
+ - $ref: "#/components/schemas/ComponentCommon"
+ - $ref: "#/components/schemas/ComponentCommon2"
+ - type: object
+ description: "Component1Self"
+ properties:
+ one:
+ type: string
+ properties:
+ anotherOne:
+ type: string
+
+ Component2:
+ description: "Component2"
+ type: object
+ required:
+ - objectType
+ allOf:
+ - $ref: "#/components/schemas/ComponentCommon"
+ - $ref: "#/components/schemas/ComponentCommon2"
+ - type: object
+ description: "Component2Self"
+ properties:
+ two:
+ type: string
+ properties:
+ anotherTwo:
+ type: string
+""");
+ var mockLogger = new Mock>();
+ var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient);
+ var document = await builder.CreateOpenApiDocumentAsync(fs);
+ var node = builder.CreateUriSpace(document);
+ var codeModel = builder.CreateSourceModel(node);
+
+ // Verify both scenarios have all the properties available from all schemas
+ var withObjectClass = codeModel.FindChildByName("ExampleWithSingleOneOfWithTypeObject");
+ Assert.NotNull(withObjectClass);
+ var withObjectClassOneProperty = withObjectClass.FindChildByName("one", false);
+ Assert.NotNull(withObjectClassOneProperty);
+ var withObjectClassCommonProperty = withObjectClass.FindChildByName("common", false);
+ Assert.NotNull(withObjectClassCommonProperty);
+ var withObjectClassCommon2Property = withObjectClass.FindChildByName("common2", false);
+ Assert.NotNull(withObjectClassCommon2Property);
+ var withObjectClassObjectTypeProperty = withObjectClass.FindChildByName("objectType", false);
+ Assert.NotNull(withObjectClassObjectTypeProperty);
+ var withObjectClassAnotherOneProperty = withObjectClass.FindChildByName("anotherOne", false);
+ Assert.NotNull(withObjectClassAnotherOneProperty);
+
+ var withoutObjectClass = codeModel.FindChildByName("Component2");
+ Assert.NotNull(withoutObjectClass);
+ var withoutObjectClassTwoProperty = withoutObjectClass.FindChildByName("two", false);
+ Assert.NotNull(withoutObjectClassTwoProperty);
+ var withoutObjectClassCommonProperty = withoutObjectClass.FindChildByName("common", false);
+ Assert.NotNull(withoutObjectClassCommonProperty );
+ var withoutObjectClassCommon2Property = withoutObjectClass.FindChildByName("common2", false);
+ Assert.NotNull(withoutObjectClassCommon2Property);
+ var withoutObjectClassObjectTypeProperty = withoutObjectClass.FindChildByName("objectType", false);
+ Assert.NotNull(withoutObjectClassObjectTypeProperty);
+ var withoutObjectClassClassAnotherTwoProperty = withoutObjectClass.FindChildByName("anotherTwo", false);
+ Assert.NotNull(withoutObjectClassClassAnotherTwoProperty);
+ }
+
[Fact]
public async Task NestedIntersectionTypeAllOfAsync()
{
@@ -9022,14 +9532,14 @@ public async Task InheritanceWithAllOfWith3Parts1Schema2InlineAsync(bool reverse
'#microsoft.graph.group': '#/components/schemas/microsoft.graph.group'
microsoft.graph.group:
allOf:"
- + (reverseOrder ? "" : @"
+ + (reverseOrder ? "" : @"
- '$ref': '#/components/schemas/microsoft.graph.directoryObject'") + @"
- properties:
groupprop1:
type: 'string'
- properties:
groupprop2:
- type: 'string'" + (!reverseOrder ? "" : @"
+ type: 'string'" + (!reverseOrder ? "" : @"
- '$ref': '#/components/schemas/microsoft.graph.directoryObject'"));
var mockLogger = new Mock>();
var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient);