feat(audit)!: ScadaBridge C3 — swap to canonical ZB.MOM.WW.Audit.AuditEvent across seams/emitters/DTO/redactor wiring; transitional 24-col storage shim (Task 2.5)

This commit is contained in:
Joseph Doherty
2026-06-02 12:37:50 -04:00
parent 5aaf9e2923
commit db707bb0de
127 changed files with 2240 additions and 3886 deletions
@@ -1,4 +1,5 @@
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit;
using ZB.MOM.WW.Audit;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Audit;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
using Timestamp = Google.Protobuf.WellKnownTypes.Timestamp;
@@ -41,38 +42,44 @@ public static class AuditEventDtoMapper
{
ArgumentNullException.ThrowIfNull(evt);
// C3 (Task 2.5): the proto contract is the UNCHANGED 24-field wire. The
// canonical record carries the ScadaBridge domain fields inside
// DetailsJson — decompose them so the DTO's typed domain fields are
// populated exactly as before.
var r = AuditRowProjection.Decompose(evt);
var dto = new AuditEventDto
{
EventId = evt.EventId.ToString(),
OccurredAtUtc = Timestamp.FromDateTime(EnsureUtc(evt.OccurredAtUtc)),
Channel = evt.Channel.ToString(),
Kind = evt.Kind.ToString(),
CorrelationId = evt.CorrelationId?.ToString() ?? string.Empty,
ExecutionId = evt.ExecutionId?.ToString() ?? string.Empty,
ParentExecutionId = evt.ParentExecutionId?.ToString() ?? string.Empty,
SourceSiteId = evt.SourceSiteId ?? string.Empty,
SourceNode = evt.SourceNode ?? string.Empty,
SourceInstanceId = evt.SourceInstanceId ?? string.Empty,
SourceScript = evt.SourceScript ?? string.Empty,
Actor = evt.Actor ?? string.Empty,
Target = evt.Target ?? string.Empty,
Status = evt.Status.ToString(),
ErrorMessage = evt.ErrorMessage ?? string.Empty,
ErrorDetail = evt.ErrorDetail ?? string.Empty,
RequestSummary = evt.RequestSummary ?? string.Empty,
ResponseSummary = evt.ResponseSummary ?? string.Empty,
PayloadTruncated = evt.PayloadTruncated,
Extra = evt.Extra ?? string.Empty
EventId = r.EventId.ToString(),
OccurredAtUtc = Timestamp.FromDateTime(EnsureUtc(r.OccurredAtUtc)),
Channel = r.Channel.ToString(),
Kind = r.Kind.ToString(),
CorrelationId = r.CorrelationId?.ToString() ?? string.Empty,
ExecutionId = r.ExecutionId?.ToString() ?? string.Empty,
ParentExecutionId = r.ParentExecutionId?.ToString() ?? string.Empty,
SourceSiteId = r.SourceSiteId ?? string.Empty,
SourceNode = r.SourceNode ?? string.Empty,
SourceInstanceId = r.SourceInstanceId ?? string.Empty,
SourceScript = r.SourceScript ?? string.Empty,
Actor = r.Actor ?? string.Empty,
Target = r.Target ?? string.Empty,
Status = r.Status.ToString(),
ErrorMessage = r.ErrorMessage ?? string.Empty,
ErrorDetail = r.ErrorDetail ?? string.Empty,
RequestSummary = r.RequestSummary ?? string.Empty,
ResponseSummary = r.ResponseSummary ?? string.Empty,
PayloadTruncated = r.PayloadTruncated,
Extra = r.Extra ?? string.Empty
};
if (evt.HttpStatus.HasValue)
if (r.HttpStatus.HasValue)
{
dto.HttpStatus = evt.HttpStatus.Value;
dto.HttpStatus = r.HttpStatus.Value;
}
if (evt.DurationMs.HasValue)
if (r.DurationMs.HasValue)
{
dto.DurationMs = evt.DurationMs.Value;
dto.DurationMs = r.DurationMs.Value;
}
return dto;
@@ -89,33 +96,35 @@ public static class AuditEventDtoMapper
{
ArgumentNullException.ThrowIfNull(dto);
return new AuditEvent
{
EventId = Guid.Parse(dto.EventId),
OccurredAtUtc = DateTime.SpecifyKind(dto.OccurredAtUtc.ToDateTime(), DateTimeKind.Utc),
IngestedAtUtc = null,
Channel = Enum.Parse<AuditChannel>(dto.Channel),
Kind = Enum.Parse<AuditKind>(dto.Kind),
CorrelationId = NullIfEmpty(dto.CorrelationId) is { } cid ? Guid.Parse(cid) : null,
ExecutionId = NullIfEmpty(dto.ExecutionId) is { } eid ? Guid.Parse(eid) : null,
ParentExecutionId = NullIfEmpty(dto.ParentExecutionId) is { } pid ? Guid.Parse(pid) : null,
SourceSiteId = NullIfEmpty(dto.SourceSiteId),
SourceNode = NullIfEmpty(dto.SourceNode),
SourceInstanceId = NullIfEmpty(dto.SourceInstanceId),
SourceScript = NullIfEmpty(dto.SourceScript),
Actor = NullIfEmpty(dto.Actor),
Target = NullIfEmpty(dto.Target),
Status = Enum.Parse<AuditStatus>(dto.Status),
HttpStatus = dto.HttpStatus,
DurationMs = dto.DurationMs,
ErrorMessage = NullIfEmpty(dto.ErrorMessage),
ErrorDetail = NullIfEmpty(dto.ErrorDetail),
RequestSummary = NullIfEmpty(dto.RequestSummary),
ResponseSummary = NullIfEmpty(dto.ResponseSummary),
PayloadTruncated = dto.PayloadTruncated,
Extra = NullIfEmpty(dto.Extra),
ForwardState = null
};
// C3 (Task 2.5): recompose the canonical record from the 24-field wire
// DTO. The domain fields are re-serialized into DetailsJson via the
// projection helper; IngestedAtUtc is left null (central sets it at
// ingest) and ForwardState is dropped (site-storage-only, never on the
// wire).
return AuditRowProjection.Recompose(new AuditRowProjection.AuditRowValues(
EventId: Guid.Parse(dto.EventId),
OccurredAtUtc: DateTime.SpecifyKind(dto.OccurredAtUtc.ToDateTime(), DateTimeKind.Utc),
IngestedAtUtc: null,
Channel: Enum.Parse<AuditChannel>(dto.Channel),
Kind: Enum.Parse<AuditKind>(dto.Kind),
Status: Enum.Parse<AuditStatus>(dto.Status),
CorrelationId: NullIfEmpty(dto.CorrelationId) is { } cid ? Guid.Parse(cid) : null,
ExecutionId: NullIfEmpty(dto.ExecutionId) is { } eid ? Guid.Parse(eid) : null,
ParentExecutionId: NullIfEmpty(dto.ParentExecutionId) is { } pid ? Guid.Parse(pid) : null,
SourceSiteId: NullIfEmpty(dto.SourceSiteId),
SourceNode: NullIfEmpty(dto.SourceNode),
SourceInstanceId: NullIfEmpty(dto.SourceInstanceId),
SourceScript: NullIfEmpty(dto.SourceScript),
Actor: NullIfEmpty(dto.Actor),
Target: NullIfEmpty(dto.Target),
HttpStatus: dto.HttpStatus,
DurationMs: dto.DurationMs,
ErrorMessage: NullIfEmpty(dto.ErrorMessage),
ErrorDetail: NullIfEmpty(dto.ErrorDetail),
RequestSummary: NullIfEmpty(dto.RequestSummary),
ResponseSummary: NullIfEmpty(dto.ResponseSummary),
PayloadTruncated: dto.PayloadTruncated,
Extra: NullIfEmpty(dto.Extra)));
}
private static string? NullIfEmpty(string? value) =>
@@ -4,7 +4,7 @@ using Akka.Actor;
using Grpc.Core;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit;
using ZB.MOM.WW.Audit;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
using ZB.MOM.WW.ScadaBridge.Commons.Observability;