sprints work
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
// <copyright file="InMemoryEvidencePackStore.cs" company="StellaOps">
|
||||
// Copyright (c) StellaOps. Licensed under the AGPL-3.0-or-later.
|
||||
// </copyright>
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using StellaOps.Evidence.Pack.Models;
|
||||
|
||||
namespace StellaOps.Evidence.Pack.Storage;
|
||||
|
||||
/// <summary>
|
||||
/// In-memory implementation of <see cref="IEvidencePackStore"/> for testing.
|
||||
/// Sprint: SPRINT_20260109_011_005 Task: EVPK-005
|
||||
/// </summary>
|
||||
public sealed class InMemoryEvidencePackStore : IEvidencePackStore
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, EvidencePack> _packs = new();
|
||||
private readonly ConcurrentDictionary<string, SignedEvidencePack> _signedPacks = new();
|
||||
private readonly ConcurrentDictionary<string, List<string>> _runLinks = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task SaveAsync(EvidencePack pack, CancellationToken cancellationToken)
|
||||
{
|
||||
_packs[pack.PackId] = pack;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task SaveSignedAsync(SignedEvidencePack signedPack, CancellationToken cancellationToken)
|
||||
{
|
||||
_signedPacks[signedPack.Pack.PackId] = signedPack;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<EvidencePack?> GetByIdAsync(string tenantId, string packId, CancellationToken cancellationToken)
|
||||
{
|
||||
// Wildcard tenant search
|
||||
if (tenantId == "*")
|
||||
{
|
||||
return Task.FromResult(_packs.TryGetValue(packId, out var pack) ? pack : null);
|
||||
}
|
||||
|
||||
if (_packs.TryGetValue(packId, out var p) && p.TenantId == tenantId)
|
||||
{
|
||||
return Task.FromResult<EvidencePack?>(p);
|
||||
}
|
||||
|
||||
return Task.FromResult<EvidencePack?>(null);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<SignedEvidencePack?> GetSignedByIdAsync(string tenantId, string packId, CancellationToken cancellationToken)
|
||||
{
|
||||
// Wildcard tenant search
|
||||
if (tenantId == "*")
|
||||
{
|
||||
return Task.FromResult(_signedPacks.TryGetValue(packId, out var pack) ? pack : null);
|
||||
}
|
||||
|
||||
if (_signedPacks.TryGetValue(packId, out var sp) && sp.Pack.TenantId == tenantId)
|
||||
{
|
||||
return Task.FromResult<SignedEvidencePack?>(sp);
|
||||
}
|
||||
|
||||
return Task.FromResult<SignedEvidencePack?>(null);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<IReadOnlyList<EvidencePack>> ListAsync(
|
||||
string tenantId,
|
||||
EvidencePackQuery? query,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var results = _packs.Values
|
||||
.Where(p => p.TenantId == tenantId);
|
||||
|
||||
if (query is not null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(query.CveId))
|
||||
{
|
||||
results = results.Where(p => p.Subject.CveId == query.CveId);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(query.Component))
|
||||
{
|
||||
results = results.Where(p => p.Subject.Component == query.Component);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(query.RunId))
|
||||
{
|
||||
var packIds = _runLinks.TryGetValue(query.RunId, out var ids)
|
||||
? ids.ToHashSet()
|
||||
: new HashSet<string>();
|
||||
results = results.Where(p => packIds.Contains(p.PackId));
|
||||
}
|
||||
|
||||
if (query.Since.HasValue)
|
||||
{
|
||||
results = results.Where(p => p.CreatedAt >= query.Since.Value);
|
||||
}
|
||||
|
||||
results = results.Take(query.Limit);
|
||||
}
|
||||
|
||||
return Task.FromResult<IReadOnlyList<EvidencePack>>(
|
||||
results.OrderByDescending(p => p.CreatedAt).ToList());
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task LinkToRunAsync(string packId, string runId, CancellationToken cancellationToken)
|
||||
{
|
||||
var links = _runLinks.GetOrAdd(runId, _ => new List<string>());
|
||||
lock (links)
|
||||
{
|
||||
if (!links.Contains(packId))
|
||||
{
|
||||
links.Add(packId);
|
||||
}
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<IReadOnlyList<EvidencePack>> GetByRunIdAsync(string runId, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!_runLinks.TryGetValue(runId, out var packIds))
|
||||
{
|
||||
return Task.FromResult<IReadOnlyList<EvidencePack>>(Array.Empty<EvidencePack>());
|
||||
}
|
||||
|
||||
var packs = packIds
|
||||
.Select(id => _packs.TryGetValue(id, out var pack) ? pack : null)
|
||||
.Where(p => p is not null)
|
||||
.Cast<EvidencePack>()
|
||||
.OrderByDescending(p => p.CreatedAt)
|
||||
.ToList();
|
||||
|
||||
return Task.FromResult<IReadOnlyList<EvidencePack>>(packs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all stored data (for testing).
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
_packs.Clear();
|
||||
_signedPacks.Clear();
|
||||
_runLinks.Clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user