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 9 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-12

- Fix enum deserialization for SendPrimitiveAsync and SendPrimitiveCollectionAsync

## [1.9.8] - 2024-07-08

- Migrated source of various libraries to mono repository at <https://github.com/microsoft/kiota-dotnet>.
Expand Down
45 changes: 1 addition & 44 deletions 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 All @@ -10,47 +10,4 @@
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<!-- Common project properties for PACKAGED projects TO BE RELEASED-->
<PropertyGroup Condition="('$(IsTestProject)' != 'true') and ('$(IsAnalyzerProject)' != 'true')">
<Authors>Microsoft</Authors>
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageIconUrl>http://go.microsoft.com/fwlink/?LinkID=288890</PackageIconUrl>
<RepositoryUrl>https://github.com/microsoft/kiota-dotnet</RepositoryUrl>
<PackageProjectUrl>https://aka.ms/kiota/docs</PackageProjectUrl>
<PackageReleaseNotes>
https://github.com/microsoft/kiota-dotnet/releases
</PackageReleaseNotes>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<Deterministic>true</Deterministic>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<AssemblyOriginatorKeyFile>35MSSharedLib1024.snk</AssemblyOriginatorKeyFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>$(NoWarn);NU5048;NETSDK1138</NoWarn>
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)','net5.0'))">true</IsTrimmable>
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)','net8.0'))">true</IsAotCompatible>
<ContinuousIntegrationBuild Condition="'$(TF_BUILD)' == 'true'">true</ContinuousIntegrationBuild>
</PropertyGroup>
<!-- Common project properties for TEST projects-->
<PropertyGroup Condition="'$(IsTestProject)' == 'true'">
<IsPackable>false</IsPackable>
<OutputType>Library</OutputType>
</PropertyGroup>
MartinM85 marked this conversation as resolved.
Show resolved Hide resolved
<!-- Include ReadMe.md for PACKAGED projects TO BE RELEASED-->
<ItemGroup Condition="('$(IsTestProject)' != 'true') and ('$(IsAnalyzerProject)' != 'true')">
<None Include="README.md">
<Pack>True</Pack>
<PackagePath>
</PackagePath>
</None>
</ItemGroup>
<!-- Include SourceLink Dependency for packages TO BE RELEASED-->
<ItemGroup Condition="('$(IsTestProject)' != 'true') and ('$(IsAnalyzerProject)' != 'true')">
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
</ItemGroup>
</Project>
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
50 changes: 50 additions & 0 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<Project>
<PropertyGroup>
<VersionPrefix>1.9.9</VersionPrefix>
MartinM85 marked this conversation as resolved.
Show resolved Hide resolved
<VersionSuffix></VersionSuffix>
<!-- This is overidden in test projects by setting to true-->
<IsTestProject>false</IsTestProject>
<!-- This is overidden in test projects by setting to true-->
<IsAnalyzerProject>false</IsAnalyzerProject>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<!-- Common project properties for PACKAGED projects TO BE RELEASED-->
<PropertyGroup Condition="('$(IsTestProject)' != 'true') and ('$(IsAnalyzerProject)' != 'true')">
<Authors>Microsoft</Authors>
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageIconUrl>http://go.microsoft.com/fwlink/?LinkID=288890</PackageIconUrl>
<RepositoryUrl>https://github.com/microsoft/kiota-dotnet</RepositoryUrl>
<PackageProjectUrl>https://aka.ms/kiota/docs</PackageProjectUrl>
<PackageReleaseNotes>
https://github.com/microsoft/kiota-dotnet/releases
</PackageReleaseNotes>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<Deterministic>true</Deterministic>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<AssemblyOriginatorKeyFile>35MSSharedLib1024.snk</AssemblyOriginatorKeyFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>$(NoWarn);NU5048;NETSDK1138</NoWarn>
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)','net5.0'))">true</IsTrimmable>
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)','net8.0'))">true</IsAotCompatible>
<ContinuousIntegrationBuild Condition="'$(TF_BUILD)' == 'true'">true</ContinuousIntegrationBuild>
</PropertyGroup>
<!-- Include ReadMe.md for PACKAGED projects TO BE RELEASED-->
<ItemGroup Condition="('$(IsTestProject)' != 'true') and ('$(IsAnalyzerProject)' != 'true')">
<None Include="README.md">
<Pack>True</Pack>
<PackagePath>
</PackagePath>
</None>
</ItemGroup>
<!-- Include SourceLink Dependency for packages TO BE RELEASED-->
<ItemGroup Condition="('$(IsTestProject)' != 'true') and ('$(IsAnalyzerProject)' != 'true')">
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
</ItemGroup>
</Project>
18 changes: 10 additions & 8 deletions src/abstractions/Helpers/EnumHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,46 +86,48 @@ private static ReadOnlySpan<char> ToEnumRawName<T>(ReadOnlySpan<char> span) wher
{
return null;
}
if(type.IsDefined(typeof(FlagsAttribute)))
Type enumType = (Nullable.GetUnderlyingType(type) is { IsEnum: true } underlyingType) ? underlyingType : type;
if(enumType.IsDefined(typeof(FlagsAttribute)))
{
int intValue = 0;
while(rawValue.Length > 0)
{
int commaIndex = rawValue.IndexOf(',');
var valueName = commaIndex < 0 ? rawValue : rawValue.Substring(0, commaIndex);
if(TryGetFieldValueName(type, valueName, out var value))
if(TryGetFieldValueName(enumType, valueName, out var value))
{
valueName = value;
}
#if NET5_0_OR_GREATER
if(Enum.TryParse(type, valueName, true, out var enumPartResult))
if(Enum.TryParse(enumType, valueName, true, out var enumPartResult))
intValue |= (int)enumPartResult!;
#else
try
{
intValue |= (int)Enum.Parse(type, valueName, true);
intValue |= (int)Enum.Parse(enumType, valueName, true);
}
catch { }
#endif

rawValue = commaIndex < 0 ? string.Empty : rawValue.Substring(commaIndex + 1);
}
result = intValue > 0 ? Enum.Parse(type, intValue.ToString(), true) : null;
result = intValue > 0 ? Enum.Parse(enumType, intValue.ToString(), true) : null;
}
else
{
if(TryGetFieldValueName(type, rawValue, out var value))

if(TryGetFieldValueName(enumType, rawValue, out var value))
{
rawValue = value;
}

#if NET5_0_OR_GREATER
Enum.TryParse(type, rawValue, true, out object? enumResult);
Enum.TryParse(enumType, rawValue, true, out object? enumResult);
result = enumResult;
#else
try
{
result = Enum.Parse(type, rawValue, true);
result = Enum.Parse(enumType, rawValue, true);
}
catch
{
Expand Down
4 changes: 4 additions & 0 deletions src/abstractions/serialization/IParseNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,11 @@ public interface IParseNode
/// Gets the collection of primitive values of the node.
/// </summary>
/// <returns>The collection of primitive values.</returns>
#if NET5_0_OR_GREATER
IEnumerable<T> GetCollectionOfPrimitiveValues<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] T>();
#else
IEnumerable<T> GetCollectionOfPrimitiveValues<T>();
#endif
/// <summary>
/// Gets the collection of enum values of the node.
/// </summary>
Expand Down
5 changes: 5 additions & 0 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 @@ -302,6 +303,10 @@ public string? BaseUrl
{
result = rootNode.GetDateValue();
}
else if(rootNode.GetStringValue() is { Length: > 0 } rawValue)
{
result = EnumHelpers.GetEnumValue(modelType, rawValue);
}
else throw new InvalidOperationException("error handling the response, unexpected type");
SetResponseType(result, span);
return (ModelType)result!;
Expand Down
36 changes: 7 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 @@ -96,7 +97,11 @@ private static string SanitizeKey(string key)
/// Get the collection of primitives of type <typeparam name="T"/>from the form node
/// </summary>
/// <returns>A collection of objects</returns>
#if NET5_0_OR_GREATER
public IEnumerable<T> GetCollectionOfPrimitiveValues<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] T>()
#else
public IEnumerable<T> GetCollectionOfPrimitiveValues<T>()
#endif
{
var genericType = typeof(T);
var primitiveValueCollection = DecodedValue.Split(ComaSeparator, StringSplitOptions.RemoveEmptyEntries);
Expand Down Expand Up @@ -226,7 +231,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 +240,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);
}
}
33 changes: 10 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 @@ -309,7 +288,11 @@ public IEnumerable<T> GetCollectionOfObjectValues<T>(ParsableFactory<T> factory)
/// Get the collection of primitives of type <typeparam name="T"/>from the json node
/// </summary>
/// <returns>A collection of objects</returns>
#if NET5_0_OR_GREATER
public IEnumerable<T> GetCollectionOfPrimitiveValues<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] T>()
#else
public IEnumerable<T> GetCollectionOfPrimitiveValues<T>()
#endif
{
if(_jsonNode.ValueKind == JsonValueKind.Array)
{
Expand Down Expand Up @@ -347,6 +330,10 @@ public IEnumerable<T> GetCollectionOfPrimitiveValues<T>()
yield return (T)(object)currentParseNode.GetDateValue()!;
else if(genericType == TypeConstants.TimeType)
yield return (T)(object)currentParseNode.GetTimeValue()!;
else if(currentParseNode.GetStringValue() is { Length: > 0 } rawValue)
{
yield return (T)EnumHelpers.GetEnumValue(genericType, rawValue)!;
}
else
throw new InvalidOperationException($"unknown type for deserialization {genericType.FullName}");
}
Expand Down
6 changes: 5 additions & 1 deletion src/serialization/json/JsonSerializationWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -432,15 +432,19 @@ public void WriteAdditionalData(IDictionary<string, object> value)
WriteAnyValue(dataValue.Key, dataValue.Value);
}

#if NET5_0_OR_GREATER
private void WriteNonParsableObjectValue<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(string? key,T value)
#else
private void WriteNonParsableObjectValue<T>(string? key, T value)
#endif
{
if(!string.IsNullOrEmpty(key))
writer.WritePropertyName(key!);
writer.WriteStartObject();
if(value == null)
writer.WriteNullValue();
else
foreach(var oProp in value.GetType().GetProperties())
foreach(var oProp in typeof(T).GetProperties())
WriteAnyValue(oProp.Name, oProp.GetValue(value));
writer.WriteEndObject();
}
Expand Down
Loading
Loading