diff --git a/src/Moneybird.Net/Extensions/JsonElementExtensions.cs b/src/Moneybird.Net/Extensions/JsonElementExtensions.cs
new file mode 100644
index 0000000..e3858c0
--- /dev/null
+++ b/src/Moneybird.Net/Extensions/JsonElementExtensions.cs
@@ -0,0 +1,24 @@
+using System.Linq;
+using System.Text.Json;
+
+namespace Moneybird.Net.Extensions
+{
+ internal static class JsonElementExtensions
+ {
+ ///
+ /// Get the error message from the error JsonElement.
+ ///
+ /// The JsonElement from the error response content.
+ /// The error message as a string.
+ public static string GetErrorMessage(this JsonElement jsonElement) {
+ var error = jsonElement.GetProperty("error");
+ var message = error.ValueKind switch
+ {
+ JsonValueKind.Object => string.Join(", ", error.EnumerateObject().SelectMany(prop => prop.Value.EnumerateArray().Select(val => $"{prop.Name}: {val.GetString()}")).ToList()),
+ _ => error.GetString(),
+ };
+
+ return message;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Moneybird.Net/Http/RequesterBase.cs b/src/Moneybird.Net/Http/RequesterBase.cs
index 2ce095e..e6edb25 100644
--- a/src/Moneybird.Net/Http/RequesterBase.cs
+++ b/src/Moneybird.Net/Http/RequesterBase.cs
@@ -1,14 +1,17 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks;
+using Moneybird.Net.Extensions;
namespace Moneybird.Net.Http
{
+ [ExcludeFromCodeCoverage]
public abstract class RequesterBase : IDisposable
{
private readonly HttpClient _httpClient;
@@ -51,15 +54,15 @@ protected HttpRequestMessage ConstructRequest(string host, string relativeUrl, s
return requestMessage;
}
-
- protected string GetQueryString(List queryParameters)
+
+ private string GetQueryString(List queryParameters)
{
return queryParameters
.Where(param => !string.IsNullOrWhiteSpace(param))
- .Aggregate(string.Empty, (current, param) => current + ("&" + param));
+ .Aggregate(string.Empty, (current, param) => current + "&" + param);
}
-
- protected void HandleRequestFailure(HttpResponseMessage response)
+
+ private void HandleRequestFailure(HttpResponseMessage response)
{
try
{
@@ -70,8 +73,8 @@ protected void HandleRequestFailure(HttpResponseMessage response)
try
{
var json = response.Content.ReadAsStringAsync().Result;
- var obj = JsonDocument.Parse(json);
- message = obj.RootElement[0].GetProperty("error").GetString();
+ var jsonDocument = JsonDocument.Parse(json);
+ message = jsonDocument.RootElement.GetErrorMessage();
}
catch {
message = response.StatusCode.ToString();
diff --git a/tests/Moneybird.Net.Tests/Extensions/JsonElementExtensionsTests.cs b/tests/Moneybird.Net.Tests/Extensions/JsonElementExtensionsTests.cs
new file mode 100644
index 0000000..cd52ace
--- /dev/null
+++ b/tests/Moneybird.Net.Tests/Extensions/JsonElementExtensionsTests.cs
@@ -0,0 +1,48 @@
+using System.Text.Json;
+using Moneybird.Net.Extensions;
+using Xunit;
+
+namespace Moneybird.Net.Tests.Extensions;
+
+public class JsonElementExtensionsTests
+{
+ [Fact]
+ public void GetErrorMessage_FromStringErrorJsonElement_Returns_CorrectString()
+ {
+ const string json = "{\"error\":\"Contact is required\",\"symbolic\":{\"contact\":\"required\"}}";
+ var jsonElement = JsonDocument.Parse(json);
+ var actualValue = jsonElement.RootElement.GetErrorMessage();
+
+ Assert.Equal("Contact is required", actualValue);
+ }
+
+ [Fact]
+ public void GetErrorMessage_FromObjectErrorJsonElement_Returns_CorrectString()
+ {
+ const string json = "{\"error\":{\"delivery_method\":[\"The sender address must contain an email address\"]}}";
+ var jsonElement = JsonDocument.Parse(json);
+ var actualValue = jsonElement.RootElement.GetErrorMessage();
+
+ Assert.Equal("delivery_method: The sender address must contain an email address", actualValue);
+ }
+
+ [Fact]
+ public void GetErrorMessage_FromObjectsErrorJsonElement_Returns_CorrectString()
+ {
+ const string json = "{\"error\":{\"firstname\":[\"is required\"],\"lastname\":[\"is required\"],\"company_name\":[\"is required\"]}}";
+ var jsonElement = JsonDocument.Parse(json);
+ var actualValue = jsonElement.RootElement.GetErrorMessage();
+
+ Assert.Equal("firstname: is required, lastname: is required, company_name: is required", actualValue);
+ }
+
+ [Fact]
+ public void GetErrorMessage_FromObjectMultipleErrorsJsonElement_Returns_CorrectString()
+ {
+ const string json = "{\"error\":{\"delivery_method\":[\"The delivery method is required\",\"The sender address must contain an email address\"]}}";
+ var jsonElement = JsonDocument.Parse(json);
+ var actualValue = jsonElement.RootElement.GetErrorMessage();
+
+ Assert.Equal("delivery_method: The delivery method is required, delivery_method: The sender address must contain an email address", actualValue);
+ }
+}
\ No newline at end of file