Skip to content

Commit

Permalink
Merge pull request #148 from microsoft/bugfix/enum-queryparameters-se…
Browse files Browse the repository at this point in the history
…rialization

bugfix/enum queryparameters serialization
  • Loading branch information
baywet authored Nov 14, 2023
2 parents bbdfa1f + 51bba94 commit e7d144d
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 62 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.7.1] - 2023-11-13

### Changed

- Fixed an issue where path and query parameters of enum type would not be expanded properly. [microsoft/kiota#3693](https://github.com/microsoft/kiota/issues/3693)

## [1.7.0] - 2023-11-07

### Added
Expand Down
11 changes: 11 additions & 0 deletions Microsoft.Kiota.Abstractions.Tests/Mocks/TestEnum.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Runtime.Serialization;

namespace Microsoft.Kiota.Abstractions.Tests.Mocks;

public enum TestEnum
{
[EnumMember(Value = "1")]
First,
[EnumMember(Value = "2")]
Second,
}
66 changes: 64 additions & 2 deletions Microsoft.Kiota.Abstractions.Tests/RequestInformationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ public void SetsPathParametersOfDateType()
};

// Act
var date = new Date(2023,10,26);
var date = new Date(2023, 10, 26);
var pathParameters = new Dictionary<string, object>
{
{ "%24date", date }
Expand All @@ -228,7 +228,7 @@ public void SetsPathParametersOfTimeType()
};

// Act
var time = new Time(6,0,0);
var time = new Time(6, 0, 0);
var pathParameters = new Dictionary<string, object>
{
{ "%24time", time }
Expand Down Expand Up @@ -446,6 +446,62 @@ public void SetsBoundaryOnMultipartBody()
Assert.Single(contentType);
Assert.Equal("multipart/form-data; boundary=" + multipartBody.Boundary, contentType.First());
}
[Fact]
public void SetsEnumValueInQueryParameters()
{
// Arrange
var testRequest = new RequestInformation()
{
HttpMethod = Method.GET,
UrlTemplate = "http://localhost/me{?dataset}"
};
// Act
testRequest.AddQueryParameters(new GetQueryParameters { DataSet = TestEnum.First });
// Assert
Assert.Equal("http://localhost/me?dataset=1", testRequest.URI.ToString());
}
[Fact]
public void SetsEnumValuesInQueryParameters()
{
// Arrange
var testRequest = new RequestInformation()
{
HttpMethod = Method.GET,
UrlTemplate = "http://localhost/me{?datasets}"
};
// Act
testRequest.AddQueryParameters(new GetQueryParameters { DataSets = new TestEnum[] { TestEnum.First, TestEnum.Second } });
// Assert
Assert.Equal("http://localhost/me?datasets=1,2", testRequest.URI.ToString());
}
[Fact]
public void SetsEnumValueInPathParameters()
{
// Arrange
var testRequest = new RequestInformation()
{
HttpMethod = Method.GET,
UrlTemplate = "http://localhost/{dataset}"
};
// Act
testRequest.PathParameters.Add("dataset", TestEnum.First);
// Assert
Assert.Equal("http://localhost/1", testRequest.URI.ToString());
}
[Fact]
public void SetsEnumValuesInPathParameters()
{
// Arrange
var testRequest = new RequestInformation()
{
HttpMethod = Method.GET,
UrlTemplate = "http://localhost/{dataset}"
};
// Act
testRequest.PathParameters.Add("dataset", new TestEnum[] { TestEnum.First, TestEnum.Second });
// Assert
Assert.Equal("http://localhost/1,2", testRequest.URI.ToString());
}
}

/// <summary>The messages in a mailbox or folder. Read-only. Nullable.</summary>
Expand All @@ -471,5 +527,11 @@ internal class GetQueryParameters
public string Search { get; set; }
/// <summary>Restrict to TenantId</summary>
public string TenantId { get; set; }
/// <summary>Which Dataset to use</summary>
[QueryParameter("dataset")]
public TestEnum DataSet { get; set; }
/// <summary>Which Dataset to use</summary>
[QueryParameter("datasets")]
public TestEnum[] DataSets { get; set; }
}
}
112 changes: 55 additions & 57 deletions Microsoft.Kiota.Abstractions.Tests/Serialization/Mocks/TestEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,38 @@
using System.Collections.Generic;
using Microsoft.Kiota.Abstractions.Serialization;

namespace Microsoft.Kiota.Abstractions.Tests.Serialization.Mocks
namespace Microsoft.Kiota.Abstractions.Tests.Serialization.Mocks;
public class TestEntity : IParsable, IAdditionalDataHolder
{
public class TestEntity : IParsable, IAdditionalDataHolder
/// <summary>Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.</summary>
public IDictionary<string, object> AdditionalData { get; set; }
/// <summary>Read-only.</summary>
public string Id { get; set; }
/// <summary>Read-only.</summary>
public TimeSpan? WorkDuration { get; set; }
/// <summary>Read-only.</summary>
public Date? BirthDay { get; set; }
/// <summary>Read-only.</summary>
public Time? StartWorkTime { get; set; }
/// <summary>Read-only.</summary>
public Time? EndWorkTime { get; set; }
/// <summary>Read-only.</summary>
public DateTimeOffset? CreatedDateTime { get; set; }
/// <summary>Read-only.</summary>
public string OfficeLocation { get; set; }
/// <summary>
/// Instantiates a new entity and sets the default values.
/// </summary>
public TestEntity()
{
/// <summary>Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.</summary>
public IDictionary<string, object> AdditionalData { get; set; }
/// <summary>Read-only.</summary>
public string Id { get; set; }
/// <summary>Read-only.</summary>
public TimeSpan? WorkDuration { get; set; }
/// <summary>Read-only.</summary>
public Date? BirthDay { get; set; }
/// <summary>Read-only.</summary>
public Time? StartWorkTime { get; set; }
/// <summary>Read-only.</summary>
public Time? EndWorkTime { get; set; }
/// <summary>Read-only.</summary>
public DateTimeOffset? CreatedDateTime { get; set; }
/// <summary>Read-only.</summary>
public string OfficeLocation { get; set; }
/// <summary>
/// Instantiates a new entity and sets the default values.
/// </summary>
public TestEntity()
{
AdditionalData = new Dictionary<string, object>();
}
/// <summary>
/// The deserialization information for the current model
/// </summary>
public virtual IDictionary<string, Action<IParseNode>> GetFieldDeserializers()
{
return new Dictionary<string, Action<IParseNode>> {
AdditionalData = new Dictionary<string, object>();
}
/// <summary>
/// The deserialization information for the current model
/// </summary>
public virtual IDictionary<string, Action<IParseNode>> GetFieldDeserializers()
{
return new Dictionary<string, Action<IParseNode>> {
{"id", n => { Id = n.GetStringValue(); } },
{"createdDateTime", n => { CreatedDateTime = n.GetDateTimeOffsetValue(); } },
{"officeLocation", n => { OfficeLocation = n.GetStringValue(); } },
Expand All @@ -43,32 +42,31 @@ public virtual IDictionary<string, Action<IParseNode>> GetFieldDeserializers()
{"startWorkTime", n => { StartWorkTime = n.GetTimeValue(); } },
{"endWorkTime", n => { EndWorkTime = n.GetTimeValue(); } },
};
}
/// <summary>
/// Serializes information the current object
/// <param name="writer">Serialization writer to use to serialize this model</param>
/// </summary>
public virtual void Serialize(ISerializationWriter writer)
{
_ = writer ?? throw new ArgumentNullException(nameof(writer));
writer.WriteStringValue("id", Id);
writer.WriteDateTimeOffsetValue("createdDateTime", CreatedDateTime);
writer.WriteStringValue("officeLocation", OfficeLocation);
writer.WriteTimeSpanValue("workDuration", WorkDuration);
writer.WriteDateValue("birthDay", BirthDay);
writer.WriteTimeValue("startWorkTime", StartWorkTime);
writer.WriteTimeValue("endWorkTime", EndWorkTime);
writer.WriteAdditionalData(AdditionalData);
}
public static TestEntity CreateFromDiscriminator(IParseNode parseNode)
}
/// <summary>
/// Serializes information the current object
/// <param name="writer">Serialization writer to use to serialize this model</param>
/// </summary>
public virtual void Serialize(ISerializationWriter writer)
{
_ = writer ?? throw new ArgumentNullException(nameof(writer));
writer.WriteStringValue("id", Id);
writer.WriteDateTimeOffsetValue("createdDateTime", CreatedDateTime);
writer.WriteStringValue("officeLocation", OfficeLocation);
writer.WriteTimeSpanValue("workDuration", WorkDuration);
writer.WriteDateValue("birthDay", BirthDay);
writer.WriteTimeValue("startWorkTime", StartWorkTime);
writer.WriteTimeValue("endWorkTime", EndWorkTime);
writer.WriteAdditionalData(AdditionalData);
}
public static TestEntity CreateFromDiscriminator(IParseNode parseNode)
{
var discriminatorValue = parseNode.GetChildNode("@odata.type")?.GetStringValue();
return discriminatorValue switch
{
var discriminatorValue = parseNode.GetChildNode("@odata.type")?.GetStringValue();
return discriminatorValue switch
{
"microsoft.graph.user" => new TestEntity(),
"microsoft.graph.group" => new TestEntity(),
_ => new TestEntity(),
};
}
"microsoft.graph.user" => new TestEntity(),
"microsoft.graph.group" => new TestEntity(),
_ => new TestEntity(),
};
}
}
2 changes: 1 addition & 1 deletion src/Microsoft.Kiota.Abstractions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<PackageProjectUrl>https://aka.ms/kiota/docs</PackageProjectUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<Deterministic>true</Deterministic>
<VersionPrefix>1.7.0</VersionPrefix>
<VersionPrefix>1.7.1</VersionPrefix>
<VersionSuffix></VersionSuffix>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
Expand Down
39 changes: 37 additions & 2 deletions src/RequestInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
#if NET5_0_OR_GREATER
Expand Down Expand Up @@ -116,7 +118,7 @@ public Uri URI
Guid guid => guid.ToString("D"),// Default of 32 digits separated by hyphens
Date date => date.ToString(), //Default to string format of the custom date object
Time time => time.ToString(), //Default to string format of the custom time object
_ => value,//return object as is as the ToString method is good enough.
_ => ReplaceEnumValueByStringRepresentation(value),//return object as is as the ToString method is good enough.
};

/// <summary>
Expand Down Expand Up @@ -161,9 +163,42 @@ public void AddQueryParameters<T>(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!, property.Value!);
QueryParameters.AddOrReplace(property.Name!, ReplaceEnumValueByStringRepresentation(property.Value!));
}
}
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;
}
#if NET5_0_OR_GREATER
private static string? GetEnumName<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(T value) where T : Enum
#else
private static string? GetEnumName<T>(T value) where T : Enum
#endif
{
var type = value.GetType();

if(Enum.GetName(type, value) is not { } name)
throw new ArgumentException($"Invalid Enum value {value} for enum of type {type}");

if(type.GetMember(name).FirstOrDefault()?.GetCustomAttribute<EnumMemberAttribute>() is { } attribute)
return attribute.Value;

return name.ToFirstCharacterLowerCase();
}
/// <summary>
/// The Request Headers.
/// </summary>
Expand Down

0 comments on commit e7d144d

Please sign in to comment.