feat: Implement Policy Engine Evaluation Service and Cache with unit tests
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

Temp commit to debug
This commit is contained in:
master
2025-11-05 07:35:53 +00:00
parent 40e7f827da
commit 9253620833
125 changed files with 18735 additions and 17215 deletions

View File

@@ -0,0 +1,95 @@
using System.Text.Json.Nodes;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using StellaOps.Findings.Ledger.Domain;
using StellaOps.Findings.Ledger.Options;
namespace StellaOps.Findings.Ledger.Infrastructure.Policy;
internal sealed record PolicyEvaluationCacheKey(string TenantId, string PolicyVersion, Guid EventId, string? ProjectionHash);
internal sealed class PolicyEvaluationCache : IDisposable
{
private readonly IMemoryCache _cache;
private readonly ILogger<PolicyEvaluationCache> _logger;
private bool _disposed;
public PolicyEvaluationCache(
LedgerServiceOptions.PolicyEngineOptions options,
ILogger<PolicyEvaluationCache> logger)
{
ArgumentNullException.ThrowIfNull(options);
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_cache = new MemoryCache(new MemoryCacheOptions
{
SizeLimit = options.Cache.SizeLimit
});
EntryLifetime = options.Cache.EntryLifetime;
}
public TimeSpan EntryLifetime { get; }
public bool TryGet(PolicyEvaluationCacheKey key, out PolicyEvaluationResult result)
{
ArgumentNullException.ThrowIfNull(key);
if (_cache.TryGetValue(key, out PolicyEvaluationResult? cached) && cached is not null)
{
_logger.LogTrace("Policy evaluation cache hit for tenant {Tenant} finding {Finding} policy {Policy}", key.TenantId, key.EventId, key.PolicyVersion);
result = Clone(cached);
return true;
}
result = null!;
return false;
}
public void Set(PolicyEvaluationCacheKey key, PolicyEvaluationResult value)
{
ArgumentNullException.ThrowIfNull(key);
ArgumentNullException.ThrowIfNull(value);
var entryOptions = new MemoryCacheEntryOptions()
.SetSize(1)
.SetAbsoluteExpiration(EntryLifetime);
_cache.Set(key, Clone(value), entryOptions);
}
private static PolicyEvaluationResult Clone(PolicyEvaluationResult result)
{
var labelsClone = result.Labels is null ? new JsonObject() : (JsonObject)result.Labels.DeepClone();
var rationaleClone = result.Rationale is null ? new JsonArray() : CloneArray(result.Rationale);
return new PolicyEvaluationResult(
result.Status,
result.Severity,
labelsClone,
result.ExplainRef,
rationaleClone);
}
private static JsonArray CloneArray(JsonArray source)
{
var clone = new JsonArray();
foreach (var item in source)
{
clone.Add(item?.DeepClone());
}
return clone;
}
public void Dispose()
{
if (_disposed)
{
return;
}
_cache.Dispose();
_disposed = true;
}
}