feat(sitecallaudit): query, KPI and detail backend for the Site Calls page
This commit is contained in:
@@ -63,4 +63,27 @@ public interface ISiteCallAuditRepository
|
||||
/// deleted.
|
||||
/// </summary>
|
||||
Task<int> PurgeTerminalAsync(DateTime olderThanUtc, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Computes a point-in-time global <see cref="SiteCallKpiSnapshot"/> from the
|
||||
/// <c>SiteCalls</c> table. Counts are aggregated server-side (no row
|
||||
/// materialisation): <c>StuckCount</c> uses <paramref name="stuckCutoff"/>;
|
||||
/// <c>FailedLastInterval</c> / <c>DeliveredLastInterval</c> use
|
||||
/// <paramref name="intervalSince"/>; the current time for <c>OldestPendingAge</c>
|
||||
/// is captured inside the method.
|
||||
/// </summary>
|
||||
Task<SiteCallKpiSnapshot> ComputeKpisAsync(
|
||||
DateTime stuckCutoff,
|
||||
DateTime intervalSince,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Computes a point-in-time <see cref="SiteCallSiteKpiSnapshot"/> per source
|
||||
/// site. Sites with no <c>SiteCalls</c> rows at all are omitted. The stuck
|
||||
/// cutoff and interval bounds are interpreted as in <see cref="ComputeKpisAsync"/>.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<SiteCallSiteKpiSnapshot>> ComputePerSiteKpisAsync(
|
||||
DateTime stuckCutoff,
|
||||
DateTime intervalSince,
|
||||
CancellationToken ct = default);
|
||||
}
|
||||
|
||||
153
src/ScadaLink.Commons/Messages/Audit/SiteCallQueries.cs
Normal file
153
src/ScadaLink.Commons/Messages/Audit/SiteCallQueries.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using ScadaLink.Commons.Types.Audit;
|
||||
|
||||
namespace ScadaLink.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="ScadaLink.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>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// A single <c>SiteCalls</c> row summarised for the Site Calls UI grid. Carries
|
||||
/// only the columns the <see cref="ScadaLink.Commons.Entities.Audit.SiteCall"/>
|
||||
/// entity genuinely exposes — there are no source-instance/script provenance
|
||||
/// columns on that entity, so unlike
|
||||
/// <see cref="ScadaLink.Commons.Messages.Notification.NotificationSummary"/>
|
||||
/// none are surfaced here.
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
/// <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="ScadaLink.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);
|
||||
|
||||
/// <summary>
|
||||
/// Site Calls UI -> Central: request for the global <c>SiteCalls</c> KPI summary.
|
||||
/// Mirrors <see cref="ScadaLink.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="ScadaLink.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);
|
||||
38
src/ScadaLink.Commons/Types/Audit/SiteCallKpiSnapshot.cs
Normal file
38
src/ScadaLink.Commons/Types/Audit/SiteCallKpiSnapshot.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
namespace ScadaLink.Commons.Types.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Point-in-time operational metrics for the central <c>SiteCalls</c> table
|
||||
/// (Site Call Audit #22), surfaced on the health dashboard. The cached-call
|
||||
/// counterpart of <see cref="ScadaLink.Commons.Types.Notifications.NotificationKpiSnapshot"/>;
|
||||
/// mirrors its shape so the Central UI Site Calls KPI tiles can reuse the
|
||||
/// Notification Outbox tile layout.
|
||||
/// </summary>
|
||||
/// <param name="BufferedCount">
|
||||
/// Count of non-terminal rows (<c>Pending</c> + <c>Retrying</c>) — calls
|
||||
/// buffered at sites awaiting retry.
|
||||
/// </param>
|
||||
/// <param name="ParkedCount">Count of rows in the <c>Parked</c> status.</param>
|
||||
/// <param name="FailedLastInterval">
|
||||
/// Count of <c>Failed</c> rows whose <see cref="ScadaLink.Commons.Entities.Audit.SiteCall.TerminalAtUtc"/>
|
||||
/// is at or after the supplied "since" timestamp.
|
||||
/// </param>
|
||||
/// <param name="DeliveredLastInterval">
|
||||
/// Count of <c>Delivered</c> rows whose <see cref="ScadaLink.Commons.Entities.Audit.SiteCall.TerminalAtUtc"/>
|
||||
/// is at or after the supplied "since" timestamp.
|
||||
/// </param>
|
||||
/// <param name="OldestPendingAge">
|
||||
/// Age of the oldest non-terminal row (<c>now - min(CreatedAtUtc)</c>), or
|
||||
/// <c>null</c> when there are no non-terminal rows.
|
||||
/// </param>
|
||||
/// <param name="StuckCount">
|
||||
/// Count of non-terminal rows (<c>Pending</c>/<c>Retrying</c>) whose
|
||||
/// <see cref="ScadaLink.Commons.Entities.Audit.SiteCall.CreatedAtUtc"/> is older
|
||||
/// than the supplied stuck cutoff. Display-only — no escalation.
|
||||
/// </param>
|
||||
public sealed record SiteCallKpiSnapshot(
|
||||
int BufferedCount,
|
||||
int ParkedCount,
|
||||
int FailedLastInterval,
|
||||
int DeliveredLastInterval,
|
||||
TimeSpan? OldestPendingAge,
|
||||
int StuckCount);
|
||||
34
src/ScadaLink.Commons/Types/Audit/SiteCallSiteKpiSnapshot.cs
Normal file
34
src/ScadaLink.Commons/Types/Audit/SiteCallSiteKpiSnapshot.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
namespace ScadaLink.Commons.Types.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// Point-in-time <c>SiteCalls</c> metrics scoped to a single source site. The
|
||||
/// per-site counterpart of <see cref="SiteCallKpiSnapshot"/>; surfaced in the
|
||||
/// per-site breakdown table on the Site Calls KPIs page. Mirrors
|
||||
/// <see cref="ScadaLink.Commons.Types.Notifications.SiteNotificationKpiSnapshot"/>.
|
||||
/// </summary>
|
||||
/// <param name="SourceSite">The site identifier these metrics are scoped to.</param>
|
||||
/// <param name="BufferedCount">Count of this site's non-terminal rows (<c>Pending</c> + <c>Retrying</c>).</param>
|
||||
/// <param name="ParkedCount">Count of this site's rows in the <c>Parked</c> status.</param>
|
||||
/// <param name="FailedLastInterval">
|
||||
/// Count of this site's <c>Failed</c> rows whose <c>TerminalAtUtc</c> is at or
|
||||
/// after the "since" timestamp.
|
||||
/// </param>
|
||||
/// <param name="DeliveredLastInterval">
|
||||
/// Count of this site's <c>Delivered</c> rows whose <c>TerminalAtUtc</c> is at
|
||||
/// or after the "since" timestamp.
|
||||
/// </param>
|
||||
/// <param name="OldestPendingAge">
|
||||
/// Age of this site's oldest non-terminal row, or <c>null</c> when it has none.
|
||||
/// </param>
|
||||
/// <param name="StuckCount">
|
||||
/// Count of this site's non-terminal rows whose <c>CreatedAtUtc</c> is older
|
||||
/// than the stuck cutoff.
|
||||
/// </param>
|
||||
public sealed record SiteCallSiteKpiSnapshot(
|
||||
string SourceSite,
|
||||
int BufferedCount,
|
||||
int ParkedCount,
|
||||
int FailedLastInterval,
|
||||
int DeliveredLastInterval,
|
||||
TimeSpan? OldestPendingAge,
|
||||
int StuckCount);
|
||||
Reference in New Issue
Block a user