diff --git a/Activout.RestClient.Test/DictionaryParameterTests.cs b/Activout.RestClient.Test/DictionaryParameterTests.cs index c5568b6..6edf1bb 100644 --- a/Activout.RestClient.Test/DictionaryParameterTests.cs +++ b/Activout.RestClient.Test/DictionaryParameterTests.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections; using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; @@ -176,6 +178,134 @@ public async Task TestBackwardCompatibilityWithNonDictionaryParams() // assert _mockHttp.VerifyNoOutstandingExpectation(); } + + [Fact] + public async Task TestQueryParamDictionaryWithDateTime() + { + // arrange + var service = CreateRestClientBuilder().Build(); + var testDate = new DateTime(2023, 12, 25, 14, 30, 45, DateTimeKind.Utc); + var expectedDateString = testDate.ToString("o"); // ISO 8601 format + var queryParams = new Dictionary + { + ["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(); + var testDate = new DateTime(2023, 12, 25, 14, 30, 45, DateTimeKind.Utc); + var expectedDateString = testDate.ToString("o"); // ISO 8601 format + var formParams = new Dictionary + { + ["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(); + var testDate = new DateTime(2023, 12, 25, 14, 30, 45, DateTimeKind.Utc); + var expectedDateString = testDate.ToString("o"); // ISO 8601 format + var headers = new Dictionary + { + ["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(); + var testDate = new DateTime(2023, 12, 25, 14, 30, 45, DateTimeKind.Utc); + var expectedDateString = testDate.ToString("o"); // ISO 8601 format + var queryParams = new Dictionary + { + ["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(); + 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 @@ -194,4 +324,19 @@ public interface ITestService [Get("test")] Task TestRegularParam([QueryParam("regularParam")] string regularParam); + + [Get("test")] + Task TestQueryParamObjectDictionary([QueryParam] Dictionary queryParams); + + [Post("test")] + Task TestFormParamObjectDictionary([FormParam] Dictionary formParams); + + [Get("test")] + Task TestHeaderParamObjectDictionary([HeaderParam] Dictionary headers); + + [Get("test")] + Task TestQueryParamDateTimeDictionary([QueryParam] Dictionary queryParams); + + [Get("test")] + Task TestQueryParamNonGenericDictionary([QueryParam] IDictionary queryParams); } \ No newline at end of file diff --git a/Activout.RestClient/Implementation/RequestHandler.cs b/Activout.RestClient/Implementation/RequestHandler.cs index 58b48ac..9d1971b 100644 --- a/Activout.RestClient/Implementation/RequestHandler.cs +++ b/Activout.RestClient/Implementation/RequestHandler.cs @@ -247,6 +247,15 @@ private void SetHeaders(HttpRequestMessage request, List 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 pathParams, @@ -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)); @@ -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(key, value)); @@ -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); diff --git a/Activout.RestClient/ParamConverter/IParamConverter.cs b/Activout.RestClient/ParamConverter/IParamConverter.cs index d8fb4a4..44e39b1 100644 --- a/Activout.RestClient/ParamConverter/IParamConverter.cs +++ b/Activout.RestClient/ParamConverter/IParamConverter.cs @@ -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); } } \ No newline at end of file diff --git a/Activout.RestClient/ParamConverter/IParamConverterManager.cs b/Activout.RestClient/ParamConverter/IParamConverterManager.cs index 334b7b8..6da3814 100644 --- a/Activout.RestClient/ParamConverter/IParamConverterManager.cs +++ b/Activout.RestClient/ParamConverter/IParamConverterManager.cs @@ -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); } } \ No newline at end of file diff --git a/Activout.RestClient/ParamConverter/Implementation/DateTimeEpochParamConverter.cs b/Activout.RestClient/ParamConverter/Implementation/DateTimeEpochParamConverter.cs index 7f255d3..924be88 100644 --- a/Activout.RestClient/ParamConverter/Implementation/DateTimeEpochParamConverter.cs +++ b/Activout.RestClient/ParamConverter/Implementation/DateTimeEpochParamConverter.cs @@ -1,5 +1,4 @@ using System; -using System.Reflection; namespace Activout.RestClient.ParamConverter.Implementation { @@ -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) diff --git a/Activout.RestClient/ParamConverter/Implementation/DateTimeIso8601ParamConverter.cs b/Activout.RestClient/ParamConverter/Implementation/DateTimeIso8601ParamConverter.cs index 528e643..6827615 100644 --- a/Activout.RestClient/ParamConverter/Implementation/DateTimeIso8601ParamConverter.cs +++ b/Activout.RestClient/ParamConverter/Implementation/DateTimeIso8601ParamConverter.cs @@ -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) diff --git a/Activout.RestClient/ParamConverter/Implementation/ParamConverterManager.cs b/Activout.RestClient/ParamConverter/Implementation/ParamConverterManager.cs index 0ce7d9f..807b4f9 100644 --- a/Activout.RestClient/ParamConverter/Implementation/ParamConverterManager.cs +++ b/Activout.RestClient/ParamConverter/Implementation/ParamConverterManager.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Reflection; namespace Activout.RestClient.ParamConverter.Implementation @@ -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; } diff --git a/Activout.RestClient/ParamConverter/Implementation/ToStringParamConverter.cs b/Activout.RestClient/ParamConverter/Implementation/ToStringParamConverter.cs index a2b0b32..e9e5af1 100644 --- a/Activout.RestClient/ParamConverter/Implementation/ToStringParamConverter.cs +++ b/Activout.RestClient/ParamConverter/Implementation/ToStringParamConverter.cs @@ -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; }