Skip to content

Commit 83f3f5c

Browse files
authored
fix(parameters): resolve freeze issue when updating auth model concurrently (#120)
- Replaced List with `ConcurrentBag` for thread-safe parameter collection - Updated unit tests to support unordered parameter comparison - Fixed incorrect README.md file path in `.csproj`
1 parent d970200 commit 83f3f5c

File tree

7 files changed

+57
-23
lines changed

7 files changed

+57
-23
lines changed

APIMatic.Core.Test/Api/HttpPost/ApiCallPostTest.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Net.Http.Json;
66
using System.Text;
77
using APIMatic.Core.Test.MockTypes.Models;
8+
using APIMatic.Core.Test.Utilities;
89
using APIMatic.Core.Utilities;
910
using NUnit.Framework;
1011
using RichardSzalay.MockHttp;
@@ -62,24 +63,24 @@ public void ApiCall_PostMultipleBody_OKResponse()
6263
//Arrange
6364
var text1 = "Post body response.";
6465
var text2 = "Second item.";
65-
var expectedResponse = CoreHelper.JsonSerialize(new Dictionary<string, object>
66+
var expectedResponse = new Dictionary<string, string>
6667
{
6768
{ "key1", text1 },
6869
{ "key2", text2 },
69-
});
70+
};
7071
var url = "/apicall/multiple-body-post/200";
7172

7273
var expected = new ServerResponse()
7374
{
74-
Message = expectedResponse,
75+
Message = CoreHelper.JsonSerialize(expectedResponse),
7576
Passed = true,
7677
};
7778

7879
var content = JsonContent.Create(expected);
7980
handlerMock.When(GetCompleteUrl(url))
8081
.With(req =>
8182
{
82-
Assert.AreEqual(expectedResponse, req.Content.ReadAsStringAsync().Result);
83+
Assert.AreEqual(expectedResponse, req.Content.ReadAsStringAsync().Result.ToJsonToDictionary());
8384
Assert.AreEqual("application/json", req.Content.Headers.ContentType.MediaType);
8485
Assert.AreEqual("utf-8", req.Content.Headers.ContentType.CharSet);
8586
Assert.AreEqual("application/json", req.Headers.Accept.ToString());
@@ -362,7 +363,8 @@ public void ApiCall_PostFormData_OKResponse()
362363
var text2 = "Value in KeyA";
363364
var text3 = "Value in KeyB";
364365
var url = "/apicall/form-post/200";
365-
var expectedFormData = "key+1=Post+form+data.&keyA=Value+in+KeyA&keyB=Value+in+KeyB";
366+
var expectedFormData =
367+
"key+1=Post+form+data.&keyA=Value+in+KeyA&keyB=Value+in+KeyB".ToQueryStringDictionary();
366368

367369
var expected = new ServerResponse()
368370
{
@@ -374,7 +376,7 @@ public void ApiCall_PostFormData_OKResponse()
374376
handlerMock.When(GetCompleteUrl(url))
375377
.With(req =>
376378
{
377-
Assert.AreEqual(expectedFormData, req.Content.ReadAsStringAsync().Result);
379+
Assert.AreEqual(expectedFormData, req.Content.ReadAsStringAsync().Result.ToQueryStringDictionary());
378380
Assert.AreEqual("application/x-www-form-urlencoded", req.Content.Headers.ContentType.MediaType);
379381
Assert.IsNull(req.Content.Headers.ContentType.CharSet);
380382
Assert.AreEqual("application/json", req.Headers.Accept.ToString());
@@ -414,7 +416,8 @@ public void ApiCall_PostQueryData_OKResponse()
414416
var text3 = "Value in KeyB";
415417
var url = "/apicall/query-post/200";
416418
var queryString = "?key%201=Post%20form%20data.&keyA=Value%20in%20KeyA&keyB=Value%20in%20KeyB";
417-
var expectedQueryString = "?key 1=Post form data.&keyA=Value in KeyA&keyB=Value in KeyB";
419+
var expectedQueryString =
420+
"?key 1=Post form data.&keyA=Value in KeyA&keyB=Value in KeyB".ToQueryStringDictionary();
418421

419422
var expected = new ServerResponse()
420423
{
@@ -426,7 +429,7 @@ public void ApiCall_PostQueryData_OKResponse()
426429
handlerMock.When(GetCompleteUrl(url + queryString))
427430
.With(req =>
428431
{
429-
Assert.AreEqual(GetCompleteUrl(url + expectedQueryString), req.RequestUri.ToString());
432+
Assert.AreEqual(expectedQueryString, req.RequestUri?.Query.ToString().ToQueryStringDictionary());
430433
return true;
431434
})
432435
.Respond(HttpStatusCode.OK, content);

APIMatic.Core.Test/AuthenticationTest.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using System.Net.Http;
45
using System.Threading.Tasks;
56
using APIMatic.Core.Authentication;
@@ -150,6 +151,15 @@ await globalConfiguration.GlobalRequestBuilder()
150151
[Test]
151152
public void Multiple_Authentication_AND_All_Missing_Validation_Failure()
152153
{
154+
var expectedLines = new[]
155+
{
156+
"Following authentication credentials are required:",
157+
"-> Missing required query field: API-KEY",
158+
"-> Missing required query field: TOKEN",
159+
"-> Missing required header field: API-KEY",
160+
"-> Missing required header field: TOKEN"
161+
}.OrderBy(line => line);
162+
153163
var globalConfiguration = new GlobalConfiguration.Builder()
154164
.ServerUrls(new Dictionary<Enum, string>
155165
{
@@ -170,11 +180,7 @@ public void Multiple_Authentication_AND_All_Missing_Validation_Failure()
170180
.Add("header"))
171181
.Build());
172182

173-
Assert.AreEqual("Following authentication credentials are required:\n" +
174-
"-> Missing required query field: API-KEY\n" +
175-
"-> Missing required query field: TOKEN\n" +
176-
"-> Missing required header field: API-KEY\n" +
177-
"-> Missing required header field: TOKEN", exp.Message);
183+
Assert.AreEqual(expectedLines, exp?.Message.Split('\n').OrderBy(line => line));
178184
}
179185

180186
[Test]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Web;
4+
using Newtonsoft.Json;
5+
6+
namespace APIMatic.Core.Test.Utilities
7+
{
8+
internal static class Extensions
9+
{
10+
public static Dictionary<string, string> ToQueryStringDictionary(this string data)
11+
{
12+
var parsedQueryString = HttpUtility.ParseQueryString(data);
13+
return parsedQueryString.AllKeys.ToDictionary(k => k, k => parsedQueryString[k]);
14+
}
15+
16+
public static Dictionary<string, string> ToJsonToDictionary(this string data)
17+
{
18+
return JsonConvert.DeserializeObject<Dictionary<string, string>>(data);
19+
}
20+
}
21+
}

APIMatic.Core.Test/Utilities/TestHelperTest.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ namespace APIMatic.Core.Test.Utilities
99
[TestFixture]
1010
internal class TestHelperTest
1111
{
12-
internal const string OBJECT_STRING = "[{\"name\":\"Shahid Khaliq\",\"age\":5147483645,\"address\":\"H # 531, S # 20\",\"uid\":\"123321\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\",\"salary\":20000,\"department\":\"Software Development\",\"joiningDay\":\"Saturday\",\"workingDays\":[\"Monday\",\"Tuesday\",\"Friday\"],\"boss\":{\"personType\":\"Boss\",\"name\":\"Zeeshan Ejaz\",\"age\":5147483645,\"address\":\"H # 531, S # 20\",\"uid\":\"123321\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\",\"salary\":20000,\"department\":\"Software Development\",\"joiningDay\":\"Saturday\",\"workingDays\":[\"Monday\",\"Tuesday\",\"Friday\"],\"dependents\":[{\"name\":\"Future Wife\",\"age\":5147483649,\"address\":\"H # 531, S # 20\",\"uid\":\"123412\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"},{\"name\":\"Future Kid\",\"age\":5147483648,\"address\":\"H # 531, S # 20\",\"uid\":\"312341\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"}],\"hiredAt\":\"Sun, 06 Nov 1994 08:49:37 GMT\",\"promotedAt\":1484719381},\"dependents\":[{\"name\":\"Future Wife\",\"age\":5147483649,\"address\":\"H # 531, S # 20\",\"uid\":\"123412\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"},{\"name\":\"Future Kid\",\"age\":5147483648,\"address\":\"H # 531, S # 20\",\"uid\":\"312341\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"}],\"hiredAt\":\"Sun, 06 Nov 1994 08:49:37 GMT\"},{\"name\":\"Shahid Khaliq\",\"age\":5147483645,\"address\":\"H # 531, S # 20\",\"uid\":\"123321\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\",\"salary\":20000,\"department\":\"Software Development\",\"joiningDay\":\"Saturday\",\"workingDays\":[\"Monday\",\"Tuesday\",\"Friday\"],\"boss\":{\"personType\":\"Boss\",\"name\":\"Zeeshan Ejaz\",\"age\":5147483645,\"address\":\"H # 531, S # 20\",\"uid\":\"123321\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\",\"salary\":20000,\"department\":\"Software Development\",\"joiningDay\":\"Saturday\",\"workingDays\":[\"Monday\",\"Tuesday\",\"Friday\"],\"dependents\":[{\"name\":\"Future Wife\",\"age\":5147483649,\"address\":\"H # 531, S # 20\",\"uid\":\"123412\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"},{\"name\":\"Future Kid\",\"age\":5147483648,\"address\":\"H # 531, S # 20\",\"uid\":\"312341\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"}],\"hiredAt\":\"Sun, 06 Nov 1994 08:49:37 GMT\",\"promotedAt\":1484719381},\"dependents\":[{\"name\":\"Future Wife\",\"age\":5147483649,\"address\":\"H # 531, S # 20\",\"uid\":\"123412\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"},{\"name\":\"Future Kid\",\"age\":5147483648,\"address\":\"H # 531, S # 20\",\"uid\":\"312341\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"}],\"hiredAt\":\"Sun, 06 Nov 1994 08:49:37 GMT\"}]";
12+
private const string OBJECT_STRING = "[{\"name\":\"Shahid Khaliq\",\"age\":5147483645,\"address\":\"H # 531, S # 20\",\"uid\":\"123321\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\",\"salary\":20000,\"department\":\"Software Development\",\"joiningDay\":\"Saturday\",\"workingDays\":[\"Monday\",\"Tuesday\",\"Friday\"],\"boss\":{\"personType\":\"Boss\",\"name\":\"Zeeshan Ejaz\",\"age\":5147483645,\"address\":\"H # 531, S # 20\",\"uid\":\"123321\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\",\"salary\":20000,\"department\":\"Software Development\",\"joiningDay\":\"Saturday\",\"workingDays\":[\"Monday\",\"Tuesday\",\"Friday\"],\"dependents\":[{\"name\":\"Future Wife\",\"age\":5147483649,\"address\":\"H # 531, S # 20\",\"uid\":\"123412\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"},{\"name\":\"Future Kid\",\"age\":5147483648,\"address\":\"H # 531, S # 20\",\"uid\":\"312341\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"}],\"hiredAt\":\"Sun, 06 Nov 1994 08:49:37 GMT\",\"promotedAt\":1484719381},\"dependents\":[{\"name\":\"Future Wife\",\"age\":5147483649,\"address\":\"H # 531, S # 20\",\"uid\":\"123412\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"},{\"name\":\"Future Kid\",\"age\":5147483648,\"address\":\"H # 531, S # 20\",\"uid\":\"312341\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"}],\"hiredAt\":\"Sun, 06 Nov 1994 08:49:37 GMT\"},{\"name\":\"Shahid Khaliq\",\"age\":5147483645,\"address\":\"H # 531, S # 20\",\"uid\":\"123321\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\",\"salary\":20000,\"department\":\"Software Development\",\"joiningDay\":\"Saturday\",\"workingDays\":[\"Monday\",\"Tuesday\",\"Friday\"],\"boss\":{\"personType\":\"Boss\",\"name\":\"Zeeshan Ejaz\",\"age\":5147483645,\"address\":\"H # 531, S # 20\",\"uid\":\"123321\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\",\"salary\":20000,\"department\":\"Software Development\",\"joiningDay\":\"Saturday\",\"workingDays\":[\"Monday\",\"Tuesday\",\"Friday\"],\"dependents\":[{\"name\":\"Future Wife\",\"age\":5147483649,\"address\":\"H # 531, S # 20\",\"uid\":\"123412\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"},{\"name\":\"Future Kid\",\"age\":5147483648,\"address\":\"H # 531, S # 20\",\"uid\":\"312341\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"}],\"hiredAt\":\"Sun, 06 Nov 1994 08:49:37 GMT\",\"promotedAt\":1484719381},\"dependents\":[{\"name\":\"Future Wife\",\"age\":5147483649,\"address\":\"H # 531, S # 20\",\"uid\":\"123412\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"},{\"name\":\"Future Kid\",\"age\":5147483648,\"address\":\"H # 531, S # 20\",\"uid\":\"312341\",\"birthday\":\"1994-02-13\",\"birthtime\":\"1994-02-13T14:01:54.9571247Z\"}],\"hiredAt\":\"Sun, 06 Nov 1994 08:49:37 GMT\"}]";
1313

14-
internal const string NEW_OBJECT = "{\"personType\":\"Empl\",\"hiredAt\":\"Sun, 06 Nov 1994 08:49:37 GMT\"}";
14+
private const string NEW_OBJECT = "{\"personType\":\"Empl\",\"hiredAt\":\"Sun, 06 Nov 1994 08:49:37 GMT\"}";
1515

16-
internal const string NEW_OBJECT_DIFFERENT_VALUES = "{\"personType\":\"not Empl\",\"hiredAt\":\"Sun, 06 Nov 1994 08:49:37 GMT\"}";
16+
private const string NEW_OBJECT_DIFFERENT_VALUES = "{\"personType\":\"not Empl\",\"hiredAt\":\"Sun, 06 Nov 1994 08:49:37 GMT\"}";
1717

18-
internal const string MIXED_TYPE_LIST = "[{\"personType\":\"boss\"},true,\"some string\"]";
18+
private const string MIXED_TYPE_LIST = "[{\"personType\":\"boss\"},true,\"some string\"]";
1919

2020
[Test]
2121
public void IsArrayOfJsonObjectsProperSubsetOf_SameLeftRightObject()

APIMatic.Core/APIMatic.Core.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<Description>Core logic and the utilities for the APIMatic's C# SDK</Description>
99
<Copyright>Copyright APIMatic Ltd.</Copyright>
1010
<PackageLicenseFile>LICENSE</PackageLicenseFile>
11-
<PackageReadmeFile>README.md</PackageReadmeFile>
11+
<PackageReadmeFile>../README.md</PackageReadmeFile>
1212
<PackageReleaseNotes>
1313
APIMatic's goal is to provide a stable runtime that powers all functionality of our C# SDKs and we are continuously trying to improve the API developer experience with each release of this package. Check out the release notes for all the improvements that are added to this package over time.
1414

APIMatic.Core/Request/Parameters/Parameter.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Copyright (c) APIMatic. All rights reserved.
33
// </copyright>
44
using System;
5+
using System.Collections.Concurrent;
56
using System.Collections.Generic;
67
using System.Linq;
78

@@ -76,7 +77,7 @@ internal virtual void Validate()
7677
/// </summary>
7778
public class Builder
7879
{
79-
private readonly List<Parameter> parameters = new List<Parameter>();
80+
private readonly ConcurrentBag<Parameter> parameters = new ConcurrentBag<Parameter>();
8081

8182
internal Builder() { }
8283

@@ -192,7 +193,7 @@ public Builder Body(Action<BodyParam> _body)
192193
internal Builder Validate()
193194
{
194195
var missingArgErrors = new List<string>();
195-
parameters.ForEach(p =>
196+
foreach (var p in parameters)
196197
{
197198
try
198199
{
@@ -202,7 +203,7 @@ internal Builder Validate()
202203
{
203204
missingArgErrors.Add(exp.Message);
204205
}
205-
});
206+
}
206207
if (missingArgErrors.Any())
207208
{
208209
throw new ArgumentNullException(null, string.Join("\n-> ", missingArgErrors));
@@ -216,7 +217,10 @@ internal Builder Validate()
216217
/// <returns></returns>
217218
internal void Apply(RequestBuilder requestBuilder)
218219
{
219-
parameters.ForEach(p => p.Apply(requestBuilder));
220+
foreach (var p in parameters)
221+
{
222+
p.Apply(requestBuilder);
223+
}
220224
}
221225
}
222226
}

APIMatic.Core/Request/Parameters/TemplateParam.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ internal override void Apply(RequestBuilder requestBuilder)
4848
}
4949
CoreHelper.TryGetInnerValueForContainer(value, out var innerValue);
5050
string replacerValue = Uri.EscapeUriString(GetReplacerValue(innerValue ?? value));
51-
requestBuilder.QueryUrl.Replace(string.Format("{{{0}}}", key), replacerValue);
51+
requestBuilder.QueryUrl.Replace($"{{{key}}}", replacerValue);
5252
}
5353
}
5454
}

0 commit comments

Comments
 (0)