using ScadaLink.Commons.Types; namespace ScadaLink.Commons.Interfaces.Services; /// /// Audit Log #23 (M3 Bundle E — Tasks E4/E5): site-side hook the /// store-and-forward retry loop invokes after every cached-call attempt and /// at terminal-state transitions, so the audit pipeline can emit /// ApiCallCached/DbWriteCached per-attempt rows and the /// CachedResolve terminal row under the original /// . /// /// /// /// The interface deliberately uses /// rather than so the /// S&F project does not need to depend on the audit vocabulary — the /// bridge living in ScadaLink.AuditLog maps the outcome to the right /// audit kind + status when materialising the CachedCallTelemetry /// packet. /// /// /// Best-effort contract (alog.md §7): implementations MUST swallow /// internal failures rather than propagating to the S&F service — a /// thrown observer must not be misclassified as a transient delivery /// failure and must not corrupt the retry-count bookkeeping. /// /// public interface ICachedCallLifecycleObserver { /// /// Called by the store-and-forward retry loop after every cached-call /// delivery attempt. Receives the message's TrackedOperationId-bearing id, /// the per-category channel discriminator, retry-count + last-error /// context, and whether the outcome reached a terminal state. /// Task OnAttemptCompletedAsync(CachedCallAttemptContext context, CancellationToken ct = default); } /// /// Per-attempt context handed to . /// /// /// Tracking id parsed from the underlying StoreAndForwardMessage.Id. /// /// /// Trust-boundary channel string — "ApiOutbound" for ExternalSystem /// cached calls, "DbOutbound" for cached DB writes. /// /// Human-readable target (system name / DB connection). /// Site id that submitted the cached call. /// Per-attempt outcome. /// Number of retries performed so far (S&F bookkeeping). /// Most recent error message (null on success). /// Most recent HTTP status (null when not applicable). /// When the underlying S&F message was first enqueued. /// When this attempt completed. /// Duration of the attempt in milliseconds (null when not measured). /// Originating instance, when known. public sealed record CachedCallAttemptContext( TrackedOperationId TrackedOperationId, string Channel, string Target, string SourceSite, CachedCallAttemptOutcome Outcome, int RetryCount, string? LastError, int? HttpStatus, DateTime CreatedAtUtc, DateTime OccurredAtUtc, int? DurationMs, string? SourceInstanceId); /// /// Coarse outcome of one cached-call delivery attempt, observed from inside /// the store-and-forward retry loop. The audit bridge maps this to the /// ApiCallCached/DbWriteCached Attempted row and, when terminal, /// the corresponding CachedResolve row. /// public enum CachedCallAttemptOutcome { /// Attempt delivered successfully — terminal Delivered state. Delivered, /// Attempt failed transiently; another retry will follow. TransientFailure, /// Attempt returned permanent failure — terminal Parked state (S&F semantics). PermanentFailure, /// Retry budget exhausted — terminal Parked state. ParkedMaxRetries, }