From 337ee22540fff768cb71164c0feda2e36655086e Mon Sep 17 00:00:00 2001 From: Chris Jackson Date: Thu, 30 Nov 2023 11:41:20 +0000 Subject: [PATCH 1/2] Add array expansion. --- .../RequestInformationTests.cs | 164 ++++++++++++++++++ src/RequestInformation.cs | 37 ++-- 2 files changed, 188 insertions(+), 13 deletions(-) diff --git a/Microsoft.Kiota.Abstractions.Tests/RequestInformationTests.cs b/Microsoft.Kiota.Abstractions.Tests/RequestInformationTests.cs index 10439093..df4cefb2 100644 --- a/Microsoft.Kiota.Abstractions.Tests/RequestInformationTests.cs +++ b/Microsoft.Kiota.Abstractions.Tests/RequestInformationTests.cs @@ -502,8 +502,167 @@ public void SetsEnumValuesInPathParameters() // Assert Assert.Equal("http://localhost/1,2", testRequest.URI.ToString()); } + + + [Fact] + public void SetsIntValueInQueryParameters() + { + // Arrange + var testRequest = new RequestInformation() + { + HttpMethod = Method.GET, + UrlTemplate = "http://localhost/me{?item}" + }; + // Act + testRequest.AddQueryParameters(new GetQueryParameters { Item = 1 }); + // Assert + Assert.Equal("http://localhost/me?item=1", testRequest.URI.ToString()); + } + [Fact] + public void SetsIntValuesInQueryParameters() + { + // Arrange + var requestInfo = new RequestInformation() + { + HttpMethod = Method.GET, + UrlTemplate = "http://localhost/me{?items}" + }; + // Act + requestInfo.AddQueryParameters(new GetQueryParameters { Items = new object []{1,2}}); + // Assert + Assert.Equal("http://localhost/me?items=1,2", requestInfo.URI.ToString()); + } + + [Fact] + public void SetsBooleanValuesInQueryParameters() + { + // Arrange + var requestInfo = new RequestInformation() + { + HttpMethod = Method.GET, + UrlTemplate = "http://localhost/me{?items}" + }; + // Act + requestInfo.AddQueryParameters(new GetQueryParameters { Items = new object [] { true, false } }); + // Assert + Assert.Equal("http://localhost/me?items=true,false", requestInfo.URI.ToString()); + } + + [Fact] + public void SetsDateTimeOffsetValuesInQueryParameters() + { + var requestInfo = new RequestInformation + { + HttpMethod = Method.GET, + UrlTemplate = "http://localhost/me{?items}" + }; + + // Act + var dateTime1 = new DateTimeOffset(2022, 8, 1, 0, 0, 0, TimeSpan.Zero); + var dateTime2 = new DateTimeOffset(2022, 8, 2, 0, 0, 0, TimeSpan.Zero); + + requestInfo.AddQueryParameters(new GetQueryParameters { Items = new object[] { dateTime1, dateTime2 } }); + + // Assert + Assert.Equal("http://localhost/me?items=2022-08-01T00%3A00%3A00.0000000%2B00%3A00,2022-08-02T00%3A00%3A00.0000000%2B00%3A00", requestInfo.URI.OriginalString); + } + [Fact] + public void SetsDateTimeValuesInQueryParameters() + { + var requestInfo = new RequestInformation + { + HttpMethod = Method.GET, + UrlTemplate = "http://localhost/me{?items}" + }; + + // Act + var dateTime1 = new DateTime(2022, 8, 1, 0, 0, 0); + var dateTime2 = new DateTime(2022, 8, 2, 0, 0, 0); + + requestInfo.AddQueryParameters(new GetQueryParameters { Items = new object[] { dateTime1, dateTime2 } }); + + // Assert + Assert.Equal("http://localhost/me?items=2022-08-01T00%3A00%3A00.0000000,2022-08-02T00%3A00%3A00.0000000", requestInfo.URI.OriginalString); + } + + [Fact] + public void SetsDateValuesInQueryParameters() + { + var requestInfo = new RequestInformation + { + HttpMethod = Method.GET, + UrlTemplate = "http://localhost/me{?items}" + }; + + // Act + var date1 = new Date(2022, 8, 1); + var date2 = new Date(2022, 8, 2); + + requestInfo.AddQueryParameters(new GetQueryParameters { Items = new object[] { date1, date2 } }); + + // Assert + Assert.Equal("http://localhost/me?items=2022-08-01,2022-08-02", requestInfo.URI.OriginalString); + } + + [Fact] + public void SetsTimeValuesInQueryParameters() + { + var requestInfo = new RequestInformation + { + HttpMethod = Method.GET, + UrlTemplate = "http://localhost/me{?items}" + }; + + // Act + var date1 = new Time(10,0,0); + var date2 = new Time(11, 1, 1); + + requestInfo.AddQueryParameters(new GetQueryParameters { Items = new object[] { date1, date2 } }); + + // Assert + Assert.Equal("http://localhost/me?items=10%3A00%3A00,11%3A01%3A01", requestInfo.URI.OriginalString); + } + + [Fact] + public void SetsGuidValuesInQueryParameters() + { + var requestInfo = new RequestInformation + { + HttpMethod = Method.GET, + UrlTemplate = "http://localhost/me{?items}" + }; + + // Act + var g1 = Guid.Parse("55331110-6817-4A9B-83B2-57617E3E08E5"); + var g2 = Guid.Parse("482DFF4F-63D6-47F4-A88B-5CAEC03180D4"); + + requestInfo.AddQueryParameters(new GetQueryParameters { Items = new object[] { g1, g2 } }); + + // Assert + Assert.Equal("http://localhost/me?items=55331110-6817-4a9b-83b2-57617e3e08e5,482dff4f-63d6-47f4-a88b-5caec03180d4", requestInfo.URI.OriginalString); + } + + + [Fact] + public void DoesNotExpandSecondLayerArrays() + { + var requestInfo = new RequestInformation + { + HttpMethod = Method.GET, + UrlTemplate = "http://localhost/me{?items}" + }; + + // Act + requestInfo.AddQueryParameters(new GetQueryParameters { Items = new object[]{new int[]{1,2,3,4} } }); + // Assert + Assert.Equal("http://localhost/me?items=System.Int32%5B%5D", requestInfo.URI.OriginalString); + } + + } + + /// The messages in a mailbox or folder. Read-only. Nullable. internal class GetQueryParameters { @@ -533,5 +692,10 @@ internal class GetQueryParameters /// Which Dataset to use [QueryParameter("datasets")] public TestEnum[] DataSets { get; set; } + + [QueryParameter("item")] + public object Item { get; set; } + [QueryParameter("items")] + public object[] Items { get; set; } } } diff --git a/src/RequestInformation.cs b/src/RequestInformation.cs index e9b4950e..f8ab13ae 100644 --- a/src/RequestInformation.cs +++ b/src/RequestInformation.cs @@ -5,10 +5,12 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.Serialization; using Microsoft.Kiota.Abstractions.Extensions; using Microsoft.Kiota.Abstractions.Serialization; @@ -89,14 +91,14 @@ public Uri URI var substitutions = new Dictionary(); foreach(var urlTemplateParameter in PathParameters) { - substitutions.Add(urlTemplateParameter.Key, GetSanitizedValue(urlTemplateParameter.Value)); + substitutions.Add(urlTemplateParameter.Key, GetSanitizedValues(urlTemplateParameter.Value)); } foreach(var queryStringParameter in QueryParameters) { if(queryStringParameter.Value != null) { - substitutions.Add(queryStringParameter.Key, GetSanitizedValue(queryStringParameter.Value)); + substitutions.Add(queryStringParameter.Key, GetSanitizedValues(queryStringParameter.Value)); } } @@ -105,6 +107,12 @@ public Uri URI } } + private static object GetSanitizedValues(object value) => value switch + { + Array array => ExpandArray(array), + _ => GetSanitizedValue(value), + }; + /// /// Sanitizes objects in order to appear appropiately in the URL /// @@ -163,25 +171,28 @@ public void AddQueryParameters(T source) !string.IsNullOrEmpty(x.Value.ToString()) && // no need to add an empty string value (x.Value is not ICollection collection || collection.Count > 0))) // no need to add empty collection { - QueryParameters.AddOrReplace(property.Name!, ReplaceEnumValueByStringRepresentation(property.Value!)); + QueryParameters.AddOrReplace(property.Name!, property.Value!); + } + } + + private static object ExpandArray(Array collection) + { + var passedArray = new string[collection.Length]; + for(var i = 0; i < collection.Length; i++) + { + passedArray[i] = GetSanitizedValue(collection.GetValue(i)!).ToString()!; } + return passedArray; } + private static object ReplaceEnumValueByStringRepresentation(object source) { if(source is Enum enumValue && GetEnumName(enumValue) is string enumValueName) { return enumValueName; } - else if(source is Array collection && collection.Length > 0 && collection.GetValue(0) is Enum) - { - var passedArray = new string[collection.Length]; - for(var i = 0; i < collection.Length; i++) - {// this is ugly but necessary due to covariance limitations with pattern matching - passedArray[i] = GetEnumName((Enum)collection.GetValue(i)!)!; - } - return passedArray; - } - else return source; + + return source; } #if NET5_0_OR_GREATER private static string? GetEnumName<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(T value) where T : Enum From 132e95862d37cd5f64f886d2c71e3fedf544ca09 Mon Sep 17 00:00:00 2001 From: Chris Jackson Date: Thu, 30 Nov 2023 13:52:42 +0000 Subject: [PATCH 2/2] Bump version number Added change log --- CHANGELOG.md | 6 ++++++ src/Microsoft.Kiota.Abstractions.csproj | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3ee3a2e..35d76143 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.7.3] - 2023-11-30 + +### Changed + +- Fixed an issue where arrays of non-string types passed into the query parameter were not being converted to strings leading to Invalid cast exceptions. [microsoft/kiota#3354](https://github.com/microsoft/kiota/issues/3354) + ## [1.7.2] - 2023-11-14 ### Added diff --git a/src/Microsoft.Kiota.Abstractions.csproj b/src/Microsoft.Kiota.Abstractions.csproj index 07249c63..8082cb53 100644 --- a/src/Microsoft.Kiota.Abstractions.csproj +++ b/src/Microsoft.Kiota.Abstractions.csproj @@ -14,7 +14,7 @@ https://aka.ms/kiota/docs true true - 1.7.2 + 1.7.3 true false