From 4dabafd856749d2236a57d5a6d873cc7c59c3410 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 5 Sep 2024 15:48:06 -0400 Subject: [PATCH 1/3] fix: missing array of primitive types in union or intersection types Signed-off-by: Vincent Biret --- CHANGELOG.md | 3 +- src/Kiota.Builder/KiotaBuilder.cs | 2 + .../Kiota.Builder.Tests/KiotaBuilderTests.cs | 83 +++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a168a2ea69..e5b8483364 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Fixed a bug where collection/array of primitive types members for union/intersection types would be ignored. [#5283](https://github.com/microsoft/kiota/issues/5283) + ## [1.18.0] - 2024-09-05 ### Added @@ -1442,4 +1444,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Initial GitHub release - diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs index edbfac7479..a9841f1fd9 100644 --- a/src/Kiota.Builder/KiotaBuilder.cs +++ b/src/Kiota.Builder/KiotaBuilder.cs @@ -1744,6 +1744,8 @@ private CodeTypeBase CreateComposedModelDeclaration(OpenApiUrlTreeNode currentNo if (string.IsNullOrEmpty(className)) if (GetPrimitiveType(currentSchema) is CodeType primitiveType && !string.IsNullOrEmpty(primitiveType.Name)) { + if (currentSchema.IsArray()) + primitiveType.CollectionKind = CodeTypeBase.CodeTypeCollectionKind.Complex; if (!unionType.ContainsType(primitiveType)) unionType.AddType(primitiveType); continue; diff --git a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs index 6518143681..64e2ecace8 100644 --- a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs +++ b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs @@ -2841,6 +2841,89 @@ public void SquishesLonelyNullablesBothAnyOf() Assert.Null(codeModel.FindChildByName("createUploadSessionResponseMember1")); } [Fact] + public void SupportsArraysInComposedTypes() + { + var anyOfSchema = new OpenApiSchema + { + Type = "object", + AdditionalPropertiesAllowed = false, + Properties = new Dictionary { + { + "date", new OpenApiSchema { + AnyOf = [ + new OpenApiSchema { + Type = "string", + }, + new OpenApiSchema { + Type = "array", + Items = new OpenApiSchema { + Type = "string", + }, + }, + ] + } + } + }, + Reference = new OpenApiReference + { + Id = "anyOfNullable", + Type = ReferenceType.Schema + }, + UnresolvedReference = false + }; + var document = new OpenApiDocument + { + Paths = new OpenApiPaths + { + ["createUploadSession"] = new OpenApiPathItem + { + Operations = { + [OperationType.Get] = new OpenApiOperation + { + Responses = new OpenApiResponses + { + ["200"] = new OpenApiResponse + { + Content = new Dictionary { + ["application/json"] = new OpenApiMediaType + { + Schema = anyOfSchema + } + } + } + } + } + } + } + }, + Components = new OpenApiComponents + { + Schemas = new Dictionary { + { + "anyOfNullable", anyOfSchema + } + }, + }, + }; + var mockLogger = new Mock>(); + var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", ApiRootUrl = "https://localhost" }, _httpClient); + builder.SetOpenApiDocument(document); + var node = builder.CreateUriSpace(document); + var codeModel = builder.CreateSourceModel(node); + var anyOfClass = codeModel.FindChildByName("anyOfNullable"); + Assert.NotNull(anyOfClass); + var dateProperty = anyOfClass.FindChildByName("date", false); + Assert.NotNull(dateProperty); + if (dateProperty.Type is not CodeIntersectionType unionType) + Assert.Fail("Date property type is not a union type"); + else + { + Assert.Equal(2, unionType.Types.Count()); + Assert.Contains(unionType.Types, x => x.Name.Equals("string", StringComparison.OrdinalIgnoreCase) && x.CollectionKind is CodeTypeBase.CodeTypeCollectionKind.None); + Assert.Contains(unionType.Types, x => x.Name.Equals("string", StringComparison.OrdinalIgnoreCase) && x.CollectionKind is CodeTypeBase.CodeTypeCollectionKind.Complex); + } + } + [Fact] public void SupportsNullableAnyOf() { var anyOfSchema = new OpenApiSchema From 01226480fe19b072249c43a522314a0345bf16ca Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 6 Sep 2024 09:25:13 -0400 Subject: [PATCH 2/3] fix: patches typescript composed type serialization Signed-off-by: Vincent Biret --- .../Writers/TypeScript/CodeFunctionWriter.cs | 11 ++++------- .../Writers/TypeScript/TypeScriptConventionService.cs | 4 +++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs index 5a80b74ad8..5f156ba4df 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeFunctionWriter.cs @@ -70,7 +70,7 @@ private void WriteComposedTypeDeserializer(CodeFunction codeElement, LanguageWri if (GetOriginalComposedType(composedParam) is not { } composedType) return; writer.StartBlock("return {"); - foreach (var mappedType in composedType.Types.Where(x => !IsPrimitiveType(x, composedType))) + foreach (var mappedType in composedType.Types.Where(x => !IsPrimitiveType(x, composedType, false))) { var functionName = GetDeserializerFunctionName(codeElement, mappedType); var variableName = composedParam.Name.ToFirstCharacterLowerCase(); @@ -86,12 +86,12 @@ private void WriteComposedTypeSerializer(CodeFunction codeElement, LanguageWrite { if (GetOriginalComposedType(composedParam) is not { } composedType) return; - if (composedType.IsComposedOfPrimitives(IsPrimitiveType)) + if (composedType.IsComposedOfPrimitives((x, y) => IsPrimitiveType(x, y, false))) { var paramName = composedParam.Name.ToFirstCharacterLowerCase(); writer.WriteLine($"if ({paramName} === undefined || {paramName} === null) return;"); writer.StartBlock($"switch (typeof {paramName}) {{"); - foreach (var type in composedType.Types.Where(x => IsPrimitiveType(x, composedType))) + foreach (var type in composedType.Types.Where(x => IsPrimitiveType(x, composedType, false))) { WriteCaseStatementForPrimitiveTypeSerialization(type, "key", paramName, codeElement, writer); } @@ -327,11 +327,8 @@ private string FindFunctionInNameSpace(string functionName, CodeElement codeElem CodeFunction[] codeFunctions = myNamespace.FindChildrenByName(functionName).ToArray(); var codeFunction = Array.Find(codeFunctions, - func => func.GetImmediateParentOfType().Name == myNamespace.Name); - - if (codeFunction == null) + func => func.GetImmediateParentOfType().Name == myNamespace.Name) ?? throw new InvalidOperationException($"Function {functionName} not found in namespace {myNamespace.Name}"); - return conventions.GetTypeString(new CodeType { TypeDefinition = codeFunction }, codeElement, false); } diff --git a/src/Kiota.Builder/Writers/TypeScript/TypeScriptConventionService.cs b/src/Kiota.Builder/Writers/TypeScript/TypeScriptConventionService.cs index a635ef6cd8..1b3b2b22e8 100644 --- a/src/Kiota.Builder/Writers/TypeScript/TypeScriptConventionService.cs +++ b/src/Kiota.Builder/Writers/TypeScript/TypeScriptConventionService.cs @@ -225,7 +225,9 @@ TYPE_LOWERCASE_BOOLEAN or }; } - public static bool IsPrimitiveType(CodeType codeType, CodeComposedTypeBase codeComposedTypeBase) => IsPrimitiveType(GetTypescriptTypeString(codeType, codeComposedTypeBase)); + public static bool IsPrimitiveType(CodeType codeType, CodeComposedTypeBase codeComposedTypeBase) => IsPrimitiveType(codeType, codeComposedTypeBase, true); + + public static bool IsPrimitiveType(CodeType codeType, CodeComposedTypeBase codeComposedTypeBase, bool includeCollectionInformation) => IsPrimitiveType(GetTypescriptTypeString(codeType, codeComposedTypeBase, includeCollectionInformation)); internal static string RemoveInvalidDescriptionCharacters(string originalDescription) => originalDescription?.Replace("\\", "/", StringComparison.OrdinalIgnoreCase) ?? string.Empty; public override bool WriteShortDescription(IDocumentedElement element, LanguageWriter writer, string prefix = "", string suffix = "") From f8f274ed217d1abef35c7d788bc3d18ea03145f2 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Fri, 6 Sep 2024 09:54:09 -0400 Subject: [PATCH 3/3] fix: adds suppression for php composed types Signed-off-by: Vincent Biret --- it/config.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/it/config.json b/it/config.json index 588285507a..a85fc9c22b 100644 --- a/it/config.json +++ b/it/config.json @@ -161,8 +161,7 @@ "Rationale": "https://github.com/microsoft/kiota/issues/2957" } ], - "IdempotencySuppressions": [ - ] + "IdempotencySuppressions": [] }, "apisguru::twilio.com:api": { "ExcludePatterns": [ @@ -253,6 +252,10 @@ { "Language": "ruby", "Rationale": "https://github.com/microsoft/kiota/issues/1816" + }, + { + "Language": "php", + "Rationale": "https://github.com/microsoft/kiota/issues/5354" } ], "IdempotencySuppressions": [ @@ -301,13 +304,10 @@ "Rationale": "https://github.com/microsoft/kiota/issues/1816" } ], - "IdempotencySuppressions": [ - ] + "IdempotencySuppressions": [] }, "apisguru::apis.guru": { - "Suppressions": [ - ], - "IdempotencySuppressions": [ - ] + "Suppressions": [], + "IdempotencySuppressions": [] } }