diff --git a/src/RobinTTY.NordigenApiClient.Tests/LiveApi/Endpoints/AgreementsEndpointTests.cs b/src/RobinTTY.NordigenApiClient.Tests/LiveApi/Endpoints/AgreementsEndpointTests.cs
index e52d802..ec8f187 100644
--- a/src/RobinTTY.NordigenApiClient.Tests/LiveApi/Endpoints/AgreementsEndpointTests.cs
+++ b/src/RobinTTY.NordigenApiClient.Tests/LiveApi/Endpoints/AgreementsEndpointTests.cs
@@ -14,11 +14,12 @@ public void Setup()
_apiClient = TestHelpers.GetConfiguredClient();
}
+ #region RequestsWithSuccessfulResponse
+
///
/// Tests the paging mechanism of retrieving end user agreements.
/// Creates 3 agreements, retrieves them using 3 s and deletes the agreements after.
///
- ///
[Test]
public async Task GetAgreementsPaged()
{
@@ -78,7 +79,6 @@ public async Task GetAgreementsPaged()
///
/// Tests the retrieval of one agreement via and string id.
///
- ///
[Test]
public async Task GetAgreement()
{
@@ -104,7 +104,6 @@ public async Task GetAgreement()
///
/// Tests the retrieval of an agreement with an invalid guid.
///
- ///
[Test]
public async Task GetAgreementWithInvalidGuid()
{
@@ -118,7 +117,6 @@ public async Task GetAgreementWithInvalidGuid()
///
/// Tests the creation and deletion of an end user agreement.
///
- ///
[Test]
public async Task CreateAcceptAndDeleteAgreement()
{
@@ -153,42 +151,70 @@ public async Task CreateAcceptAndDeleteAgreement()
Assert.That(deletionResponse.Result!.Summary, Is.EqualTo("End User Agreement deleted"));
}
+ #endregion
+
+ #region RequestsWithErrors
+
+ ///
+ /// Tests the retrieving of an end user agreement with an invalid institution id.
+ ///
+ [Test]
+ public async Task GetAgreementWithInvalidInstitutionId()
+ {
+ var agreement = new CreateAgreementRequest(90, 90,
+ ["balances", "details", "transactions"], "invalid_institution");
+
+ var response = await _apiClient.AgreementsEndpoint.CreateAgreement(agreement);
+
+ AssertionHelpers.AssertNordigenApiResponseIsUnsuccessful(response, HttpStatusCode.BadRequest);
+ Assert.Multiple(() =>
+ {
+ Assert.That(response.Error!.InstitutionIdError, Is.Not.Null);
+ Assert.That(response.Error!.InstitutionIdError!.Summary,
+ Is.EqualTo("Unknown Institution ID invalid_institution"));
+ Assert.That(response.Error!.InstitutionIdError!.Detail,
+ Is.EqualTo("Get Institution IDs from /institutions/?country={$COUNTRY_CODE}"));
+ });
+ }
+
///
/// Tests the creation of an end user agreement with an invalid institution id.
///
- ///
[Test]
public async Task CreateAgreementWithInvalidInstitutionId()
{
- var agreement = new CreateAgreementRequest(90, 90, ["balances", "details", "transactions"],
- "SANDBOXFINANCE_SFIN000");
+ var agreement = new CreateAgreementRequest(90, 90,
+ ["balances", "details", "transactions"], "invalid_institution");
+
var response = await _apiClient.AgreementsEndpoint.CreateAgreement(agreement);
- AssertionHelpers.AssertNordigenApiResponseIsUnsuccessful(response, HttpStatusCode.BadRequest);
- var result = response.Error!;
- Assert.That(result.InstitutionIdError, Is.Not.Null);
- Assert.That(result.InstitutionIdError!.Detail,
- Is.EqualTo("Get Institution IDs from /institutions/?country={$COUNTRY_CODE}"));
+ AssertionHelpers.AssertNordigenApiResponseIsUnsuccessful(response, HttpStatusCode.BadRequest);
+ Assert.Multiple(() =>
+ {
+ Assert.That(response.Error!.InstitutionIdError, Is.Not.Null);
+ Assert.That(response.Error!.InstitutionIdError!.Summary,
+ Is.EqualTo("Unknown Institution ID invalid_institution"));
+ Assert.That(response.Error!.InstitutionIdError!.Detail,
+ Is.EqualTo("Get Institution IDs from /institutions/?country={$COUNTRY_CODE}"));
+ });
}
///
/// Tests the creation of an end user agreement with invalid parameters.
///
- ///
[Test]
public async Task CreateAgreementWithInvalidParams()
{
var agreement = new CreateAgreementRequest(200, 200,
["balances", "details", "transactions", "invalid", "invalid2"], "SANDBOXFINANCE_SFIN0000");
- var response = await _apiClient.AgreementsEndpoint.CreateAgreement(agreement);
- AssertionHelpers.AssertNordigenApiResponseIsUnsuccessful(response, HttpStatusCode.BadRequest);
+ var response = await _apiClient.AgreementsEndpoint.CreateAgreement(agreement);
var result = response.Error!;
+
Assert.Multiple(() =>
{
- Assert.That(
- new[] {result.InstitutionIdError, result.AgreementError},
- Has.All.Null);
+ AssertionHelpers.AssertNordigenApiResponseIsUnsuccessful(response, HttpStatusCode.BadRequest);
+ Assert.That(new[] {result.InstitutionIdError, result.AgreementError}, Has.All.Null);
Assert.That(result.AccessScopeError!.Detail,
Is.EqualTo("Choose one or several from ['balances', 'details', 'transactions']"));
Assert.That(result.AccessValidForDaysError!.Detail,
@@ -198,4 +224,25 @@ public async Task CreateAgreementWithInvalidParams()
"max_historical_days must be > 0 and <= SANDBOXFINANCE_SFIN0000 transaction_total_days (90)"));
});
}
+
+ [Test]
+ public async Task CreateAgreementWithInvalidParamsAtPolishInstitution()
+ {
+ var agreement = new CreateAgreementRequest(90, 90,
+ ["balances", "transactions"], "PKO_BPKOPLPW");
+
+ var response = await _apiClient.AgreementsEndpoint.CreateAgreement(agreement);
+ var result = response.Error!;
+
+ Assert.Multiple(() =>
+ {
+ AssertionHelpers.AssertNordigenApiResponseIsUnsuccessful(response, HttpStatusCode.BadRequest);
+ Assert.That(new[] {result.InstitutionIdError, result.AgreementError}, Has.All.Null);
+ Assert.That(result.Detail,
+ Is.EqualTo("For this institution the following scopes are required together: ['details', 'balances']"));
+ Assert.That(result.Summary, Is.EqualTo("Institution access scope dependencies error"));
+ });
+ }
+
+ #endregion
}
diff --git a/src/RobinTTY.NordigenApiClient/JsonConverters/SingleOrArrayConverter.cs b/src/RobinTTY.NordigenApiClient/JsonConverters/SingleOrArrayConverter.cs
index 097c306..b514e84 100644
--- a/src/RobinTTY.NordigenApiClient/JsonConverters/SingleOrArrayConverter.cs
+++ b/src/RobinTTY.NordigenApiClient/JsonConverters/SingleOrArrayConverter.cs
@@ -6,7 +6,7 @@ namespace RobinTTY.NordigenApiClient.JsonConverters;
internal class SingleOrArrayConverter : JsonConverter
where TEnumerable : IEnumerable
{
- public override TEnumerable? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ public override TEnumerable Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
switch (reader.TokenType)
{
@@ -20,6 +20,7 @@ internal class SingleOrArrayConverter : JsonConverter(ref reader, options);
if (listItem != null) list.Add(listItem);
}
+
return (TEnumerable) (IEnumerable) list;
default:
var item = JsonSerializer.Deserialize(ref reader, options);
@@ -43,4 +44,4 @@ public override void Write(Utf8JsonWriter writer, TEnumerable value, JsonSeriali
writer.WriteEndArray();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/RobinTTY.NordigenApiClient/JsonConverters/StringArrayMergeConverter.cs b/src/RobinTTY.NordigenApiClient/JsonConverters/StringArrayMergeConverter.cs
new file mode 100644
index 0000000..508c238
--- /dev/null
+++ b/src/RobinTTY.NordigenApiClient/JsonConverters/StringArrayMergeConverter.cs
@@ -0,0 +1,37 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using RobinTTY.NordigenApiClient.Models.Responses;
+
+namespace RobinTTY.NordigenApiClient.JsonConverters;
+
+///
+/// For some errors the GoCardless API returns arrays for Summary/Detail properties inside the .
+/// I've never actually seen them contain multiple values, but this converter merges them into one string so that the
+/// can stay as simple as possible.
+///
+internal class StringArrayMergeConverter : JsonConverter
+{
+ public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ switch (reader.TokenType)
+ {
+ case JsonTokenType.Null:
+ return null;
+ case JsonTokenType.StartArray:
+ var list = new List();
+ while (reader.Read())
+ {
+ if (reader.TokenType == JsonTokenType.EndArray) break;
+ var listItem = JsonSerializer.Deserialize(ref reader, options);
+ if (listItem != null) list.Add(listItem);
+ }
+
+ return string.Join("; ", list);
+ default:
+ return JsonSerializer.Deserialize(ref reader, options);
+ }
+ }
+
+ public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) =>
+ JsonSerializer.Serialize(writer, value, options);
+}
diff --git a/src/RobinTTY.NordigenApiClient/Models/Responses/BasicResponse.cs b/src/RobinTTY.NordigenApiClient/Models/Responses/BasicResponse.cs
index d192839..9e2f2d0 100644
--- a/src/RobinTTY.NordigenApiClient/Models/Responses/BasicResponse.cs
+++ b/src/RobinTTY.NordigenApiClient/Models/Responses/BasicResponse.cs
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
+using RobinTTY.NordigenApiClient.JsonConverters;
namespace RobinTTY.NordigenApiClient.Models.Responses;
@@ -11,12 +12,14 @@ public class BasicResponse
/// The summary text of the response/error.
///
[JsonPropertyName("summary")]
+ [JsonConverter(typeof(StringArrayMergeConverter))]
public string? Summary { get; init; }
///
/// The detailed description of the response/error.
///
[JsonPropertyName("detail")]
+ [JsonConverter(typeof(StringArrayMergeConverter))]
public string? Detail { get; init; }
///