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