diff --git a/src/ConfigCat.Cli.Services/Api/ApiClient.cs b/src/ConfigCat.Cli.Services/Api/ApiClient.cs index a071114..0aa626d 100644 --- a/src/ConfigCat.Cli.Services/Api/ApiClient.cs +++ b/src/ConfigCat.Cli.Services/Api/ApiClient.cs @@ -64,7 +64,7 @@ protected async Task GetAsync(HttpMethod method, string path, var content = await response.Content.ReadAsStringAsync(token); this.Output.Verbose($"Response body: {content}"); - ValidateResponse(response); + ValidateResponse(response, content); try { @@ -96,7 +96,7 @@ protected async Task SendAsync(HttpMethod method, string path, object body, Canc var content = await response.Content.ReadAsStringAsync(token); this.Output.Verbose($"Response body: {content}"); - ValidateResponse(response); + ValidateResponse(response, content); } protected async Task SendAsync(HttpMethod method, string path, object body, CancellationToken token) @@ -119,7 +119,7 @@ protected async Task SendAsync(HttpMethod method, string path, var content = await response.Content.ReadAsStringAsync(token); this.Output.Verbose($"Response body: {content}"); - ValidateResponse(response); + ValidateResponse(response, content); try { @@ -160,10 +160,17 @@ private void LogRetry(HttpResponseMessage result, Exception exception, AttemptCo this.Output.Verbose($"{message}, retrying... [{context.CurrentAttempt}. attempt, waiting {context.CurrentDelay}]", ConsoleColor.Yellow); } - private static void ValidateResponse(HttpResponseMessage responseMessage) + private static void ValidateResponse(HttpResponseMessage responseMessage, string content) { if (!responseMessage.IsSuccessStatusCode) + { + Dictionary errorDetails; + try { errorDetails = JsonSerializer.Deserialize>(content); } + catch { errorDetails = null; } + throw new HttpStatusException(responseMessage.StatusCode, - responseMessage.ReasonPhrase); + responseMessage.ReasonPhrase, + errorDetails); + } } } \ No newline at end of file diff --git a/src/ConfigCat.Cli.Services/Exceptions/HttpStatusException.cs b/src/ConfigCat.Cli.Services/Exceptions/HttpStatusException.cs index ebe1da8..897df6d 100644 --- a/src/ConfigCat.Cli.Services/Exceptions/HttpStatusException.cs +++ b/src/ConfigCat.Cli.Services/Exceptions/HttpStatusException.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Net; namespace ConfigCat.Cli.Services.Exceptions; @@ -6,6 +7,7 @@ namespace ConfigCat.Cli.Services.Exceptions; public class HttpStatusException( HttpStatusCode statusCode, string reason, + Dictionary errorDetails = null, string message = null, Exception innerException = null) : Exception(message, innerException) @@ -13,4 +15,6 @@ public class HttpStatusException( public HttpStatusCode StatusCode { get; } = statusCode; public string ReasonPhrase { get; } = reason; + + public IReadOnlyDictionary ErrorDetails { get; } = errorDetails; } \ No newline at end of file diff --git a/src/ConfigCat.Cli/Program.cs b/src/ConfigCat.Cli/Program.cs index 6ec4e7e..fc863b2 100644 --- a/src/ConfigCat.Cli/Program.cs +++ b/src/ConfigCat.Cli/Program.cs @@ -15,6 +15,7 @@ using System.CommandLine.IO; using System.CommandLine.Parsing; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Net.Http; using System.Reflection; using System.Security.Cryptography; @@ -105,7 +106,15 @@ private static async Task Main(string[] args) output.WriteError("Terminated."); break; case HttpStatusException statusException: - output.WriteError($"Http request failed: {(int)statusException.StatusCode} {statusException.ReasonPhrase}."); + var errorMessage = $"Http request failed: {(int)statusException.StatusCode} {statusException.ReasonPhrase}."; + if (statusException.ErrorDetails is { } errorDetails) + { + errorMessage += Environment.NewLine + + "Details:" + Environment.NewLine + + string.Join(Environment.NewLine, errorDetails + .SelectMany(kvp => kvp.Value.Select(msg => $"- [{kvp.Key}] {msg}"))); + } + output.WriteError(errorMessage); break; case MaxRetryAttemptsReachedException { OperationResult: HttpResponseMessage response }: output.WriteError($"Http request failed: {(int)response.StatusCode} {response.ReasonPhrase}.");