Skip to content

Commit 30887ea

Browse files
authored
[fix] Handle serializing array/list parameters in GET/DELETE query strings (#601)
1 parent c81ef8c commit 30887ea

File tree

3 files changed

+62
-10
lines changed

3 files changed

+62
-10
lines changed

EasyPost.Tests/HttpTests/RequestTest.cs

+22-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Generic;
2+
using System.Net.Http;
23
using EasyPost._base;
34
using EasyPost.Http;
45
using Xunit;
@@ -13,9 +14,29 @@ public class RequestTest
1314
[Fact]
1415
public void TestRequestDisposal()
1516
{
16-
Request request = new("https://google.com", "not_a_real_endpoint", Method.Get, ApiVersion.V2, new Dictionary<string, object> { { "key", "value" } }, new Dictionary<string, string> { { "header_key", "header_value" } });
17+
Request request = new("https://example.com", "not_a_real_endpoint", Method.Get, ApiVersion.V2, new Dictionary<string, object> { { "key", "value" } }, new Dictionary<string, string> { { "header_key", "header_value" } });
1718
CustomAssertions.DoesNotThrow(() => request.Dispose());
1819
}
1920

21+
[Fact]
22+
public void TestQueryParameterList()
23+
{
24+
var parameters = new Dictionary<string, object>
25+
{
26+
{ "key1", new List<string> { "value1", "value2" } },
27+
{ "key2", "value3" }
28+
};
29+
30+
Request request = new("https://example.com", "not_a_real_endpoint", Method.Get, ApiVersion.V2, parameters, new Dictionary<string, string> { { "header_key", "header_value" } });
31+
32+
HttpRequestMessage httpRequestMessage = request.AsHttpRequestMessage();
33+
34+
string queryString = httpRequestMessage.RequestUri?.Query;
35+
36+
Assert.Contains("key1[]=value1", queryString); // Does not account for URL encoding
37+
Assert.Contains("key1[]=value2", queryString); // Does not account for URL encoding
38+
Assert.Contains("key2=value3", queryString); // Does not account for URL encoding
39+
}
40+
2041
#endregion
2142
}

EasyPost/Http/Request.cs

+39-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
34
using System.Collections.Specialized;
45
using System.Net.Http;
@@ -115,6 +116,9 @@ private void BuildQueryParameters()
115116
// add query parameters
116117
NameValueCollection query = HttpUtility.ParseQueryString(string.Empty);
117118

119+
// Separately store the list parameters to add them last
120+
List<string> listParameters = new();
121+
118122
// build query string from parameters
119123
foreach (KeyValuePair<string, object> param in _parameters)
120124
{
@@ -124,26 +128,52 @@ private void BuildQueryParameters()
124128
continue;
125129
}
126130

127-
query[param.Key] = param.Value switch
131+
var @switch = new SwitchCase
128132
{
129-
// TODO: Handle special conversions for other types
130-
// DateTime dateTime => dateTime.ToString("o", CultureInfo.InvariantCulture),
131-
var _ => param.Value.ToString(),
133+
{ param.Value is IList, () => listParameters = AddListQueryParameter(listParameters, param.Key, (IList)param.Value) },
134+
{ SwitchCaseScenario.Default, () => query[param.Key] = param.Value.ToString() },
132135
};
136+
@switch.MatchFirstTrue();
133137
}
134138

135-
// short circuit if no query parameters
136-
if (query.Count == 0)
139+
// Finalize the query string
140+
string queryString = query.ToString() ?? string.Empty;
141+
142+
// Add list parameters to the query string
143+
string parameterCharacter = queryString.Length == 0 ? "?" : "&";
144+
foreach (string pair in listParameters)
137145
{
138-
return;
146+
queryString += $"{parameterCharacter}{pair}";
147+
parameterCharacter = "&";
139148
}
140149

141150
// rebuild the request URL with the query string appended
142151
var uriBuilder = new UriBuilder(_requestMessage.RequestUri!)
143152
{
144-
Query = query.ToString(),
153+
Query = queryString,
145154
};
146-
_requestMessage.RequestUri = new Uri(uriBuilder.ToString());
155+
156+
// _requestMessage.RequestUri = new Uri(uriBuilder.ToString());
157+
_requestMessage.RequestUri = uriBuilder.Uri;
158+
}
159+
160+
private static List<string> AddListQueryParameter(List<string> pairs, string key, IList value)
161+
{
162+
string keyPrefix = $"{HttpUtility.UrlEncode(key)}[]";
163+
// ReSharper disable once LoopCanBeConvertedToQuery
164+
foreach (object? item in value)
165+
{
166+
string? itemString = item?.ToString();
167+
if (itemString == null)
168+
{
169+
continue;
170+
}
171+
172+
string pair = $"{keyPrefix}={HttpUtility.UrlEncode(itemString)}";
173+
pairs.Add(pair);
174+
}
175+
176+
return pairs;
147177
}
148178

149179
/// <inheritdoc cref="EasyPostClient._isDisposed"/>

EasyPost/Parameters/Tracker/All.cs

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public class All : BaseAllParameters<Models.API.Tracker>
8484
EndDatetime = dictionary.GetOrNull<string>("end_datetime"),
8585
Carrier = dictionary.GetOrNull<string>("carrier"),
8686
TrackingCode = dictionary.GetOrNull<string>("tracking_code"),
87+
TrackingCodes = dictionary.GetOrNull<List<string>>("tracking_codes"),
8788
};
8889
}
8990
}

0 commit comments

Comments
 (0)