Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SendPrimitiveAsync throws InvalidOperationException for enums #291

Merged
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.9.9] - 2024-07-11

- Fix enum deserialization for SendPrimitiveAsync and

## [1.9.8] - 2024-07-08

- Migrated source of various libraries to mono repository at <https://github.com/microsoft/kiota-dotnet>.
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<!-- Common default project properties for ALL projects-->
<PropertyGroup>
<VersionPrefix>1.9.8</VersionPrefix>
<VersionPrefix>1.9.9</VersionPrefix>
<VersionSuffix></VersionSuffix>
<!-- This is overidden in test projects by setting to true-->
<IsTestProject>false</IsTestProject>
Expand Down
6 changes: 6 additions & 0 deletions Microsoft.Kiota.sln
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Kiota.Serializati
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Kiota.Serialization.Text.Tests", "tests\serialization\text\Microsoft.Kiota.Serialization.Text.Tests.csproj", "{5F6AC278-C4A4-4EED-A7D3-1750A4D6FD15}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Kiota.Trimming.Validation", "tests\trimming\Microsoft.Kiota.Trimming.Validation\Microsoft.Kiota.Trimming.Validation.csproj", "{13992912-662D-4A3B-9354-DD49DB1AC2D5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -110,6 +112,10 @@ Global
{5F6AC278-C4A4-4EED-A7D3-1750A4D6FD15}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5F6AC278-C4A4-4EED-A7D3-1750A4D6FD15}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5F6AC278-C4A4-4EED-A7D3-1750A4D6FD15}.Release|Any CPU.Build.0 = Release|Any CPU
{13992912-662D-4A3B-9354-DD49DB1AC2D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{13992912-662D-4A3B-9354-DD49DB1AC2D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13992912-662D-4A3B-9354-DD49DB1AC2D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13992912-662D-4A3B-9354-DD49DB1AC2D5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
8 changes: 0 additions & 8 deletions src/abstractions/IRequestAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,23 +50,15 @@ public interface IRequestAdapter
/// <param name="errorMapping">The error factories mapping to use in case of a failed request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the requests.</param>
/// <returns>The deserialized primitive response model.</returns>
#if NET5_0_OR_GREATER
MartinM85 marked this conversation as resolved.
Show resolved Hide resolved
Task<ModelType?> SendPrimitiveAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] ModelType>(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>>? errorMapping = default, CancellationToken cancellationToken = default);
#else
Task<ModelType?> SendPrimitiveAsync<ModelType>(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>>? errorMapping = default, CancellationToken cancellationToken = default);
#endif
/// <summary>
/// Executes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model collection.
/// </summary>
/// <param name="requestInfo">The RequestInformation object to use for the HTTP request.</param>
/// <param name="errorMapping">The error factories mapping to use in case of a failed request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the requests.</param>
/// <returns>The deserialized primitive response model collection.</returns>
#if NET5_0_OR_GREATER
Task<IEnumerable<ModelType>?> SendPrimitiveCollectionAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] ModelType>(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>>? errorMapping = default, CancellationToken cancellationToken = default);
#else
Task<IEnumerable<ModelType>?> SendPrimitiveCollectionAsync<ModelType>(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>>? errorMapping = default, CancellationToken cancellationToken = default);
#endif
/// <summary>
/// Executes the HTTP request specified by the given RequestInformation with no return content.
/// </summary>
Expand Down
15 changes: 7 additions & 8 deletions src/http/httpClient/HttpClientRequestAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Authentication;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Helpers;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions.Store;
using Microsoft.Kiota.Http.HttpClientLibrary.Middleware;
Expand Down Expand Up @@ -123,11 +124,7 @@ public string? BaseUrl
/// <param name="errorMapping">The error factories mapping to use in case of a failed request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the request.</param>
/// <returns>The deserialized primitive response model collection.</returns>
#if NET5_0_OR_GREATER
public async Task<IEnumerable<ModelType>?> SendPrimitiveCollectionAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] ModelType>(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>>? errorMapping = default, CancellationToken cancellationToken = default)
#else
public async Task<IEnumerable<ModelType>?> SendPrimitiveCollectionAsync<ModelType>(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>>? errorMapping = default, CancellationToken cancellationToken = default)
#endif
{
using var span = startTracingSpan(requestInfo, nameof(SendPrimitiveCollectionAsync));
var response = await GetHttpResponseMessage(requestInfo, cancellationToken, span).ConfigureAwait(false);
Expand Down Expand Up @@ -208,11 +205,7 @@ public string? BaseUrl
/// <param name="errorMapping">The error factories mapping to use in case of a failed request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the request.</param>
/// <returns>The deserialized primitive response model.</returns>
#if NET5_0_OR_GREATER
public async Task<ModelType?> SendPrimitiveAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] ModelType>(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>>? errorMapping = default, CancellationToken cancellationToken = default)
#else
public async Task<ModelType?> SendPrimitiveAsync<ModelType>(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>>? errorMapping = default, CancellationToken cancellationToken = default)
#endif
{
using var span = startTracingSpan(requestInfo, nameof(SendPrimitiveAsync));
var modelType = typeof(ModelType);
Expand Down Expand Up @@ -302,6 +295,12 @@ public string? BaseUrl
{
result = rootNode.GetDateValue();
}
else if(
Nullable.GetUnderlyingType(modelType) is { IsEnum: true } underlyingType &&
rootNode.GetStringValue() is { Length: > 0 } rawValue)
{
result = EnumHelpers.GetEnumValue(underlyingType, rawValue);
}
else throw new InvalidOperationException("error handling the response, unexpected type");
SetResponseType(result, span);
return (ModelType)result!;
Expand Down
32 changes: 3 additions & 29 deletions src/serialization/form/FormParseNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Xml;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Helpers;
using Microsoft.Kiota.Abstractions.Serialization;
#if NET5_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
Expand Down Expand Up @@ -226,7 +227,7 @@ private void AssignFieldValues<T>(T item) where T : IParsable
#endif
{
foreach(var v in DecodedValue.Split(ComaSeparator, StringSplitOptions.RemoveEmptyEntries))
yield return GetEnumValueInternal<T>(v);
yield return EnumHelpers.GetEnumValue<T>(v);
}

#if NET5_0_OR_GREATER
Expand All @@ -235,33 +236,6 @@ private void AssignFieldValues<T>(T item) where T : IParsable
T? IParseNode.GetEnumValue<T>()
#endif
{
return GetEnumValueInternal<T>(DecodedValue);
}

private static T? GetEnumValueInternal<T>(string rawValue) where T : struct, Enum
{
if(string.IsNullOrEmpty(rawValue))
return null;
if(typeof(T).IsDefined(typeof(FlagsAttribute)))
{
ReadOnlySpan<char> valueSpan = rawValue.AsSpan();
int value = 0;
while(valueSpan.Length > 0)
{
int commaIndex = valueSpan.IndexOf(',');
ReadOnlySpan<char> valueNameSpan = commaIndex < 0 ? valueSpan : valueSpan.Slice(0, commaIndex);
#if NET6_0_OR_GREATER
if(Enum.TryParse<T>(valueNameSpan, true, out var result))
#else
if(Enum.TryParse<T>(valueNameSpan.ToString(), true, out var result))
#endif
value |= (int)(object)result;
valueSpan = commaIndex < 0 ? ReadOnlySpan<char>.Empty : valueSpan.Slice(commaIndex + 1);
}
return (T)(object)value;
}
else if(Enum.TryParse<T>(rawValue, out var result))
return result;
return null;
return EnumHelpers.GetEnumValue<T>(DecodedValue);
}
}
30 changes: 7 additions & 23 deletions src/serialization/json/JsonParseNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Xml;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Helpers;
using Microsoft.Kiota.Abstractions.Serialization;

#if NET5_0_OR_GREATER
Expand Down Expand Up @@ -217,29 +218,7 @@ public JsonParseNode(JsonElement node, KiotaJsonSerializationContext jsonSeriali
#endif
{
var rawValue = _jsonNode.GetString();
if(string.IsNullOrEmpty(rawValue)) return null;

rawValue = ToEnumRawName<T>(rawValue!);
if(typeof(T).IsDefined(typeof(FlagsAttribute)))
{
ReadOnlySpan<char> valueSpan = rawValue.AsSpan();
int value = 0;
while(valueSpan.Length > 0)
{
int commaIndex = valueSpan.IndexOf(',');
ReadOnlySpan<char> valueNameSpan = commaIndex < 0 ? valueSpan : valueSpan.Slice(0, commaIndex);
#if NET6_0_OR_GREATER
if(Enum.TryParse<T>(valueNameSpan, true, out var result))
#else
if(Enum.TryParse<T>(valueNameSpan.ToString(), true, out var result))
#endif
value |= (int)(object)result;
valueSpan = commaIndex < 0 ? ReadOnlySpan<char>.Empty : valueSpan.Slice(commaIndex + 1);
}
return (T)(object)value;
}
else
return Enum.TryParse<T>(rawValue, true, out var result) ? result : null;
return EnumHelpers.GetEnumValue<T>(rawValue!);
}

/// <summary>
Expand Down Expand Up @@ -347,6 +326,11 @@ public IEnumerable<T> GetCollectionOfPrimitiveValues<T>()
yield return (T)(object)currentParseNode.GetDateValue()!;
else if(genericType == TypeConstants.TimeType)
yield return (T)(object)currentParseNode.GetTimeValue()!;
else if(Nullable.GetUnderlyingType(genericType) is { IsEnum: true } underlyingType &&
currentParseNode.GetStringValue() is { Length: > 0 } rawValue)
{
yield return (T)EnumHelpers.GetEnumValue(underlyingType, rawValue)!;
}
else
throw new InvalidOperationException($"unknown type for deserialization {genericType.FullName}");
}
Expand Down
3 changes: 2 additions & 1 deletion src/serialization/text/TextParseNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Xml;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Helpers;
using Microsoft.Kiota.Abstractions.Serialization;
#if NET5_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
Expand Down Expand Up @@ -78,5 +79,5 @@ public TextParseNode(string? text)
#else
public T? GetEnumValue<T>() where T : struct, Enum
#endif
=> Enum.TryParse<T>(Text, true, out var result) ? result : null;
=> Text is null ? null : EnumHelpers.GetEnumValue<T>(Text);
}
Loading
Loading