Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
145 changes: 145 additions & 0 deletions Activout.RestClient.Test/DictionaryParameterTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
Expand Down Expand Up @@ -176,6 +178,134 @@ public async Task TestBackwardCompatibilityWithNonDictionaryParams()
// assert
_mockHttp.VerifyNoOutstandingExpectation();
}

[Fact]
public async Task TestQueryParamDictionaryWithDateTime()
{
// arrange
var service = CreateRestClientBuilder().Build<ITestService>();
var testDate = new DateTime(2023, 12, 25, 14, 30, 45, DateTimeKind.Utc);
var expectedDateString = testDate.ToString("o"); // ISO 8601 format
var queryParams = new Dictionary<string, object>
{
["stringParam"] = "value1",
["dateParam"] = testDate
};

_mockHttp
.When("https://example.com/api/test")
.WithExactQueryString($"stringParam=value1&dateParam={Uri.EscapeDataString(expectedDateString)}")
.Respond("application/json", "{}");

// act
await service.TestQueryParamObjectDictionary(queryParams);

// assert
_mockHttp.VerifyNoOutstandingExpectation();
}

[Fact]
public async Task TestFormParamDictionaryWithDateTime()
{
// arrange
var service = CreateRestClientBuilder().Build<ITestService>();
var testDate = new DateTime(2023, 12, 25, 14, 30, 45, DateTimeKind.Utc);
var expectedDateString = testDate.ToString("o"); // ISO 8601 format
var formParams = new Dictionary<string, object>
{
["stringField"] = "value1",
["dateField"] = testDate
};

_mockHttp
.When(HttpMethod.Post, "https://example.com/api/test")
.WithFormData("stringField", "value1")
.WithFormData("dateField", expectedDateString)
.Respond("application/json", "{}");

// act
await service.TestFormParamObjectDictionary(formParams);

// assert
_mockHttp.VerifyNoOutstandingExpectation();
}

[Fact]
public async Task TestHeaderParamDictionaryWithDateTime()
{
// arrange
var service = CreateRestClientBuilder().Build<ITestService>();
var testDate = new DateTime(2023, 12, 25, 14, 30, 45, DateTimeKind.Utc);
var expectedDateString = testDate.ToString("o"); // ISO 8601 format
var headers = new Dictionary<string, object>
{
["X-String-Header"] = "value1",
["X-Date-Header"] = testDate
};

_mockHttp
.When("https://example.com/api/test")
.WithHeaders("X-String-Header", "value1")
.WithHeaders("X-Date-Header", expectedDateString)
.Respond("application/json", "{}");

// act
await service.TestHeaderParamObjectDictionary(headers);

// assert
_mockHttp.VerifyNoOutstandingExpectation();
}

[Fact]
public async Task TestGenericDictionaryWithDateTime()
{
// arrange
var service = CreateRestClientBuilder().Build<ITestService>();
var testDate = new DateTime(2023, 12, 25, 14, 30, 45, DateTimeKind.Utc);
var expectedDateString = testDate.ToString("o"); // ISO 8601 format
var queryParams = new Dictionary<string, DateTime>
{
["startDate"] = testDate,
["endDate"] = testDate.AddDays(1)
};

_mockHttp
.When("https://example.com/api/test")
.WithExactQueryString($"startDate={Uri.EscapeDataString(expectedDateString)}&endDate={Uri.EscapeDataString(testDate.AddDays(1).ToString("o"))}")
.Respond("application/json", "{}");

// act
await service.TestQueryParamDateTimeDictionary(queryParams);

// assert
_mockHttp.VerifyNoOutstandingExpectation();
}

[Fact]
public async Task TestNonGenericDictionaryWithDateTime()
{
// arrange
var service = CreateRestClientBuilder().Build<ITestService>();
var testDate = new DateTime(2023, 12, 25, 14, 30, 45, DateTimeKind.Utc);
var expectedDateString = testDate.ToString("o"); // ISO 8601 format

var queryParams = new Hashtable
{
["stringParam"] = "value1",
["dateParam"] = testDate
};

_mockHttp
.When("https://example.com/api/test")
.WithExactQueryString($"stringParam=value1&dateParam={Uri.EscapeDataString(expectedDateString)}")
.Respond("application/json", "{}");

// act
await service.TestQueryParamNonGenericDictionary(queryParams);

// assert
_mockHttp.VerifyNoOutstandingExpectation();
}
}

public interface ITestService
Expand All @@ -194,4 +324,19 @@ public interface ITestService

[Get("test")]
Task TestRegularParam([QueryParam("regularParam")] string regularParam);

[Get("test")]
Task TestQueryParamObjectDictionary([QueryParam] Dictionary<string, object> queryParams);

[Post("test")]
Task TestFormParamObjectDictionary([FormParam] Dictionary<string, object> formParams);

[Get("test")]
Task TestHeaderParamObjectDictionary([HeaderParam] Dictionary<string, object> headers);

[Get("test")]
Task TestQueryParamDateTimeDictionary([QueryParam] Dictionary<string, DateTime> queryParams);

[Get("test")]
Task TestQueryParamNonGenericDictionary([QueryParam] IDictionary queryParams);
}
15 changes: 12 additions & 3 deletions Activout.RestClient/Implementation/RequestHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,15 @@ private void SetHeaders(HttpRequestMessage request, List<KeyValuePair<string, ob
headers.ForEach(p => request.Headers.Add(p.Key, p.Value.ToString()));
}

private string ConvertValueToString(object value)
{
if (value == null)
return null;

var converter = _context.ParamConverterManager.GetConverter(value.GetType());
return converter?.ToString(value) ?? value.ToString();
}

private CancellationToken GetParams(
object[] args,
Dictionary<string, object> pathParams,
Expand Down Expand Up @@ -300,7 +309,7 @@ private CancellationToken GetParams(
foreach (DictionaryEntry entry in dictionary)
{
var key = entry.Key?.ToString();
var value = entry.Value?.ToString();
var value = ConvertValueToString(entry.Value);
if (key != null && value != null)
{
queryParams.Add(Uri.EscapeDataString(key) + "=" + Uri.EscapeDataString(value));
Expand All @@ -321,7 +330,7 @@ private CancellationToken GetParams(
foreach (DictionaryEntry entry in dictionary)
{
var key = entry.Key?.ToString();
var value = entry.Value?.ToString();
var value = ConvertValueToString(entry.Value);
if (key != null && value != null)
{
formParams.Add(new KeyValuePair<string, string>(key, value));
Expand All @@ -342,7 +351,7 @@ private CancellationToken GetParams(
foreach (DictionaryEntry entry in dictionary)
{
var key = entry.Key?.ToString();
var value = entry.Value?.ToString();
var value = ConvertValueToString(entry.Value);
if (key != null && value != null)
{
headers.AddOrReplaceHeader(key, value, headerParamAttribute.Replace);
Expand Down
4 changes: 2 additions & 2 deletions Activout.RestClient/ParamConverter/IParamConverter.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using System.Reflection;
using System;

namespace Activout.RestClient.ParamConverter
{
public interface IParamConverter
{
bool CanConvert(ParameterInfo parameterInfo);
bool CanConvert(Type type);
string ToString(object value);
}
}
4 changes: 3 additions & 1 deletion Activout.RestClient/ParamConverter/IParamConverterManager.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System.Reflection;
using System;
using System.Reflection;

namespace Activout.RestClient.ParamConverter
{
public interface IParamConverterManager
{
IParamConverter GetConverter(ParameterInfo parameterInfo);
IParamConverter GetConverter(Type type);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Reflection;

namespace Activout.RestClient.ParamConverter.Implementation
{
Expand All @@ -14,9 +13,9 @@ public static long ToUnixTime(this DateTime date)

public class DateTimeEpochParamConverter : IParamConverter
{
public bool CanConvert(ParameterInfo parameterInfo)
public bool CanConvert(Type type)
{
return parameterInfo.ParameterType == typeof(DateTime);
return type == typeof(DateTime);
}

public string ToString(object value)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using System;
using System.Reflection;

namespace Activout.RestClient.ParamConverter.Implementation
{
public class DateTimeIso8601ParamConverter : IParamConverter
{
public bool CanConvert(ParameterInfo parameterInfo)
public bool CanConvert(Type type)
{
return parameterInfo.ParameterType == typeof(DateTime);
return type == typeof(DateTime);
}

public string ToString(object value)
Expand Down
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change IParamConverter.CanConvert() to take a Type instead of creating a fake ParameterInfo instance.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot please Change IParamConverter.CanConvert() to take a Type instead of creating a fake ParameterInfo instance in ParamConverterManager

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed IParamConverter.CanConvert() to take Type instead of ParameterInfo and removed the TypeOnlyParameterInfo helper class. All implementations now use Type directly, making the code cleaner and eliminating the fake ParameterInfo instances. Commit: 1bff500

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Activout.RestClient.ParamConverter.Implementation
Expand All @@ -13,10 +14,15 @@ public ParamConverterManager()
}

public IParamConverter GetConverter(ParameterInfo parameterInfo)
{
return GetConverter(parameterInfo.ParameterType);
}

public IParamConverter GetConverter(Type type)
{
foreach (var paramConverter in ParamConverters)
{
if (paramConverter.CanConvert(parameterInfo))
if (paramConverter.CanConvert(type))
{
return paramConverter;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using System.Reflection;
using System;

namespace Activout.RestClient.ParamConverter.Implementation
{
public class ToStringParamConverter : IParamConverter
{
public bool CanConvert(ParameterInfo parameterInfo)
public bool CanConvert(Type type)
{
return true;
}
Expand Down