Files
git.stella-ops.org/src/Scanner/__Libraries/StellaOps.Scanner.Sources/Domain/SbomSourceRun.cs
2026-01-04 21:48:13 +02:00

180 lines
5.6 KiB
C#

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