refactor: rename ScadaLink → ZB.MOM.WW.ScadaBridge (code + projects + namespaces)
Solution + 23 src projects + 26 test projects renamed; folders, csproj, namespaces, and ScadaLinkDbContext/ScadaBridgeDbContext class updated. ActorSystem "scadalink" → "scadabridge", Akka seed-node URLs migrated. SQL roles/logins, LDAP domains, CLI command name, and CLI config dir (~/.scadalink → ~/.scadabridge) also renamed. Build green; 5 Host.Tests fail awaiting SQL login rename in next commit. Pre-existing StaleTagMonitor timing flakes unchanged. Rename script committed at tools/rename-to-scadabridge.sh.
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Akka message sent to the central <c>AuditLogIngestActor</c> (Audit Log #23,
|
||||
/// M2 site-sync pipeline) carrying a batch of <see cref="AuditEvent"/> rows
|
||||
/// decoded by the <c>SiteStreamGrpcServer</c> from a site's
|
||||
/// <c>IngestAuditEvents</c> gRPC RPC. The actor stamps
|
||||
/// <see cref="AuditEvent.IngestedAtUtc"/> and writes the rows idempotently to
|
||||
/// the central <c>AuditLog</c> table.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Lives in <c>ZB.MOM.WW.ScadaBridge.Commons</c> rather than <c>ZB.MOM.WW.ScadaBridge.AuditLog</c>
|
||||
/// because the gRPC server in <c>ZB.MOM.WW.ScadaBridge.Communication</c> needs to construct
|
||||
/// it, and <c>ZB.MOM.WW.ScadaBridge.AuditLog</c> already references
|
||||
/// <c>ZB.MOM.WW.ScadaBridge.Communication</c> (the proto DTOs live there). Putting the
|
||||
/// message in Commons avoids a project-reference cycle.
|
||||
/// </remarks>
|
||||
public sealed record IngestAuditEventsCommand(IReadOnlyList<AuditEvent> Events);
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Reply from the central <c>AuditLogIngestActor</c> for an
|
||||
/// <see cref="IngestAuditEventsCommand"/>. <see cref="AcceptedEventIds"/> lists
|
||||
/// every row the actor considers durably persisted at central — including ids
|
||||
/// that were already present before the call (first-write-wins idempotency).
|
||||
/// The gRPC handler echoes these ids back over the wire as the <c>IngestAck</c>
|
||||
/// the site uses to flip rows to <c>Forwarded</c>.
|
||||
/// </summary>
|
||||
public sealed record IngestAuditEventsReply(IReadOnlyList<Guid> AcceptedEventIds);
|
||||
@@ -0,0 +1,30 @@
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Akka message sent to the central <c>AuditLogIngestActor</c> (Audit Log #23 M3
|
||||
/// Bundle D dual-write transaction) carrying a batch of combined audit +
|
||||
/// site-call telemetry packets decoded by the <c>SiteStreamGrpcServer</c> from a
|
||||
/// site's <c>IngestCachedTelemetry</c> gRPC RPC. For each entry the actor writes
|
||||
/// the <see cref="AuditEvent"/> row AND the <see cref="SiteCall"/> upsert inside
|
||||
/// a single MS SQL transaction — both succeed or both roll back, so the audit
|
||||
/// and operational mirrors never drift mid-row.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Lives in <c>ZB.MOM.WW.ScadaBridge.Commons</c> for the same reason as
|
||||
/// <c>IngestAuditEventsCommand</c>: the gRPC server in
|
||||
/// <c>ZB.MOM.WW.ScadaBridge.Communication</c> constructs it and <c>ZB.MOM.WW.ScadaBridge.AuditLog</c>
|
||||
/// already references Communication. Putting the message in Commons avoids a
|
||||
/// project-reference cycle.
|
||||
/// </remarks>
|
||||
public sealed record IngestCachedTelemetryCommand(IReadOnlyList<CachedTelemetryEntry> Entries);
|
||||
|
||||
/// <summary>
|
||||
/// One lifecycle event of a cached call: the <see cref="AuditEvent"/> to insert
|
||||
/// (idempotent on <see cref="AuditEvent.EventId"/>) plus the
|
||||
/// <see cref="SiteCall"/> to upsert (monotonic on
|
||||
/// <see cref="SiteCall.TrackedOperationId"/>). The two rows are paired so the
|
||||
/// central dual-write transaction can commit them atomically.
|
||||
/// </summary>
|
||||
public sealed record CachedTelemetryEntry(AuditEvent Audit, SiteCall SiteCall);
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Reply from the central <c>AuditLogIngestActor</c> for an
|
||||
/// <see cref="IngestCachedTelemetryCommand"/>. <see cref="AcceptedEventIds"/>
|
||||
/// lists every entry whose dual-write transaction (AuditLog INSERT + SiteCalls
|
||||
/// UPSERT) committed; entries whose transaction rolled back are absent so the
|
||||
/// site can leave the row Pending and retry on the next drain.
|
||||
/// </summary>
|
||||
public sealed record IngestCachedTelemetryReply(IReadOnlyList<Guid> AcceptedEventIds);
|
||||
@@ -0,0 +1,166 @@
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Audit;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Site Calls UI -> Central: paginated, filtered query over the central
|
||||
/// <c>SiteCalls</c> table (Site Call Audit #22). All filter fields are optional;
|
||||
/// <see cref="StuckOnly"/> restricts results to stuck cached calls. Mirrors
|
||||
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Messages.Notification.NotificationOutboxQueryRequest"/>
|
||||
/// but uses keyset paging (<see cref="AfterCreatedAtUtc"/> + <see cref="AfterId"/>)
|
||||
/// to match the repository's <c>(CreatedAtUtc DESC, TrackedOperationId DESC)</c>
|
||||
/// cursor, rather than page numbers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="ChannelFilter"/> matches the <c>SiteCall.Channel</c> column —
|
||||
/// <c>"ApiOutbound"</c> or <c>"DbOutbound"</c> (the spec's <c>Kind</c> notion;
|
||||
/// the entity exposes it as <c>Channel</c>). <see cref="TargetKeyword"/> is an
|
||||
/// exact-match target filter, consistent with the repository's
|
||||
/// <see cref="SiteCallQueryFilter.Target"/> predicate.
|
||||
/// </remarks>
|
||||
/// <param name="PageSize">
|
||||
/// Requested page size. The actor clamps this to the <c>[1, 200]</c> range, so
|
||||
/// the effective ceiling is 200 rows per page regardless of the value sent.
|
||||
/// </param>
|
||||
public sealed record SiteCallQueryRequest(
|
||||
string CorrelationId,
|
||||
string? StatusFilter,
|
||||
string? SourceSiteFilter,
|
||||
string? ChannelFilter,
|
||||
string? TargetKeyword,
|
||||
bool StuckOnly,
|
||||
DateTime? FromUtc,
|
||||
DateTime? ToUtc,
|
||||
DateTime? AfterCreatedAtUtc,
|
||||
Guid? AfterId,
|
||||
int PageSize,
|
||||
string? SourceNodeFilter = null);
|
||||
|
||||
/// <summary>
|
||||
/// A single <c>SiteCalls</c> row summarised for the Site Calls UI grid. Carries
|
||||
/// only the columns the <see cref="ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit.SiteCall"/>
|
||||
/// entity genuinely exposes — there are no source-instance/script provenance
|
||||
/// columns on that entity, so unlike
|
||||
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Messages.Notification.NotificationSummary"/>
|
||||
/// none are surfaced here.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="HttpStatus"/> is not called out in the Site Call Audit plan, but
|
||||
/// it is a real (nullable) <see cref="ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit.SiteCall"/>
|
||||
/// column — the last HTTP status code observed for the call — so it is surfaced
|
||||
/// here for the grid; <c>null</c> for non-HTTP channels or before a first attempt.
|
||||
/// </remarks>
|
||||
public sealed record SiteCallSummary(
|
||||
Guid TrackedOperationId,
|
||||
string SourceSite,
|
||||
string Channel,
|
||||
string Target,
|
||||
string Status,
|
||||
int RetryCount,
|
||||
string? LastError,
|
||||
int? HttpStatus,
|
||||
DateTime CreatedAtUtc,
|
||||
DateTime UpdatedAtUtc,
|
||||
DateTime? TerminalAtUtc,
|
||||
bool IsStuck,
|
||||
string? SourceNode = null);
|
||||
|
||||
/// <summary>
|
||||
/// Central -> Site Calls UI: paginated response for a <see cref="SiteCallQueryRequest"/>.
|
||||
/// The keyset cursor of the last row is echoed back as
|
||||
/// <see cref="NextAfterCreatedAtUtc"/> + <see cref="NextAfterId"/> for the caller
|
||||
/// to request the following page; both are <c>null</c> when the page was empty.
|
||||
/// On a repository fault <see cref="Success"/> is <c>false</c>,
|
||||
/// <see cref="ErrorMessage"/> carries the cause and <see cref="SiteCalls"/> is empty.
|
||||
/// </summary>
|
||||
public sealed record SiteCallQueryResponse(
|
||||
string CorrelationId,
|
||||
bool Success,
|
||||
string? ErrorMessage,
|
||||
IReadOnlyList<SiteCallSummary> SiteCalls,
|
||||
DateTime? NextAfterCreatedAtUtc,
|
||||
Guid? NextAfterId);
|
||||
|
||||
/// <summary>
|
||||
/// Site Calls UI -> Central: request for the full detail of a single cached call,
|
||||
/// for the report detail modal.
|
||||
/// </summary>
|
||||
public sealed record SiteCallDetailRequest(
|
||||
string CorrelationId,
|
||||
Guid TrackedOperationId);
|
||||
|
||||
/// <summary>
|
||||
/// Central -> Site Calls UI: full detail for one cached call. On a repository
|
||||
/// fault or missing row, <see cref="Success"/> is <c>false</c> /
|
||||
/// <see cref="Detail"/> is <c>null</c> and <see cref="ErrorMessage"/> carries
|
||||
/// the cause.
|
||||
/// </summary>
|
||||
public sealed record SiteCallDetailResponse(
|
||||
string CorrelationId,
|
||||
bool Success,
|
||||
string? ErrorMessage,
|
||||
SiteCallDetail? Detail);
|
||||
|
||||
/// <summary>
|
||||
/// Full <c>SiteCalls</c> row detail for the report detail modal — every field
|
||||
/// on the <see cref="ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit.SiteCall"/> entity,
|
||||
/// including <see cref="LastError"/> and the <see cref="IngestedAtUtc"/>
|
||||
/// timestamp the grid summary omits.
|
||||
/// </summary>
|
||||
public sealed record SiteCallDetail(
|
||||
Guid TrackedOperationId,
|
||||
string SourceSite,
|
||||
string Channel,
|
||||
string Target,
|
||||
string Status,
|
||||
int RetryCount,
|
||||
string? LastError,
|
||||
int? HttpStatus,
|
||||
DateTime CreatedAtUtc,
|
||||
DateTime UpdatedAtUtc,
|
||||
DateTime? TerminalAtUtc,
|
||||
DateTime IngestedAtUtc,
|
||||
string? SourceNode = null);
|
||||
|
||||
/// <summary>
|
||||
/// Site Calls UI -> Central: request for the global <c>SiteCalls</c> KPI summary.
|
||||
/// Mirrors <see cref="ZB.MOM.WW.ScadaBridge.Commons.Messages.Notification.NotificationKpiRequest"/>.
|
||||
/// </summary>
|
||||
public sealed record SiteCallKpiRequest(
|
||||
string CorrelationId);
|
||||
|
||||
/// <summary>
|
||||
/// Central -> Site Calls UI: KPI summary for the Site Calls dashboard. On a
|
||||
/// repository fault <see cref="Success"/> is <c>false</c>,
|
||||
/// <see cref="ErrorMessage"/> carries the cause, and the KPI fields are
|
||||
/// zeroed/<c>null</c>.
|
||||
/// </summary>
|
||||
public sealed record SiteCallKpiResponse(
|
||||
string CorrelationId,
|
||||
bool Success,
|
||||
string? ErrorMessage,
|
||||
int BufferedCount,
|
||||
int ParkedCount,
|
||||
int FailedLastInterval,
|
||||
int DeliveredLastInterval,
|
||||
TimeSpan? OldestPendingAge,
|
||||
int StuckCount);
|
||||
|
||||
/// <summary>
|
||||
/// Site Calls UI -> Central: request for the per-source-site <c>SiteCalls</c>
|
||||
/// KPI breakdown. Mirrors
|
||||
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Messages.Notification.PerSiteNotificationKpiRequest"/>.
|
||||
/// </summary>
|
||||
public sealed record PerSiteSiteCallKpiRequest(
|
||||
string CorrelationId);
|
||||
|
||||
/// <summary>
|
||||
/// Central -> Site Calls UI: per-site KPI breakdown for the Site Calls KPIs
|
||||
/// page. On a repository fault <see cref="Success"/> is <c>false</c>,
|
||||
/// <see cref="ErrorMessage"/> carries the cause, and <see cref="Sites"/> is empty.
|
||||
/// </summary>
|
||||
public sealed record PerSiteSiteCallKpiResponse(
|
||||
string CorrelationId,
|
||||
bool Success,
|
||||
string? ErrorMessage,
|
||||
IReadOnlyList<SiteCallSiteKpiSnapshot> Sites);
|
||||
@@ -0,0 +1,113 @@
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Outcome of a Site Call Audit (#22) Retry/Discard relay — distinguishes the
|
||||
/// three cases the Central UI Site Calls page must surface differently.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The "site unreachable" case is deliberately separate from
|
||||
/// <see cref="OperationFailed"/>: central is an eventually-consistent mirror,
|
||||
/// not the source of truth, so a relay that never reaches the owning site is a
|
||||
/// transient transport condition the operator can retry — not a failed
|
||||
/// operation. The UI shows "site unreachable" rather than a generic error.
|
||||
/// </remarks>
|
||||
public enum SiteCallRelayOutcome
|
||||
{
|
||||
/// <summary>
|
||||
/// The owning site received the relay command and applied the action to its
|
||||
/// Store-and-Forward buffer (the parked cached call was reset to retry, or
|
||||
/// discarded). The corrected state reaches central later via telemetry.
|
||||
/// </summary>
|
||||
Applied,
|
||||
|
||||
/// <summary>
|
||||
/// The owning site received the relay command but found nothing to do — no
|
||||
/// parked row matched the tracked id (already delivered/discarded, or no
|
||||
/// longer <c>Parked</c>). A definitive answer from the site, not a failure.
|
||||
/// </summary>
|
||||
NotParked,
|
||||
|
||||
/// <summary>
|
||||
/// The owning site could not be reached (offline / no ClusterClient route /
|
||||
/// relay timed out). The action was NOT applied; the operator may retry once
|
||||
/// the site is back online.
|
||||
/// </summary>
|
||||
SiteUnreachable,
|
||||
|
||||
/// <summary>
|
||||
/// The owning site was reached but reported it could not apply the action
|
||||
/// (its parked-message handler was unavailable or its store faulted).
|
||||
/// </summary>
|
||||
OperationFailed,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Central UI → Site Call Audit: relay a Retry of a parked cached call to its
|
||||
/// owning site. The owning site performs the actual retry on its
|
||||
/// Store-and-Forward buffer — central never mutates the central <c>SiteCalls</c>
|
||||
/// mirror row. Mirrors
|
||||
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Messages.Notification.RetryNotificationRequest"/>
|
||||
/// but carries <see cref="SourceSite"/> (the relay target) and answers with a
|
||||
/// distinct site-unreachable outcome.
|
||||
/// </summary>
|
||||
/// <param name="CorrelationId">Request correlation id, echoed on the response.</param>
|
||||
/// <param name="TrackedOperationId">
|
||||
/// The cached operation to retry — the PK of the central <c>SiteCalls</c> row
|
||||
/// and the S&F buffer message id at the owning site.
|
||||
/// </param>
|
||||
/// <param name="SourceSite">
|
||||
/// The owning site (<c>SiteCall.SourceSite</c>) the relay is routed to.
|
||||
/// </param>
|
||||
public sealed record RetrySiteCallRequest(
|
||||
string CorrelationId,
|
||||
Guid TrackedOperationId,
|
||||
string SourceSite);
|
||||
|
||||
/// <summary>
|
||||
/// Site Call Audit → Central UI: result of a <see cref="RetrySiteCallRequest"/>.
|
||||
/// </summary>
|
||||
/// <param name="CorrelationId">Echoed request correlation id.</param>
|
||||
/// <param name="Outcome">
|
||||
/// The relay outcome — <see cref="SiteCallRelayOutcome.Applied"/>,
|
||||
/// <see cref="SiteCallRelayOutcome.NotParked"/>,
|
||||
/// <see cref="SiteCallRelayOutcome.SiteUnreachable"/> or
|
||||
/// <see cref="SiteCallRelayOutcome.OperationFailed"/>.
|
||||
/// </param>
|
||||
/// <param name="Success">
|
||||
/// Convenience flag — <c>true</c> only for <see cref="SiteCallRelayOutcome.Applied"/>.
|
||||
/// </param>
|
||||
/// <param name="SiteReachable">
|
||||
/// <c>false</c> only for <see cref="SiteCallRelayOutcome.SiteUnreachable"/>; lets
|
||||
/// the UI distinguish "site offline" from "operation failed" without switching
|
||||
/// on the enum.
|
||||
/// </param>
|
||||
/// <param name="ErrorMessage">
|
||||
/// Human-readable detail for a non-applied outcome; <c>null</c> on success.
|
||||
/// </param>
|
||||
public sealed record RetrySiteCallResponse(
|
||||
string CorrelationId,
|
||||
SiteCallRelayOutcome Outcome,
|
||||
bool Success,
|
||||
bool SiteReachable,
|
||||
string? ErrorMessage);
|
||||
|
||||
/// <summary>
|
||||
/// Central UI → Site Call Audit: relay a Discard of a parked cached call to its
|
||||
/// owning site. See <see cref="RetrySiteCallRequest"/> for the source-of-truth
|
||||
/// and routing rationale.
|
||||
/// </summary>
|
||||
public sealed record DiscardSiteCallRequest(
|
||||
string CorrelationId,
|
||||
Guid TrackedOperationId,
|
||||
string SourceSite);
|
||||
|
||||
/// <summary>
|
||||
/// Site Call Audit → Central UI: result of a <see cref="DiscardSiteCallRequest"/>.
|
||||
/// Same shape as <see cref="RetrySiteCallResponse"/>.
|
||||
/// </summary>
|
||||
public sealed record DiscardSiteCallResponse(
|
||||
string CorrelationId,
|
||||
SiteCallRelayOutcome Outcome,
|
||||
bool Success,
|
||||
bool SiteReachable,
|
||||
string? ErrorMessage);
|
||||
@@ -0,0 +1,19 @@
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Akka message sent to the central <c>SiteCallAuditActor</c> (Site Call Audit
|
||||
/// #22, Audit Log #23 M3 Bundle C) carrying one <see cref="SiteCall"/> row to
|
||||
/// be persisted via <c>ISiteCallAuditRepository.UpsertAsync</c>. The repository
|
||||
/// performs an insert-if-not-exists then monotonic update — duplicate gRPC
|
||||
/// packets and reconciliation pulls can both feed the actor without rolling
|
||||
/// state back.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Lives in <c>ZB.MOM.WW.ScadaBridge.Commons</c> rather than <c>ZB.MOM.WW.ScadaBridge.SiteCallAudit</c>
|
||||
/// so the gRPC server in <c>ZB.MOM.WW.ScadaBridge.Communication</c> can construct it
|
||||
/// without taking a project reference on the actor's host project (Bundle D
|
||||
/// adds the IngestCachedTelemetry RPC that will Tell this command).
|
||||
/// </remarks>
|
||||
public sealed record UpsertSiteCallCommand(SiteCall SiteCall);
|
||||
@@ -0,0 +1,14 @@
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Reply from the central <c>SiteCallAuditActor</c> for an
|
||||
/// <see cref="UpsertSiteCallCommand"/>. <see cref="Accepted"/> is <c>true</c>
|
||||
/// when the upsert reached the repository without throwing (including the
|
||||
/// monotonic-no-op case where the stored status' rank wins) and <c>false</c>
|
||||
/// when persistence raised an exception. The actor itself stays alive in
|
||||
/// either case — audit-write failures must NEVER abort the user-facing action
|
||||
/// (Audit Log #23 §13).
|
||||
/// </summary>
|
||||
public sealed record UpsertSiteCallReply(TrackedOperationId TrackedOperationId, bool Accepted);
|
||||
Reference in New Issue
Block a user