Surface Infisical API error body in thrown exceptions
Parse the JSON envelope (message/error/reqId) returned by 4xx/5xx responses and include the human-readable message in the InfisicalApiException message itself, plus new ApiErrorMessage and ApiRequestId properties. InfisicalErrorDetails and the error handler propagate the new fields so PowerShell error records and the logger surface the server-side reason instead of an opaque status line.
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PSInfisicalAPI.Errors
|
||||
{
|
||||
internal static class InfisicalApiErrorEnvelope
|
||||
{
|
||||
public static void Enrich(InfisicalApiException exception, string body)
|
||||
{
|
||||
if (exception == null || string.IsNullOrEmpty(body))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string trimmed = body.TrimStart();
|
||||
if (trimmed.Length == 0 || (trimmed[0] != '{' && trimmed[0] != '['))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
JObject obj;
|
||||
try
|
||||
{
|
||||
JToken token = JToken.Parse(body);
|
||||
if (token.Type != JTokenType.Object) { return; }
|
||||
obj = (JObject)token;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string message = ReadString(obj, "message");
|
||||
string error = ReadString(obj, "error");
|
||||
string reqId = ReadString(obj, "reqId");
|
||||
|
||||
if (!string.IsNullOrEmpty(message)) { exception.ApiErrorMessage = message; }
|
||||
if (!string.IsNullOrEmpty(error) && string.IsNullOrEmpty(exception.ApiErrorCode)) { exception.ApiErrorCode = error; }
|
||||
if (!string.IsNullOrEmpty(reqId)) { exception.ApiRequestId = reqId; }
|
||||
}
|
||||
|
||||
public static string BuildExceptionMessage(int statusCode, string reasonPhrase, string body)
|
||||
{
|
||||
string baseMessage = string.Concat(
|
||||
"Infisical API returned ",
|
||||
statusCode.ToString(System.Globalization.CultureInfo.InvariantCulture),
|
||||
" (", reasonPhrase ?? string.Empty, ").");
|
||||
|
||||
string apiMessage = null;
|
||||
string apiError = null;
|
||||
string reqId = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(body))
|
||||
{
|
||||
string trimmed = body.TrimStart();
|
||||
if (trimmed.Length > 0 && trimmed[0] == '{')
|
||||
{
|
||||
try
|
||||
{
|
||||
JToken token = JToken.Parse(body);
|
||||
if (token.Type == JTokenType.Object)
|
||||
{
|
||||
JObject obj = (JObject)token;
|
||||
apiMessage = ReadString(obj, "message");
|
||||
apiError = ReadString(obj, "error");
|
||||
reqId = ReadString(obj, "reqId");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(apiMessage) && string.IsNullOrEmpty(apiError) && string.IsNullOrEmpty(reqId))
|
||||
{
|
||||
return baseMessage;
|
||||
}
|
||||
|
||||
System.Text.StringBuilder builder = new System.Text.StringBuilder(baseMessage);
|
||||
if (!string.IsNullOrEmpty(apiMessage))
|
||||
{
|
||||
builder.Append(' ').Append(apiMessage);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(apiError) || !string.IsNullOrEmpty(reqId))
|
||||
{
|
||||
builder.Append(" [");
|
||||
bool needsSeparator = false;
|
||||
if (!string.IsNullOrEmpty(apiError))
|
||||
{
|
||||
builder.Append("error=").Append(apiError);
|
||||
needsSeparator = true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(reqId))
|
||||
{
|
||||
if (needsSeparator) { builder.Append("; "); }
|
||||
builder.Append("reqId=").Append(reqId);
|
||||
}
|
||||
|
||||
builder.Append(']');
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static string ReadString(JObject obj, string name)
|
||||
{
|
||||
JToken token;
|
||||
if (obj.TryGetValue(name, StringComparison.OrdinalIgnoreCase, out token) && token != null && token.Type == JTokenType.String)
|
||||
{
|
||||
return (string)token;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ namespace PSInfisicalAPI.Errors
|
||||
public int? StatusCode { get; set; }
|
||||
public string ReasonPhrase { get; set; }
|
||||
public string ApiErrorCode { get; set; }
|
||||
public string ApiErrorMessage { get; set; }
|
||||
public string ApiRequestId { get; set; }
|
||||
public string SanitizedBody { get; set; }
|
||||
public int? LineNumber { get; set; }
|
||||
public int? LinePosition { get; set; }
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace PSInfisicalAPI.Errors
|
||||
details.StatusCode = apiException.StatusCode;
|
||||
details.ReasonPhrase = apiException.ReasonPhrase;
|
||||
details.ApiErrorCode = apiException.ApiErrorCode;
|
||||
details.ApiErrorMessage = apiException.ApiErrorMessage;
|
||||
details.ApiRequestId = apiException.ApiRequestId;
|
||||
details.SanitizedBody = apiException.SanitizedBody;
|
||||
details.EndpointName = apiException.EndpointName;
|
||||
details.RequestMethod = apiException.RequestMethod;
|
||||
@@ -70,6 +72,16 @@ namespace PSInfisicalAPI.Errors
|
||||
logger.Error(Component, string.Concat("API Error Code: ", details.ApiErrorCode));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(details.ApiErrorMessage))
|
||||
{
|
||||
logger.Error(Component, string.Concat("API Error Message: ", details.ApiErrorMessage));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(details.ApiRequestId))
|
||||
{
|
||||
logger.Error(Component, string.Concat("API Request Id: ", details.ApiRequestId));
|
||||
}
|
||||
|
||||
if (details.LineNumber.HasValue)
|
||||
{
|
||||
logger.Error(Component, string.Concat("Line: ", details.LineNumber.Value.ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace PSInfisicalAPI.Errors
|
||||
public int StatusCode { get; set; }
|
||||
public string ReasonPhrase { get; set; }
|
||||
public string ApiErrorCode { get; set; }
|
||||
public string ApiErrorMessage { get; set; }
|
||||
public string ApiRequestId { get; set; }
|
||||
public string SanitizedBody { get; set; }
|
||||
public string EndpointName { get; set; }
|
||||
public string RequestMethod { get; set; }
|
||||
|
||||
@@ -135,15 +135,14 @@ namespace PSInfisicalAPI.Http
|
||||
|
||||
private static InfisicalApiException BuildApiException(InfisicalHttpResponse response, InfisicalEndpointDefinition definition)
|
||||
{
|
||||
InfisicalApiException exception = new InfisicalApiException(string.Concat(
|
||||
"Infisical API returned ",
|
||||
response.StatusCode.ToString(CultureInfo.InvariantCulture),
|
||||
" (", response.ReasonPhrase ?? string.Empty, ")."));
|
||||
string message = InfisicalApiErrorEnvelope.BuildExceptionMessage(response.StatusCode, response.ReasonPhrase, response.Body);
|
||||
InfisicalApiException exception = new InfisicalApiException(message);
|
||||
exception.StatusCode = response.StatusCode;
|
||||
exception.ReasonPhrase = response.ReasonPhrase;
|
||||
exception.EndpointName = definition.Name;
|
||||
exception.RequestMethod = definition.Method;
|
||||
exception.SanitizedBody = response.Body;
|
||||
InfisicalApiErrorEnvelope.Enrich(exception, response.Body);
|
||||
return exception;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -625,15 +625,14 @@ namespace PSInfisicalAPI.Secrets
|
||||
|
||||
private static InfisicalApiException BuildApiException(InfisicalHttpResponse response, InfisicalEndpointDefinition definition)
|
||||
{
|
||||
InfisicalApiException exception = new InfisicalApiException(string.Concat(
|
||||
"Infisical API returned ",
|
||||
response.StatusCode.ToString(CultureInfo.InvariantCulture),
|
||||
" (", response.ReasonPhrase ?? string.Empty, ")."));
|
||||
string message = InfisicalApiErrorEnvelope.BuildExceptionMessage(response.StatusCode, response.ReasonPhrase, response.Body);
|
||||
InfisicalApiException exception = new InfisicalApiException(message);
|
||||
exception.StatusCode = response.StatusCode;
|
||||
exception.ReasonPhrase = response.ReasonPhrase;
|
||||
exception.EndpointName = definition.Name;
|
||||
exception.RequestMethod = definition.Method;
|
||||
exception.SanitizedBody = response.Body;
|
||||
InfisicalApiErrorEnvelope.Enrich(exception, response.Body);
|
||||
return exception;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user