feat(inbound-api): method-script exception detail on the inbound audit row, never on the HTTP body (D3)

This commit is contained in:
Joseph Doherty
2026-09-09 12:15:00 -04:00
parent a45857ddd6
commit 98c6809a66
6 changed files with 188 additions and 4 deletions
@@ -79,6 +79,16 @@ public sealed class AuditWriteMiddleware
/// </summary>
public const string AuditActorItemKey = "ZB.MOM.WW.ScadaBridge.InboundAPI.AuditActor";
/// <summary>
/// <see cref="HttpContext.Items"/> key used by the endpoint handler to publish the
/// method script's exception detail when the script threw (Flow Debugger step logs,
/// design 2026-09-09 D3). The executor deliberately sanitises the caller-facing
/// message, so this slot is the only place the real cause survives; it is consumed
/// once here, onto the inbound audit row's <c>ErrorDetail</c>, and never written to
/// the HTTP response. An exception that ESCAPED the pipeline takes precedence.
/// </summary>
public const string ErrorDetailItemKey = "ScadaBridge.Audit.ErrorDetail";
/// <summary>
/// ParentExecutionId: <see cref="HttpContext.Items"/> key under
/// which this middleware stashes the inbound request's per-request
@@ -318,6 +328,17 @@ public sealed class AuditWriteMiddleware
headerDict[header.Key] = value;
}
// Flow Debugger step logs (design 2026-09-09 D3): an exception that ESCAPED the
// pipeline is the authoritative failure, so it wins; otherwise the endpoint may
// have stashed the method script's exception (which the executor keeps out of the
// HTTP body). Absent both, the row carries no detail.
var errorDetail = thrown?.ToString();
if (errorDetail is null
&& ctx.Items.TryGetValue(ErrorDetailItemKey, out var stashedErrorDetail))
{
errorDetail = stashedErrorDetail as string;
}
var extra = JsonSerializer.Serialize(new
{
remoteIp = ctx.Connection.RemoteIpAddress?.ToString(),
@@ -347,6 +368,7 @@ public sealed class AuditWriteMiddleware
httpStatus: statusCode,
durationMs: (int)Math.Min(durationMs, int.MaxValue),
errorMessage: thrown?.Message,
errorDetail: errorDetail,
requestSummary: requestBody,
responseSummary: responseBody,
payloadTruncated: payloadTruncated,