docs re-org, audit fixes, build fixes
This commit is contained in:
@@ -7,11 +7,11 @@ using StellaOps.SbomService.Models;
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public class EntrypointEndpointsTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
public class EntrypointEndpointsTests : IClassFixture<WebApplicationFactory<StellaOps.SbomService.Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
private readonly WebApplicationFactory<StellaOps.SbomService.Program> _factory;
|
||||
|
||||
public EntrypointEndpointsTests(WebApplicationFactory<Program> factory)
|
||||
public EntrypointEndpointsTests(WebApplicationFactory<StellaOps.SbomService.Program> factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ using Xunit;
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public class OrchestratorEndpointsTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
public class OrchestratorEndpointsTests : IClassFixture<WebApplicationFactory<StellaOps.SbomService.Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
private readonly WebApplicationFactory<StellaOps.SbomService.Program> _factory;
|
||||
|
||||
public OrchestratorEndpointsTests(WebApplicationFactory<Program> factory)
|
||||
public OrchestratorEndpointsTests(WebApplicationFactory<StellaOps.SbomService.Program> factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ using Xunit;
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public class ProjectionEndpointTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
public class ProjectionEndpointTests : IClassFixture<WebApplicationFactory<StellaOps.SbomService.Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
private readonly WebApplicationFactory<StellaOps.SbomService.Program> _factory;
|
||||
|
||||
public ProjectionEndpointTests(WebApplicationFactory<Program> factory)
|
||||
public ProjectionEndpointTests(WebApplicationFactory<StellaOps.SbomService.Program> factory)
|
||||
{
|
||||
var contentRoot = ResolveContentRoot();
|
||||
_factory = factory.WithWebHostBuilder(builder =>
|
||||
|
||||
@@ -8,11 +8,11 @@ using Xunit;
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public class ResolverFeedExportTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
public class ResolverFeedExportTests : IClassFixture<WebApplicationFactory<StellaOps.SbomService.Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
private readonly WebApplicationFactory<StellaOps.SbomService.Program> _factory;
|
||||
|
||||
public ResolverFeedExportTests(WebApplicationFactory<Program> factory)
|
||||
public ResolverFeedExportTests(WebApplicationFactory<StellaOps.SbomService.Program> factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ using Xunit;
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public class SbomAssetEventsTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
public class SbomAssetEventsTests : IClassFixture<WebApplicationFactory<StellaOps.SbomService.Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
private readonly WebApplicationFactory<StellaOps.SbomService.Program> _factory;
|
||||
|
||||
public SbomAssetEventsTests(WebApplicationFactory<Program> factory)
|
||||
public SbomAssetEventsTests(WebApplicationFactory<StellaOps.SbomService.Program> factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ using Xunit;
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public class SbomEndpointsTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
public class SbomEndpointsTests : IClassFixture<WebApplicationFactory<StellaOps.SbomService.Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
private readonly WebApplicationFactory<StellaOps.SbomService.Program> _factory;
|
||||
|
||||
public SbomEndpointsTests(WebApplicationFactory<Program> factory)
|
||||
public SbomEndpointsTests(WebApplicationFactory<StellaOps.SbomService.Program> factory)
|
||||
{
|
||||
_factory = factory.WithWebHostBuilder(_ => { });
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ using Xunit;
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public class SbomEventEndpointsTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
public class SbomEventEndpointsTests : IClassFixture<WebApplicationFactory<StellaOps.SbomService.Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
private readonly WebApplicationFactory<StellaOps.SbomService.Program> _factory;
|
||||
|
||||
public SbomEventEndpointsTests(WebApplicationFactory<Program> factory)
|
||||
public SbomEventEndpointsTests(WebApplicationFactory<StellaOps.SbomService.Program> factory)
|
||||
{
|
||||
_factory = factory.WithWebHostBuilder(_ => { });
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ using Xunit;
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public class SbomInventoryEventsTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
public class SbomInventoryEventsTests : IClassFixture<WebApplicationFactory<StellaOps.SbomService.Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
private readonly WebApplicationFactory<StellaOps.SbomService.Program> _factory;
|
||||
|
||||
public SbomInventoryEventsTests(WebApplicationFactory<Program> factory)
|
||||
public SbomInventoryEventsTests(WebApplicationFactory<StellaOps.SbomService.Program> factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@ using Xunit;
|
||||
using StellaOps.TestKit;
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public sealed class SbomLedgerEndpointsTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
public sealed class SbomLedgerEndpointsTests : IClassFixture<WebApplicationFactory<StellaOps.SbomService.Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
private readonly WebApplicationFactory<StellaOps.SbomService.Program> _factory;
|
||||
|
||||
public SbomLedgerEndpointsTests(WebApplicationFactory<Program> factory)
|
||||
public SbomLedgerEndpointsTests(WebApplicationFactory<StellaOps.SbomService.Program> factory)
|
||||
{
|
||||
_factory = factory.WithWebHostBuilder(_ => { });
|
||||
}
|
||||
|
||||
@@ -1311,5 +1311,8 @@ app.MapPost("/internal/orchestrator/watermarks", async Task<IResult> (
|
||||
|
||||
app.Run();
|
||||
|
||||
// Program class public for WebApplicationFactory<Program>
|
||||
public partial class Program;
|
||||
// Program class in namespace to avoid conflicts with other assemblies
|
||||
namespace StellaOps.SbomService
|
||||
{
|
||||
public partial class Program;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@ namespace StellaOps.SbomService.Repositories;
|
||||
internal sealed class InMemoryOrchestratorRepository : IOrchestratorRepository
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, List<OrchestratorSource>> _sources = new(StringComparer.Ordinal);
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public InMemoryOrchestratorRepository()
|
||||
public InMemoryOrchestratorRepository(TimeProvider? timeProvider = null)
|
||||
{
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
Seed();
|
||||
}
|
||||
|
||||
@@ -37,7 +39,7 @@ internal sealed class InMemoryOrchestratorRepository : IOrchestratorRepository
|
||||
sourceId,
|
||||
request.ArtifactDigest.Trim(),
|
||||
request.SourceType.Trim(),
|
||||
DateTimeOffset.UtcNow,
|
||||
_timeProvider.GetUtcNow(),
|
||||
request.Metadata.Trim());
|
||||
|
||||
// Idempotent on (tenant, artifactDigest, sourceType)
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace StellaOps.SbomService.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the current time abstraction - delegates to TimeProvider.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Deprecated: Prefer injecting TimeProvider directly. This interface is kept
|
||||
/// for backward compatibility during migration.
|
||||
/// </remarks>
|
||||
public interface IClock
|
||||
{
|
||||
DateTimeOffset UtcNow { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default IClock implementation that delegates to TimeProvider.
|
||||
/// </summary>
|
||||
public sealed class SystemClock : IClock
|
||||
{
|
||||
public DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public SystemClock(TimeProvider? timeProvider = null)
|
||||
{
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public DateTimeOffset UtcNow => _timeProvider.GetUtcNow();
|
||||
}
|
||||
|
||||
@@ -136,9 +136,9 @@ public sealed record ReplayVerificationResult
|
||||
public ImmutableArray<ReplayFieldDrift> Drifts { get; init; } = ImmutableArray<ReplayFieldDrift>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// When the verification was performed.
|
||||
/// When the verification was performed. Must be explicitly set by calling code.
|
||||
/// </summary>
|
||||
public DateTimeOffset VerifiedAt { get; init; } = DateTimeOffset.UtcNow;
|
||||
public required DateTimeOffset VerifiedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional message with additional context.
|
||||
@@ -249,7 +249,7 @@ public sealed record ReplayDriftAnalysis
|
||||
public required string DriftSummary { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the analysis was performed.
|
||||
/// When the analysis was performed. Must be explicitly set by calling code.
|
||||
/// </summary>
|
||||
public DateTimeOffset AnalyzedAt { get; init; } = DateTimeOffset.UtcNow;
|
||||
public required DateTimeOffset AnalyzedAt { get; init; }
|
||||
}
|
||||
|
||||
@@ -26,19 +26,22 @@ internal sealed class LineageCompareService : ILineageCompareService
|
||||
private readonly IVexDeltaRepository? _vexDeltaRepository;
|
||||
private readonly ILineageCompareCache? _cache;
|
||||
private readonly ILogger<LineageCompareService> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public LineageCompareService(
|
||||
ISbomLineageGraphService lineageService,
|
||||
ISbomLedgerService ledgerService,
|
||||
ILogger<LineageCompareService> logger,
|
||||
IVexDeltaRepository? vexDeltaRepository = null,
|
||||
ILineageCompareCache? cache = null)
|
||||
ILineageCompareCache? cache = null,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_lineageService = lineageService ?? throw new ArgumentNullException(nameof(lineageService));
|
||||
_ledgerService = ledgerService ?? throw new ArgumentNullException(nameof(ledgerService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_vexDeltaRepository = vexDeltaRepository;
|
||||
_cache = cache;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -139,7 +142,7 @@ internal sealed class LineageCompareService : ILineageCompareService
|
||||
FromDigest = fromDigest,
|
||||
ToDigest = toDigest,
|
||||
TenantId = tenantId,
|
||||
ComputedAt = DateTimeOffset.UtcNow,
|
||||
ComputedAt = _timeProvider.GetUtcNow(),
|
||||
FromArtifact = fromArtifact,
|
||||
ToArtifact = toArtifact,
|
||||
Summary = summary,
|
||||
|
||||
@@ -21,16 +21,19 @@ internal sealed class LineageExportService : ILineageExportService
|
||||
private readonly ISbomLineageGraphService _lineageService;
|
||||
private readonly IReplayHashService? _replayHashService;
|
||||
private readonly ILogger<LineageExportService> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private const long MaxExportSizeBytes = 50 * 1024 * 1024; // 50MB limit
|
||||
|
||||
public LineageExportService(
|
||||
ISbomLineageGraphService lineageService,
|
||||
ILogger<LineageExportService> logger,
|
||||
IReplayHashService? replayHashService = null)
|
||||
IReplayHashService? replayHashService = null,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_lineageService = lineageService;
|
||||
_logger = logger;
|
||||
_replayHashService = replayHashService;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public async Task<LineageExportResponse?> ExportAsync(
|
||||
@@ -59,7 +62,7 @@ internal sealed class LineageExportService : ILineageExportService
|
||||
Version = "1.0",
|
||||
FromDigest = request.FromDigest,
|
||||
ToDigest = request.ToDigest,
|
||||
GeneratedAt = DateTimeOffset.UtcNow,
|
||||
GeneratedAt = _timeProvider.GetUtcNow(),
|
||||
ReplayHash = diff.ReplayHash ?? ComputeFallbackHash(request.FromDigest, request.ToDigest),
|
||||
SbomDiff = request.IncludeSbomDiff ? diff.SbomDiff?.Summary : null,
|
||||
VexDeltas = request.IncludeVexDeltas ? diff.VexDiff : null,
|
||||
@@ -91,7 +94,7 @@ internal sealed class LineageExportService : ILineageExportService
|
||||
// Generate export ID and URL
|
||||
var exportId = Guid.NewGuid().ToString("N");
|
||||
var downloadUrl = $"/api/v1/lineage/export/{exportId}/download";
|
||||
var expiresAt = DateTimeOffset.UtcNow.AddHours(24);
|
||||
var expiresAt = _timeProvider.GetUtcNow().AddHours(24);
|
||||
|
||||
// TODO: Store evidence pack for retrieval (file system, blob storage, etc.)
|
||||
// For now, return metadata only
|
||||
@@ -114,9 +117,9 @@ internal sealed class LineageExportService : ILineageExportService
|
||||
};
|
||||
}
|
||||
|
||||
private static string ComputeFallbackHash(string fromDigest, string toDigest)
|
||||
private string ComputeFallbackHash(string fromDigest, string toDigest)
|
||||
{
|
||||
var input = $"{fromDigest}:{toDigest}:{DateTimeOffset.UtcNow:O}";
|
||||
var input = $"{fromDigest}:{toDigest}:{_timeProvider.GetUtcNow():O}";
|
||||
var bytes = Encoding.UTF8.GetBytes(input);
|
||||
var hashBytes = SHA256.HashData(bytes);
|
||||
return Convert.ToHexString(hashBytes).ToLowerInvariant();
|
||||
|
||||
@@ -221,11 +221,13 @@ internal sealed class InMemoryLineageHoverCache : ILineageHoverCache
|
||||
{
|
||||
private readonly Dictionary<string, (SbomLineageHoverCard Card, DateTimeOffset ExpiresAt)> _cache = new();
|
||||
private readonly LineageHoverCacheOptions _options;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly object _lock = new();
|
||||
|
||||
public InMemoryLineageHoverCache(LineageHoverCacheOptions? options = null)
|
||||
public InMemoryLineageHoverCache(LineageHoverCacheOptions? options = null, TimeProvider? timeProvider = null)
|
||||
{
|
||||
_options = options ?? new LineageHoverCacheOptions();
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public Task<SbomLineageHoverCard?> GetAsync(string fromDigest, string toDigest, string tenantId, CancellationToken ct = default)
|
||||
@@ -240,7 +242,7 @@ internal sealed class InMemoryLineageHoverCache : ILineageHoverCache
|
||||
{
|
||||
if (_cache.TryGetValue(key, out var entry))
|
||||
{
|
||||
if (entry.ExpiresAt > DateTimeOffset.UtcNow)
|
||||
if (entry.ExpiresAt > _timeProvider.GetUtcNow())
|
||||
{
|
||||
return Task.FromResult<SbomLineageHoverCard?>(entry.Card);
|
||||
}
|
||||
@@ -260,7 +262,7 @@ internal sealed class InMemoryLineageHoverCache : ILineageHoverCache
|
||||
}
|
||||
|
||||
var key = BuildKey(fromDigest, toDigest, tenantId);
|
||||
var expiresAt = DateTimeOffset.UtcNow.Add(_options.Ttl);
|
||||
var expiresAt = _timeProvider.GetUtcNow().Add(_options.Ttl);
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
|
||||
@@ -11,8 +11,8 @@ public sealed record OrchestratorControlState(
|
||||
string Backpressure,
|
||||
DateTimeOffset UpdatedAtUtc)
|
||||
{
|
||||
public static OrchestratorControlState Default(string tenantId) =>
|
||||
new(tenantId, false, 0, "normal", DateTimeOffset.UtcNow);
|
||||
public static OrchestratorControlState Default(string tenantId, TimeProvider? timeProvider = null) =>
|
||||
new(tenantId, false, 0, "normal", (timeProvider ?? TimeProvider.System).GetUtcNow());
|
||||
}
|
||||
|
||||
public sealed record OrchestratorControlRequest(
|
||||
@@ -34,13 +34,18 @@ internal sealed class OrchestratorControlService : IOrchestratorControlService
|
||||
private readonly Counter<long> _controlUpdates;
|
||||
private readonly ObservableGauge<int> _throttleGauge;
|
||||
private readonly ObservableGauge<int> _pausedGauge;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
private readonly ConcurrentDictionary<string, OrchestratorControlState> _cache = new(StringComparer.Ordinal);
|
||||
|
||||
public OrchestratorControlService(IOrchestratorControlRepository repository, Meter meter)
|
||||
public OrchestratorControlService(
|
||||
IOrchestratorControlRepository repository,
|
||||
Meter meter,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_repository = repository;
|
||||
_meter = meter;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
_controlUpdates = meter.CreateCounter<long>("sbom_orchestrator_control_updates");
|
||||
_throttleGauge = meter.CreateObservableGauge("sbom_orchestrator_throttle_percent", ObserveThrottle);
|
||||
_pausedGauge = meter.CreateObservableGauge("sbom_orchestrator_paused", ObservePaused);
|
||||
@@ -66,7 +71,7 @@ internal sealed class OrchestratorControlService : IOrchestratorControlService
|
||||
Paused: request.Paused ?? current.Paused,
|
||||
ThrottlePercent: throttle,
|
||||
Backpressure: string.IsNullOrWhiteSpace(request.Backpressure) ? current.Backpressure : request.Backpressure!.Trim().ToLowerInvariant(),
|
||||
UpdatedAtUtc: DateTimeOffset.UtcNow);
|
||||
UpdatedAtUtc: _timeProvider.GetUtcNow());
|
||||
|
||||
await _repository.SetAsync(updated, cancellationToken);
|
||||
_cache[updated.TenantId] = updated;
|
||||
|
||||
@@ -30,15 +30,18 @@ public sealed class RegistrySourceService : IRegistrySourceService
|
||||
private readonly IRegistrySourceRepository _sourceRepository;
|
||||
private readonly IRegistrySourceRunRepository _runRepository;
|
||||
private readonly ILogger<RegistrySourceService> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public RegistrySourceService(
|
||||
IRegistrySourceRepository sourceRepository,
|
||||
IRegistrySourceRunRepository runRepository,
|
||||
ILogger<RegistrySourceService> logger)
|
||||
ILogger<RegistrySourceService> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_sourceRepository = sourceRepository;
|
||||
_runRepository = runRepository;
|
||||
_logger = logger;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -125,7 +128,7 @@ public sealed class RegistrySourceService : IRegistrySourceService
|
||||
if (request.Status.HasValue) source.Status = request.Status.Value;
|
||||
if (request.Tags is not null) source.Tags = request.Tags.ToList();
|
||||
|
||||
source.UpdatedAt = DateTimeOffset.UtcNow;
|
||||
source.UpdatedAt = _timeProvider.GetUtcNow();
|
||||
source.UpdatedBy = userId;
|
||||
|
||||
var updated = await _sourceRepository.UpdateAsync(source, cancellationToken);
|
||||
@@ -156,23 +159,23 @@ public sealed class RegistrySourceService : IRegistrySourceService
|
||||
var source = await _sourceRepository.GetByIdAsync(id, cancellationToken);
|
||||
if (source is null)
|
||||
{
|
||||
return new TestRegistrySourceResponse(id, false, "Registry source not found", null, TimeSpan.Zero, DateTimeOffset.UtcNow);
|
||||
return new TestRegistrySourceResponse(id, false, "Registry source not found", null, TimeSpan.Zero, _timeProvider.GetUtcNow());
|
||||
}
|
||||
|
||||
var startTime = DateTimeOffset.UtcNow;
|
||||
var startTime = _timeProvider.GetUtcNow();
|
||||
|
||||
// TODO: Implement actual registry connection test
|
||||
// For now, simulate a successful test
|
||||
await Task.Delay(100, cancellationToken);
|
||||
|
||||
var duration = DateTimeOffset.UtcNow - startTime;
|
||||
var duration = _timeProvider.GetUtcNow() - startTime;
|
||||
|
||||
// Update source status
|
||||
var newStatus = RegistrySourceStatus.Active;
|
||||
if (source.Status != newStatus)
|
||||
{
|
||||
source.Status = newStatus;
|
||||
source.UpdatedAt = DateTimeOffset.UtcNow;
|
||||
source.UpdatedAt = _timeProvider.GetUtcNow();
|
||||
await _sourceRepository.UpdateAsync(source, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -188,7 +191,7 @@ public sealed class RegistrySourceService : IRegistrySourceService
|
||||
["type"] = source.Type.ToString()
|
||||
},
|
||||
duration,
|
||||
DateTimeOffset.UtcNow);
|
||||
_timeProvider.GetUtcNow());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -209,7 +212,7 @@ public sealed class RegistrySourceService : IRegistrySourceService
|
||||
Status = RegistryRunStatus.Queued,
|
||||
TriggerType = triggerType,
|
||||
TriggerMetadata = triggerMetadata,
|
||||
StartedAt = DateTimeOffset.UtcNow
|
||||
StartedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
|
||||
var created = await _runRepository.CreateAsync(run, cancellationToken);
|
||||
@@ -229,7 +232,7 @@ public sealed class RegistrySourceService : IRegistrySourceService
|
||||
if (source is null) return null;
|
||||
|
||||
source.Status = RegistrySourceStatus.Paused;
|
||||
source.UpdatedAt = DateTimeOffset.UtcNow;
|
||||
source.UpdatedAt = _timeProvider.GetUtcNow();
|
||||
source.UpdatedBy = userId;
|
||||
|
||||
var updated = await _sourceRepository.UpdateAsync(source, cancellationToken);
|
||||
@@ -252,7 +255,7 @@ public sealed class RegistrySourceService : IRegistrySourceService
|
||||
}
|
||||
|
||||
source.Status = RegistrySourceStatus.Active;
|
||||
source.UpdatedAt = DateTimeOffset.UtcNow;
|
||||
source.UpdatedAt = _timeProvider.GetUtcNow();
|
||||
source.UpdatedBy = userId;
|
||||
|
||||
var updated = await _sourceRepository.UpdateAsync(source, cancellationToken);
|
||||
|
||||
@@ -74,6 +74,7 @@ internal sealed class ReplayVerificationService : IReplayVerificationService
|
||||
ExpectedHash = request.ReplayHash,
|
||||
ComputedHash = string.Empty,
|
||||
Status = ReplayVerificationStatus.InputsNotFound,
|
||||
VerifiedAt = _clock.UtcNow,
|
||||
Error = "Unable to determine verification inputs. Provide explicit inputs or ensure hash is stored."
|
||||
};
|
||||
}
|
||||
@@ -119,6 +120,7 @@ internal sealed class ReplayVerificationService : IReplayVerificationService
|
||||
ExpectedHash = request.ReplayHash,
|
||||
ComputedHash = string.Empty,
|
||||
Status = ReplayVerificationStatus.Error,
|
||||
VerifiedAt = _clock.UtcNow,
|
||||
Error = ex.Message
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,6 +16,12 @@ public interface IWatermarkService
|
||||
internal sealed class InMemoryWatermarkService : IWatermarkService
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, WatermarkState> _watermarks = new(StringComparer.Ordinal);
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public InMemoryWatermarkService(TimeProvider? timeProvider = null)
|
||||
{
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public Task<WatermarkState> GetAsync(string tenantId, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -24,14 +30,14 @@ internal sealed class InMemoryWatermarkService : IWatermarkService
|
||||
return Task.FromResult(state);
|
||||
}
|
||||
|
||||
var created = new WatermarkState(tenantId, string.Empty, DateTimeOffset.UtcNow);
|
||||
var created = new WatermarkState(tenantId, string.Empty, _timeProvider.GetUtcNow());
|
||||
_watermarks[tenantId] = created;
|
||||
return Task.FromResult(created);
|
||||
}
|
||||
|
||||
public Task<WatermarkState> SetAsync(string tenantId, string watermark, CancellationToken cancellationToken)
|
||||
{
|
||||
var state = new WatermarkState(tenantId, watermark, DateTimeOffset.UtcNow);
|
||||
var state = new WatermarkState(tenantId, watermark, _timeProvider.GetUtcNow());
|
||||
_watermarks[tenantId] = state;
|
||||
return Task.FromResult(state);
|
||||
}
|
||||
|
||||
@@ -19,5 +19,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="StellaOps.SbomService.Tests" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -13,12 +13,15 @@ public sealed class SbomLineageEdgeRepository : RepositoryBase<LineageDataSource
|
||||
private const string Schema = "sbom";
|
||||
private const string Table = "sbom_lineage_edges";
|
||||
private const string FullTable = $"{Schema}.{Table}";
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public SbomLineageEdgeRepository(
|
||||
LineageDataSource dataSource,
|
||||
ILogger<SbomLineageEdgeRepository> logger)
|
||||
ILogger<SbomLineageEdgeRepository> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
: base(dataSource, logger)
|
||||
{
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public async ValueTask<LineageGraph> GetGraphAsync(
|
||||
@@ -260,7 +263,7 @@ public sealed class SbomLineageEdgeRepository : RepositoryBase<LineageDataSource
|
||||
ArtifactDigest: artifactDigest,
|
||||
SbomVersionId: null,
|
||||
SequenceNumber: 0,
|
||||
CreatedAt: DateTimeOffset.UtcNow,
|
||||
CreatedAt: _timeProvider.GetUtcNow(),
|
||||
Metadata: null
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ public sealed class LineageGraphService : ILineageGraphService
|
||||
private readonly ISbomVerdictLinkRepository _verdictRepository;
|
||||
private readonly IDistributedCache? _cache;
|
||||
private readonly ILogger<LineageGraphService> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
private static readonly TimeSpan CacheExpiry = TimeSpan.FromMinutes(10);
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web);
|
||||
@@ -25,13 +26,15 @@ public sealed class LineageGraphService : ILineageGraphService
|
||||
IVexDeltaRepository deltaRepository,
|
||||
ISbomVerdictLinkRepository verdictRepository,
|
||||
ILogger<LineageGraphService> logger,
|
||||
IDistributedCache? cache = null)
|
||||
IDistributedCache? cache = null,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_edgeRepository = edgeRepository;
|
||||
_deltaRepository = deltaRepository;
|
||||
_verdictRepository = verdictRepository;
|
||||
_cache = cache;
|
||||
_logger = logger;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public async ValueTask<LineageGraphResponse> GetLineageAsync(
|
||||
@@ -184,7 +187,7 @@ public sealed class LineageGraphService : ILineageGraphService
|
||||
|
||||
// Placeholder implementation
|
||||
var downloadUrl = $"https://evidence.stellaops.example/exports/{Guid.NewGuid()}.tar.gz";
|
||||
var expiresAt = DateTimeOffset.UtcNow.AddHours(24);
|
||||
var expiresAt = _timeProvider.GetUtcNow().AddHours(24);
|
||||
|
||||
return new ExportResult(
|
||||
DownloadUrl: downloadUrl,
|
||||
|
||||
@@ -11,11 +11,16 @@ namespace StellaOps.SbomService.Persistence.Postgres.Repositories;
|
||||
/// </summary>
|
||||
public sealed class PostgresOrchestratorRepository : RepositoryBase<SbomServiceDataSource>, IOrchestratorRepository
|
||||
{
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private bool _tableInitialized;
|
||||
|
||||
public PostgresOrchestratorRepository(SbomServiceDataSource dataSource, ILogger<PostgresOrchestratorRepository> logger)
|
||||
public PostgresOrchestratorRepository(
|
||||
SbomServiceDataSource dataSource,
|
||||
ILogger<PostgresOrchestratorRepository> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
: base(dataSource, logger)
|
||||
{
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<OrchestratorSource>> ListAsync(string tenantId, CancellationToken cancellationToken)
|
||||
@@ -79,7 +84,7 @@ public sealed class PostgresOrchestratorRepository : RepositoryBase<SbomServiceD
|
||||
var count = Convert.ToInt32(await countCommand.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false));
|
||||
var sourceId = $"src-{count + 1:D3}";
|
||||
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var now = _timeProvider.GetUtcNow();
|
||||
|
||||
const string insertSql = @"
|
||||
INSERT INTO sbom.orchestrator_sources (tenant_id, source_id, artifact_digest, source_type, created_at, metadata)
|
||||
|
||||
@@ -78,9 +78,9 @@ public sealed record LineageEdge
|
||||
public required string TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the edge was created.
|
||||
/// When the edge was created. Must be explicitly set by calling code.
|
||||
/// </summary>
|
||||
public DateTimeOffset CreatedAt { get; init; } = DateTimeOffset.UtcNow;
|
||||
public required DateTimeOffset CreatedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional metadata about the relationship.
|
||||
|
||||
@@ -94,9 +94,9 @@ public sealed record SbomVerdictLink
|
||||
public required string TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the link was created.
|
||||
/// When the link was created. Must be explicitly set by calling code.
|
||||
/// </summary>
|
||||
public DateTimeOffset LinkedAt { get; init; } = DateTimeOffset.UtcNow;
|
||||
public required DateTimeOffset LinkedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// SBOM artifact digest for cross-reference.
|
||||
|
||||
Reference in New Issue
Block a user