namespace StellaOps.Scanner.Sources.Domain; #pragma warning disable CA1062 // Validate arguments of public methods - TimeProvider validated at DI boundary /// /// Represents a single execution run of an SBOM source. /// Tracks status, timing, item counts, and any errors. /// public sealed class SbomSourceRun { /// Unique run identifier. public Guid RunId { get; init; } /// Source that was run. public Guid SourceId { get; init; } /// Tenant owning the source. public string TenantId { get; init; } = null!; /// What triggered this run. public SbomSourceRunTrigger Trigger { get; init; } /// Additional trigger details (webhook payload digest, cron expression, etc.). public string? TriggerDetails { get; init; } /// Current status of the run. public SbomSourceRunStatus Status { get; private set; } = SbomSourceRunStatus.Running; /// When the run started. public DateTimeOffset StartedAt { get; init; } /// When the run completed (if finished). public DateTimeOffset? CompletedAt { get; private set; } /// /// Duration in milliseconds. Pass a TimeProvider to get the live duration for in-progress runs. /// public long GetDurationMs(TimeProvider? timeProvider = null) { if (CompletedAt.HasValue) return (long)(CompletedAt.Value - StartedAt).TotalMilliseconds; var now = timeProvider?.GetUtcNow() ?? DateTimeOffset.UtcNow; return (long)(now - StartedAt).TotalMilliseconds; } /// Number of items discovered to scan. public int ItemsDiscovered { get; private set; } /// Number of items that were scanned. public int ItemsScanned { get; private set; } /// Number of items that succeeded. public int ItemsSucceeded { get; private set; } /// Number of items that failed. public int ItemsFailed { get; private set; } /// Number of items that were skipped. public int ItemsSkipped { get; private set; } /// IDs of scan jobs created by this run. public List ScanJobIds { get; init; } = []; /// Error message if failed. public string? ErrorMessage { get; private set; } /// Error stack trace if failed. public string? ErrorStackTrace { get; private set; } /// Correlation ID for distributed tracing. public string CorrelationId { get; init; } = null!; // ------------------------------------------------------------------------- // Factory Methods // ------------------------------------------------------------------------- /// /// Create a new source run. /// public static SbomSourceRun Create( Guid sourceId, string tenantId, SbomSourceRunTrigger trigger, string correlationId, TimeProvider timeProvider, string? triggerDetails = null) { return new SbomSourceRun { RunId = Guid.NewGuid(), SourceId = sourceId, TenantId = tenantId, Trigger = trigger, TriggerDetails = triggerDetails, Status = SbomSourceRunStatus.Running, StartedAt = timeProvider.GetUtcNow(), CorrelationId = correlationId }; } // ------------------------------------------------------------------------- // Progress Updates // ------------------------------------------------------------------------- /// /// Set the number of discovered items. /// public void SetDiscoveredItems(int count) { ItemsDiscovered = count; } /// /// Record a successfully scanned item. /// public void RecordItemSuccess(Guid scanJobId) { ItemsScanned++; ItemsSucceeded++; ScanJobIds.Add(scanJobId); } /// /// Record a failed item. /// public void RecordItemFailure() { ItemsScanned++; ItemsFailed++; } /// /// Record a skipped item. /// public void RecordItemSkipped() { ItemsSkipped++; } // ------------------------------------------------------------------------- // Completion // ------------------------------------------------------------------------- /// /// Complete the run successfully. /// public void Complete(TimeProvider timeProvider) { Status = ItemsFailed > 0 ? SbomSourceRunStatus.PartialSuccess : ItemsSucceeded > 0 ? SbomSourceRunStatus.Succeeded : SbomSourceRunStatus.Skipped; CompletedAt = timeProvider.GetUtcNow(); } /// /// Fail the run with an error. /// public void Fail(string message, TimeProvider timeProvider, string? stackTrace = null) { Status = SbomSourceRunStatus.Failed; ErrorMessage = message; ErrorStackTrace = stackTrace; CompletedAt = timeProvider.GetUtcNow(); } /// /// Cancel the run. /// public void Cancel(string reason, TimeProvider timeProvider) { Status = SbomSourceRunStatus.Cancelled; ErrorMessage = reason; CompletedAt = timeProvider.GetUtcNow(); } }