feat(site-call-audit): relay Retry/Discard emit operator-identity audit rows

Additive RequestedBy on Retry/DiscardSiteCallRequest, plumbed from the Central UI.
On an Applied relay, SiteCallAuditActor emits one best-effort CachedResolve central
direct-write row (Submitted/Discarded) with the operator as Actor. Gated on an
injected ICentralAuditWriter (null in existing tests → no mirror read, unchanged);
the site remains the source of truth for the state change.
This commit is contained in:
Joseph Doherty
2026-07-09 08:21:41 -04:00
parent 50bb1ef8ab
commit 2c45c3238b
5 changed files with 319 additions and 15 deletions
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
using ZB.MOM.WW.ScadaBridge.CentralUI.Auth;
@@ -48,6 +49,7 @@ public partial class SiteCallsReport
[Inject] private NavigationManager Navigation { get; set; } = null!;
[Inject] private SiteScopeService SiteScope { get; set; } = null!;
[Inject] private AuthenticationStateProvider AuthStateProvider { get; set; } = null!;
// The Status filter <select> options — the exact strings the dropdown binds and
// the KPI tiles emit (e.g. ?status=Parked). A query-string status only seeds the
@@ -296,8 +298,9 @@ public partial class SiteCallsReport
_actionInProgress = true;
try
{
var requestedBy = await AuthStateProvider.GetCurrentUsernameAsync();
var response = await CommunicationService.RetrySiteCallAsync(
new RetrySiteCallRequest(Guid.NewGuid().ToString("N"), c.TrackedOperationId, c.SourceSite));
new RetrySiteCallRequest(Guid.NewGuid().ToString("N"), c.TrackedOperationId, c.SourceSite, requestedBy));
ShowRelayOutcome(response.Outcome, response.SiteReachable, response.ErrorMessage,
appliedMessage: $"Retry of {ShortId(c.TrackedOperationId)} relayed to {SiteName(c.SourceSite)}.");
if (response.Success)
@@ -330,8 +333,9 @@ public partial class SiteCallsReport
_actionInProgress = true;
try
{
var requestedBy = await AuthStateProvider.GetCurrentUsernameAsync();
var response = await CommunicationService.DiscardSiteCallAsync(
new DiscardSiteCallRequest(Guid.NewGuid().ToString("N"), c.TrackedOperationId, c.SourceSite));
new DiscardSiteCallRequest(Guid.NewGuid().ToString("N"), c.TrackedOperationId, c.SourceSite, requestedBy));
ShowRelayOutcome(response.Outcome, response.SiteReachable, response.ErrorMessage,
appliedMessage: $"Discard of {ShortId(c.TrackedOperationId)} relayed to {SiteName(c.SourceSite)}.");
if (response.Success)