feat: Implement Policy Engine Evaluation Service and Cache with unit tests
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Temp commit to debug
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user