diff --git a/Microsoft.Kiota.Http.HttpClientLibrary.Tests/RequestAdapterTests.cs b/Microsoft.Kiota.Http.HttpClientLibrary.Tests/RequestAdapterTests.cs index 479342c..ef55e6a 100644 --- a/Microsoft.Kiota.Http.HttpClientLibrary.Tests/RequestAdapterTests.cs +++ b/Microsoft.Kiota.Http.HttpClientLibrary.Tests/RequestAdapterTests.cs @@ -664,6 +664,134 @@ public async Task SendPrimitiveReturnsNullIfValueCannotBeParsedToEnum() Assert.Null(response); } + + [Fact] + public async Task SendPrimitiveHandleEnumFlagsIfValuesAreStrings() + { + var mockHandler = new Mock(); + var client = new HttpClient(mockHandler.Object); + mockHandler.Protected() + .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) + .ReturnsAsync(new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent("Value1,Value3") + }); + + var mockParseNode = new Mock(); + mockParseNode.Setup(x => x.GetStringValue()) + .Returns("Value1,Value3"); + + var mockParseNodeFactory = new Mock(); + mockParseNodeFactory.Setup(x => x.GetRootParseNodeAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(mockParseNode.Object)); + var adapter = new HttpClientRequestAdapter(_authenticationProvider, mockParseNodeFactory.Object, httpClient: client); + var requestInfo = new RequestInformation + { + HttpMethod = Method.GET, + URI = new Uri("https://example.com") + }; + + var response = await adapter.SendPrimitiveAsync(requestInfo); + + Assert.Equal(TestEnumWithFlags.Value1 | TestEnumWithFlags.Value3, response); + } + + [Fact] + public async Task SendPrimitiveHandleEnumFlagsIfValuesAreIntegers() + { + var mockHandler = new Mock(); + var client = new HttpClient(mockHandler.Object); + mockHandler.Protected() + .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) + .ReturnsAsync(new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent("1,2") + }); + + var mockParseNode = new Mock(); + mockParseNode.Setup(x => x.GetStringValue()) + .Returns("1,2"); + + var mockParseNodeFactory = new Mock(); + mockParseNodeFactory.Setup(x => x.GetRootParseNodeAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(mockParseNode.Object)); + var adapter = new HttpClientRequestAdapter(_authenticationProvider, mockParseNodeFactory.Object, httpClient: client); + var requestInfo = new RequestInformation + { + HttpMethod = Method.GET, + URI = new Uri("https://example.com") + }; + + var response = await adapter.SendPrimitiveAsync(requestInfo); + + Assert.Equal(TestEnumWithFlags.Value1 | TestEnumWithFlags.Value2, response); + } + + [Fact] + public async Task SendPrimitiveHandleEnumFlagsIfValuesAreFromEnumMember() + { + var mockHandler = new Mock(); + var client = new HttpClient(mockHandler.Object); + mockHandler.Protected() + .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) + .ReturnsAsync(new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent("Value__3,Value__2") + }); + + var mockParseNode = new Mock(); + mockParseNode.Setup(x => x.GetStringValue()) + .Returns("Value__3,Value__2"); + + var mockParseNodeFactory = new Mock(); + mockParseNodeFactory.Setup(x => x.GetRootParseNodeAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(mockParseNode.Object)); + var adapter = new HttpClientRequestAdapter(_authenticationProvider, mockParseNodeFactory.Object, httpClient: client); + var requestInfo = new RequestInformation + { + HttpMethod = Method.GET, + URI = new Uri("https://example.com") + }; + + var response = await adapter.SendPrimitiveAsync(requestInfo); + + Assert.Equal(TestEnumWithFlags.Value2 | TestEnumWithFlags.Value3, response); + } + + [Fact] + public async Task SendPrimitiveReturnsNullIfFlagValueCannotBeParsedToEnum() + { + var mockHandler = new Mock(); + var client = new HttpClient(mockHandler.Object); + mockHandler.Protected() + .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) + .ReturnsAsync(new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent("Value0") + }); + + var mockParseNode = new Mock(); + mockParseNode.Setup(x => x.GetStringValue()) + .Returns("Value0"); + + var mockParseNodeFactory = new Mock(); + mockParseNodeFactory.Setup(x => x.GetRootParseNodeAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(mockParseNode.Object)); + var adapter = new HttpClientRequestAdapter(_authenticationProvider, mockParseNodeFactory.Object, httpClient: client); + var requestInfo = new RequestInformation + { + HttpMethod = Method.GET, + URI = new Uri("https://example.com") + }; + + var response = await adapter.SendPrimitiveAsync(requestInfo); + + Assert.Null(response); + } } public enum TestEnum @@ -675,4 +803,15 @@ public enum TestEnum [EnumMember(Value = "Value__3")] Value3 } + + [Flags] + public enum TestEnumWithFlags + { + [EnumMember(Value = "Value__1")] + Value1 = 0x01, + [EnumMember(Value = "Value__2")] + Value2 = 0x02, + [EnumMember(Value = "Value__3")] + Value3 = 0x04 + } } diff --git a/src/HttpClientRequestAdapter.cs b/src/HttpClientRequestAdapter.cs index 3366f87..d26e42a 100644 --- a/src/HttpClientRequestAdapter.cs +++ b/src/HttpClientRequestAdapter.cs @@ -299,27 +299,61 @@ public string? BaseUrl Nullable.GetUnderlyingType(modelType) is { IsEnum: true } underlyingType && rootNode.GetStringValue() is { Length: > 0 } rawValue) { - foreach(var field in underlyingType.GetFields()) + if(underlyingType.IsDefined(typeof(FlagsAttribute))) { - if(field.GetCustomAttribute() is { } attr && rawValue.Equals(attr.Value, StringComparison.Ordinal)) + int intValue = 0; + while(rawValue.Length > 0) { - rawValue = field.Name; - break; - } - } + int commaIndex = rawValue.IndexOf(','); + var valueName = commaIndex < 0 ? rawValue : rawValue.Substring(0, commaIndex); + foreach(var field in underlyingType.GetFields()) + { + if(field.GetCustomAttribute() is { } attr && valueName.Equals(attr.Value, StringComparison.Ordinal)) + { + valueName = field.Name; + break; + } + } #if NET5_0_OR_GREATER - Enum.TryParse(underlyingType, rawValue, true, out object? enumResult); - result = enumResult; + if(Enum.TryParse(underlyingType, valueName, true, out var enumPartResult)) + intValue |= (int)enumPartResult!; #else - try - { - result = Enum.Parse(underlyingType, rawValue, true); + try + { + intValue |= (int)Enum.Parse(underlyingType, valueName, true); + } + catch { } +#endif + + rawValue = commaIndex < 0 ? string.Empty : rawValue.Substring(commaIndex + 1); + } + result = intValue > 0 ? Enum.Parse(underlyingType, intValue.ToString(), true) : null; } - catch + else { - result = null; - } + foreach(var field in underlyingType.GetFields()) + { + if(field.GetCustomAttribute() is { } attr && rawValue.Equals(attr.Value, StringComparison.Ordinal)) + { + rawValue = field.Name; + break; + } + } + +#if NET5_0_OR_GREATER + Enum.TryParse(underlyingType, rawValue, true, out object? enumResult); + result = enumResult; +#else + try + { + result = Enum.Parse(underlyingType, rawValue, true); + } + catch + { + result = null; + } #endif + } } else {