using System; using System.Collections.Generic; namespace StellaOps.Platform.Analytics.Options; public sealed class AnalyticsIngestionOptions { public const string SectionName = "Platform:AnalyticsIngestion"; public bool Enabled { get; set; } = true; public string? PostgresConnectionString { get; set; } public string SchemaVersion { get; set; } = "1.0.0"; public string IngestVersion { get; set; } = "1.0.0"; public AnalyticsStreamOptions Streams { get; set; } = new(); public AnalyticsCasOptions Cas { get; set; } = new(); public AnalyticsAttestationOptions Attestations { get; set; } = new(); public List AllowedTenants { get; set; } = new(); public void Normalize() { SchemaVersion = SchemaVersion?.Trim() ?? "1.0.0"; IngestVersion = IngestVersion?.Trim() ?? "1.0.0"; PostgresConnectionString = string.IsNullOrWhiteSpace(PostgresConnectionString) ? null : PostgresConnectionString.Trim(); Streams ??= new AnalyticsStreamOptions(); Cas ??= new AnalyticsCasOptions(); Attestations ??= new AnalyticsAttestationOptions(); AllowedTenants ??= new List(); Streams.Normalize(); Cas.Normalize(); Attestations.Normalize(); AllowedTenants = AllowedTenants .Where(value => !string.IsNullOrWhiteSpace(value)) .Select(value => value.Trim()) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); } public void Validate() { if (Enabled && string.IsNullOrWhiteSpace(PostgresConnectionString)) { throw new InvalidOperationException( "Analytics ingestion requires a Postgres connection string."); } } } public sealed class AnalyticsStreamOptions { public string ScannerStream { get; set; } = "orchestrator:events"; public string ConcelierObservationStream { get; set; } = "concelier:advisory.observation.updated:v1"; public string ConcelierLinksetStream { get; set; } = "concelier:advisory.linkset.updated:v1"; public string AttestorStream { get; set; } = "attestor:events"; public bool StartFromBeginning { get; set; } = false; public void Normalize() { ScannerStream = NormalizeName(ScannerStream); ConcelierObservationStream = NormalizeName(ConcelierObservationStream); ConcelierLinksetStream = NormalizeName(ConcelierLinksetStream); AttestorStream = NormalizeName(AttestorStream); } private static string NormalizeName(string value) => string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim(); } public sealed class AnalyticsCasOptions { public string? RootPath { get; set; } public string? DefaultBucket { get; set; } public void Normalize() { RootPath = string.IsNullOrWhiteSpace(RootPath) ? null : RootPath.Trim(); DefaultBucket = string.IsNullOrWhiteSpace(DefaultBucket) ? null : DefaultBucket.Trim(); } } public sealed class AnalyticsAttestationOptions { public string BundleUriTemplate { get; set; } = "bundle:{digest}"; public void Normalize() { BundleUriTemplate = string.IsNullOrWhiteSpace(BundleUriTemplate) ? "bundle:{digest}" : BundleUriTemplate.Trim(); } }