consolidation of some of the modules, localization fixes, product advisories work, qa work

This commit is contained in:
master
2026-03-05 03:54:22 +02:00
parent 7bafcc3eef
commit 8e1cb9448d
3878 changed files with 72600 additions and 46861 deletions

View File

@@ -0,0 +1,6 @@
namespace StellaOps.RiskEngine.Infrastructure;
public class Class1
{
}

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" ?>
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\..\StellaOps.RiskEngine.Core\StellaOps.RiskEngine.Core.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Npgsql" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,32 @@
using StellaOps.RiskEngine.Core.Contracts;
using StellaOps.RiskEngine.Core.Services;
using System.Collections.Concurrent;
namespace StellaOps.RiskEngine.Infrastructure.Stores;
/// <summary>
/// Deterministic in-memory store for risk score results.
/// Used for offline/ephemeral runs and testing until ledger integration lands.
/// </summary>
public sealed class InMemoryRiskScoreResultStore : IRiskScoreResultStore
{
private readonly ConcurrentDictionary<Guid, RiskScoreResult> results = new();
private readonly ConcurrentQueue<Guid> order = new();
public Task SaveAsync(RiskScoreResult result, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (results.TryAdd(result.JobId, result))
{
order.Enqueue(result.JobId);
}
return Task.CompletedTask;
}
public IReadOnlyList<RiskScoreResult> Snapshot() =>
order.Select(id => results[id]).ToArray();
public bool TryGet(Guid jobId, out RiskScoreResult result) =>
results.TryGetValue(jobId, out result!);
}

View File

@@ -0,0 +1,195 @@
using Npgsql;
using NpgsqlTypes;
using StellaOps.RiskEngine.Core.Contracts;
using StellaOps.RiskEngine.Core.Services;
using System.Text.Json;
namespace StellaOps.RiskEngine.Infrastructure.Stores;
/// <summary>
/// PostgreSQL-backed risk score result store for durable production retrieval.
/// </summary>
public sealed class PostgresRiskScoreResultStore : IRiskScoreResultStore, IAsyncDisposable
{
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
private readonly NpgsqlDataSource _dataSource;
private readonly object _initGate = new();
private bool _tableInitialized;
public PostgresRiskScoreResultStore(string connectionString)
{
ArgumentException.ThrowIfNullOrWhiteSpace(connectionString);
_dataSource = NpgsqlDataSource.Create(connectionString);
}
public async Task SaveAsync(RiskScoreResult result, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
await EnsureTableAsync(cancellationToken).ConfigureAwait(false);
const string sql = """
INSERT INTO riskengine.risk_score_results (
job_id,
provider,
subject,
score,
success,
error,
signals,
completed_at
) VALUES (
@job_id,
@provider,
@subject,
@score,
@success,
@error,
@signals,
@completed_at
)
ON CONFLICT (job_id) DO UPDATE SET
provider = EXCLUDED.provider,
subject = EXCLUDED.subject,
score = EXCLUDED.score,
success = EXCLUDED.success,
error = EXCLUDED.error,
signals = EXCLUDED.signals,
completed_at = EXCLUDED.completed_at;
""";
await using var connection = await _dataSource.OpenConnectionAsync(cancellationToken).ConfigureAwait(false);
await using var command = new NpgsqlCommand(sql, connection);
command.Parameters.AddWithValue("job_id", result.JobId);
command.Parameters.AddWithValue("provider", result.Provider);
command.Parameters.AddWithValue("subject", result.Subject);
command.Parameters.AddWithValue("score", result.Score);
command.Parameters.AddWithValue("success", result.Success);
command.Parameters.AddWithValue("error", (object?)result.Error ?? DBNull.Value);
command.Parameters.Add("signals", NpgsqlDbType.Jsonb).Value = JsonSerializer.Serialize(result.Signals, JsonOptions);
command.Parameters.AddWithValue("completed_at", result.CompletedAtUtc);
await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
}
public bool TryGet(Guid jobId, out RiskScoreResult result)
{
EnsureTable();
const string sql = """
SELECT provider, subject, score, success, error, signals, completed_at
FROM riskengine.risk_score_results
WHERE job_id = @job_id;
""";
using var connection = _dataSource.OpenConnection();
using var command = new NpgsqlCommand(sql, connection);
command.Parameters.AddWithValue("job_id", jobId);
using var reader = command.ExecuteReader();
if (!reader.Read())
{
result = default!;
return false;
}
var provider = reader.GetString(0);
var subject = reader.GetString(1);
var score = reader.GetDouble(2);
var success = reader.GetBoolean(3);
var error = reader.IsDBNull(4) ? null : reader.GetString(4);
var signalsJson = reader.GetString(5);
var completedAt = reader.GetFieldValue<DateTimeOffset>(6);
var signals = JsonSerializer.Deserialize<Dictionary<string, double>>(signalsJson, JsonOptions)
?? new Dictionary<string, double>(StringComparer.Ordinal);
result = new RiskScoreResult(
jobId,
provider,
subject,
score,
success,
error,
signals,
completedAt);
return true;
}
public ValueTask DisposeAsync()
{
return _dataSource.DisposeAsync();
}
private async Task EnsureTableAsync(CancellationToken cancellationToken)
{
lock (_initGate)
{
if (_tableInitialized)
{
return;
}
}
const string ddl = """
CREATE SCHEMA IF NOT EXISTS riskengine;
CREATE TABLE IF NOT EXISTS riskengine.risk_score_results (
job_id UUID PRIMARY KEY,
provider TEXT NOT NULL,
subject TEXT NOT NULL,
score DOUBLE PRECISION NOT NULL,
success BOOLEAN NOT NULL,
error TEXT NULL,
signals JSONB NOT NULL,
completed_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_risk_score_results_completed_at
ON riskengine.risk_score_results (completed_at DESC);
""";
await using var connection = await _dataSource.OpenConnectionAsync(cancellationToken).ConfigureAwait(false);
await using var command = new NpgsqlCommand(ddl, connection);
await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
lock (_initGate)
{
_tableInitialized = true;
}
}
private void EnsureTable()
{
lock (_initGate)
{
if (_tableInitialized)
{
return;
}
}
const string ddl = """
CREATE SCHEMA IF NOT EXISTS riskengine;
CREATE TABLE IF NOT EXISTS riskengine.risk_score_results (
job_id UUID PRIMARY KEY,
provider TEXT NOT NULL,
subject TEXT NOT NULL,
score DOUBLE PRECISION NOT NULL,
success BOOLEAN NOT NULL,
error TEXT NULL,
signals JSONB NOT NULL,
completed_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_risk_score_results_completed_at
ON riskengine.risk_score_results (completed_at DESC);
""";
using var connection = _dataSource.OpenConnection();
using var command = new NpgsqlCommand(ddl, connection);
command.ExecuteNonQuery();
lock (_initGate)
{
_tableInitialized = true;
}
}
}

View File

@@ -0,0 +1,9 @@
# StellaOps.RiskEngine.Infrastructure Task Board
This board mirrors active sprint tasks for this module.
Source of truth: `docs/implplan/SPRINT_20260130_002_Tools_csproj_remediation_solid_review.md`.
| Task ID | Status | Notes |
| --- | --- | --- |
| REMED-05 | TODO | Remediation checklist: docs/implplan/audits/csproj-standards/remediation/checklists/src/RiskEngine/StellaOps.RiskEngine/StellaOps.RiskEngine.Infrastructure/StellaOps.RiskEngine.Infrastructure.md. |
| REMED-06 | DONE | SOLID review notes captured for SPRINT_20260130_002. |
| SPRINT-312-005 | DONE | Added `PostgresRiskScoreResultStore` with schema/bootstrap and deterministic upsert/read behavior. |