Files
git.stella-ops.org/src/Policy/StellaOps.Policy.Engine/Gates/Determinization/SignalSnapshotBuilder.cs
2026-01-07 09:43:12 +02:00

96 lines
3.0 KiB
C#

using Microsoft.Extensions.Logging;
using StellaOps.Policy.Determinization.Models;
namespace StellaOps.Policy.Engine.Gates.Determinization;
/// <summary>
/// Builds signal snapshots for determinization evaluation by querying signal repositories.
/// </summary>
public sealed class SignalSnapshotBuilder : ISignalSnapshotBuilder
{
private readonly ISignalRepository _signalRepository;
private readonly TimeProvider _timeProvider;
private readonly ILogger<SignalSnapshotBuilder> _logger;
public SignalSnapshotBuilder(
ISignalRepository signalRepository,
TimeProvider timeProvider,
ILogger<SignalSnapshotBuilder> logger)
{
_signalRepository = signalRepository;
_timeProvider = timeProvider;
_logger = logger;
}
public async Task<SignalSnapshot> BuildAsync(
string cveId,
string componentPurl,
CancellationToken ct = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(cveId);
ArgumentException.ThrowIfNullOrWhiteSpace(componentPurl);
_logger.LogDebug(
"Building signal snapshot for CVE {CveId} on {Purl}",
cveId,
componentPurl);
var snapshotAt = _timeProvider.GetUtcNow();
var subjectKey = BuildSubjectKey(cveId, componentPurl);
// Query all signals in parallel
var signalsTask = _signalRepository.GetSignalsAsync(subjectKey, ct);
var signals = await signalsTask;
// Build snapshot from retrieved signals
var snapshot = SignalSnapshot.Empty(cveId, componentPurl, snapshotAt);
foreach (var signal in signals)
{
snapshot = ApplySignal(snapshot, signal);
}
_logger.LogDebug(
"Built signal snapshot for CVE {CveId} on {Purl}: {SignalCount} signals present",
cveId,
componentPurl,
signals.Count);
return snapshot;
}
private static string BuildSubjectKey(string cveId, string componentPurl)
=> $"{cveId}::{componentPurl}";
private SignalSnapshot ApplySignal(SignalSnapshot snapshot, Signal signal)
{
// This is a placeholder implementation
// In a real implementation, this would map Signal objects to SignalState<T> instances
// based on signal type and update the appropriate field in the snapshot
return snapshot;
}
}
/// <summary>
/// Repository for retrieving signals.
/// </summary>
public interface ISignalRepository
{
/// <summary>
/// Get all signals for the given subject key.
/// </summary>
Task<IReadOnlyList<Signal>> GetSignalsAsync(string subjectKey, CancellationToken ct = default);
}
/// <summary>
/// Represents a signal retrieved from storage.
/// </summary>
public sealed record Signal
{
public required string Type { get; init; }
public required string SubjectKey { get; init; }
public required DateTimeOffset ObservedAt { get; init; }
public required object? Evidence { get; init; }
}