From 7dd639e573799833b3a7c20c51a92e2402995af9 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 14 Mar 2022 13:39:40 -0400 Subject: [PATCH] - fixes #1270 a bug where missing schema title would make union types fail --- CHANGELOG.md | 2 + .../Extensions/OpenApiSchemaExtensions.cs | 6 +- src/Kiota.Builder/KiotaBuilder.cs | 7 + .../Kiota.Builder.Tests/KiotaBuilderTests.cs | 143 ++++++++++++++++++ 4 files changed, 155 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0efdf57dcb..9612d77816 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed a bug where unnecessary namespaces would be added to models generation #1273 - Fixed a bug where Go byte arrays would not write deserializers properly. - Fixed a bug where integers would not be recognized when type is not number. +- Fixed a bug where union types with primitive member types would fail to generate #1270 +- Fixed a bug where union types with inline schema member types would fail to generate #1270 ## [0.0.18] - 2022-03-14 diff --git a/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs b/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs index cdb967e700..211780080d 100644 --- a/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs +++ b/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs @@ -13,7 +13,7 @@ public static IEnumerable GetSchemaTitles(this OpenApiSchema schema) { else if(schema.Items != null) return schema.Items.GetSchemaTitles(); else if(!string.IsNullOrEmpty(schema.Title)) - return new List{ schema.Title }; + return new string[] { schema.Title }; else if(schema.AnyOf.Any()) return schema.AnyOf.FlattenIfRequired(classNamesFlattener); else if(schema.AllOf.Any()) @@ -21,9 +21,9 @@ public static IEnumerable GetSchemaTitles(this OpenApiSchema schema) { else if(schema.OneOf.Any()) return schema.OneOf.FlattenIfRequired(classNamesFlattener); else if(!string.IsNullOrEmpty(schema.Reference?.Id)) - return new List{schema.Reference.Id.Split('/').Last().Split('.').Last()}; + return new string[] {schema.Reference.Id.Split('/').Last().Split('.').Last()}; else if(!string.IsNullOrEmpty(schema.Xml?.Name)) - return new List{schema.Xml.Name}; + return new string[] {schema.Xml.Name}; else return Enumerable.Empty(); } public static IEnumerable GetNonEmptySchemas(this OpenApiSchema schema) { diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index 0aa2b138ae..acf595d35e 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -810,12 +810,19 @@ private CodeTypeBase CreateUnionModelDeclaration(OpenApiUrlTreeNode currentNode, var unionType = new CodeUnionType { Name = currentNode.GetClassName(operation: operation, suffix: suffixForInlineSchema, schema: schema), }; + var membersWithNoName = 0; foreach(var currentSchema in schemas) { var shortestNamespaceName = currentSchema.Reference == null ? currentNode.GetNodeNamespaceFromPath(config.ClientNamespaceName) : GetModelsNamespaceNameFromReferenceId(currentSchema.Reference.Id); var shortestNamespace = rootNamespace.FindNamespaceByName(shortestNamespaceName); if(shortestNamespace == null) shortestNamespace = rootNamespace.AddNamespace(shortestNamespaceName); var className = currentSchema.GetSchemaTitle(); + if (string.IsNullOrEmpty(className)) + if(GetPrimitiveType(currentSchema) is CodeType primitiveType && !string.IsNullOrEmpty(primitiveType.Name)) { + unionType.AddType(primitiveType); + continue; + } else + className = $"{unionType.Name}Member{++membersWithNoName}"; var codeDeclaration = AddModelDeclarationIfDoesntExist(currentNode, currentSchema, className, shortestNamespace); unionType.AddType(new CodeType { TypeDefinition = codeDeclaration, diff --git a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs index c0b2f33345..0789d91db0 100644 --- a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs @@ -970,6 +970,149 @@ public void AddsDiscriminatorMappings(){ Assert.NotNull(castType.TypeDefinition); Assert.Equal(directoryObjectClass, castType.TypeDefinition); } + [Fact] + public void UnionOfPrimitiveTypesWorks() { + var simpleObjet = new OpenApiSchema { + Type = "object", + Properties = new Dictionary { + { + "id", new OpenApiSchema { + Type = "string" + } + } + }, + Reference = new OpenApiReference { + Id = "subNS.simpleObject", + Type = ReferenceType.Schema + }, + UnresolvedReference = false + }; + var document = new OpenApiDocument() { + Paths = new OpenApiPaths() { + ["unionType"] = new OpenApiPathItem() { + Operations = { + [OperationType.Get] = new OpenApiOperation() { + Responses = new OpenApiResponses + { + ["200"] = new OpenApiResponse { + Content = { + ["application/json"] = new OpenApiMediaType { + Schema = new OpenApiSchema { + OneOf = new List { + simpleObjet, + new OpenApiSchema { + Type = "number" + } + } + } + } + } + }, + } + } + } + } + }, + Components = new OpenApiComponents() { + Schemas = new Dictionary { + { + "subNS.simpleObject", simpleObjet + } + } + }, + }; + var mockLogger = new Mock>(); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration() { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var node = builder.CreateUriSpace(document); + var codeModel = builder.CreateSourceModel(node); + var requestBuilderNS = codeModel.FindNamespaceByName("ApiSdk.unionType"); + Assert.NotNull(requestBuilderNS); + var requestBuilderClass = requestBuilderNS.FindChildByName("unionTypeRequestBuilder", false); + Assert.NotNull(requestBuilderClass); + var requestExecutorMethod = requestBuilderClass.Methods.FirstOrDefault(x => x.IsOfKind(CodeMethodKind.RequestExecutor)); + Assert.NotNull(requestExecutorMethod); + var executorReturnType = requestExecutorMethod.ReturnType as CodeUnionType; + Assert.NotNull(executorReturnType); + Assert.Equal(2, executorReturnType.Types.Count()); + var typeNames = executorReturnType.Types.Select(x => x.Name).ToHashSet(StringComparer.OrdinalIgnoreCase); + Assert.Contains("simpleObject", typeNames); + Assert.Contains("number", typeNames); + } + [Fact] + public void UnionOfInlineSchemasWorks() { + var simpleObjet = new OpenApiSchema { + Type = "object", + Properties = new Dictionary { + { + "id", new OpenApiSchema { + Type = "string" + } + } + }, + Reference = new OpenApiReference { + Id = "subNS.simpleObject", + Type = ReferenceType.Schema + }, + UnresolvedReference = false + }; + var document = new OpenApiDocument() { + Paths = new OpenApiPaths() { + ["unionType"] = new OpenApiPathItem() { + Operations = { + [OperationType.Get] = new OpenApiOperation() { + Responses = new OpenApiResponses + { + ["200"] = new OpenApiResponse { + Content = { + ["application/json"] = new OpenApiMediaType { + Schema = new OpenApiSchema { + OneOf = new List { + simpleObjet, + new OpenApiSchema { + Type = "object", + Properties = new Dictionary { + { + "name", new OpenApiSchema { + Type = "string" + } + } + } + } + } + } + } + } + }, + } + } + } + } + }, + Components = new OpenApiComponents() { + Schemas = new Dictionary { + { + "subNS.simpleObject", simpleObjet + } + } + }, + }; + var mockLogger = new Mock>(); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration() { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }); + var node = builder.CreateUriSpace(document); + var codeModel = builder.CreateSourceModel(node); + var requestBuilderNS = codeModel.FindNamespaceByName("ApiSdk.unionType"); + Assert.NotNull(requestBuilderNS); + var requestBuilderClass = requestBuilderNS.FindChildByName("unionTypeRequestBuilder", false); + Assert.NotNull(requestBuilderClass); + var requestExecutorMethod = requestBuilderClass.Methods.FirstOrDefault(x => x.IsOfKind(CodeMethodKind.RequestExecutor)); + Assert.NotNull(requestExecutorMethod); + var executorReturnType = requestExecutorMethod.ReturnType as CodeUnionType; + Assert.NotNull(executorReturnType); + Assert.Equal(2, executorReturnType.Types.Count()); + var typeNames = executorReturnType.Types.Select(x => x.Name).ToHashSet(StringComparer.OrdinalIgnoreCase); + Assert.Contains("simpleObject", typeNames); + Assert.Contains("unionTypeResponseMember1", typeNames); + } [InlineData("string", "", "string")]// https://spec.openapis.org/registry/format/ [InlineData("string", "commonmark", "string")] [InlineData("string", "html", "string")]