consolidation of some of the modules, localization fixes, product advisories work, qa work
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
namespace StellaOps.Symbols.Marketplace.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A catalog entry representing an installable symbol/debug pack.
|
||||
/// </summary>
|
||||
public sealed record SymbolPackCatalogEntry
|
||||
{
|
||||
public Guid Id { get; init; }
|
||||
public Guid SourceId { get; init; }
|
||||
public string PackId { get; init; } = string.Empty;
|
||||
public string Platform { get; init; } = string.Empty;
|
||||
public string[] Components { get; init; } = [];
|
||||
public string DsseDigest { get; init; } = string.Empty;
|
||||
public string Version { get; init; } = string.Empty;
|
||||
public long SizeBytes { get; init; }
|
||||
public bool Installed { get; init; }
|
||||
public DateTimeOffset PublishedAt { get; init; }
|
||||
public DateTimeOffset? InstalledAt { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace StellaOps.Symbols.Marketplace.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Registry of symbol providers (vendor/distro/community/partner).
|
||||
/// </summary>
|
||||
public sealed record SymbolPackSource
|
||||
{
|
||||
public Guid Id { get; init; }
|
||||
public string Key { get; init; } = string.Empty;
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public string SourceType { get; init; } = string.Empty;
|
||||
public string? Url { get; init; }
|
||||
public int Priority { get; init; }
|
||||
public bool Enabled { get; init; } = true;
|
||||
public int FreshnessSlaSeconds { get; init; } = 21600;
|
||||
public decimal WarningRatio { get; init; } = 0.80m;
|
||||
public DateTimeOffset CreatedAt { get; init; }
|
||||
public DateTimeOffset? UpdatedAt { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace StellaOps.Symbols.Marketplace.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Freshness projection for a symbol source, mirroring the AdvisorySourceFreshnessRecord pattern.
|
||||
/// </summary>
|
||||
public sealed record SymbolSourceFreshnessRecord(
|
||||
Guid SourceId,
|
||||
string SourceKey,
|
||||
string SourceName,
|
||||
string SourceType,
|
||||
string? SourceUrl,
|
||||
int Priority,
|
||||
bool Enabled,
|
||||
DateTimeOffset? LastSyncAt,
|
||||
DateTimeOffset? LastSuccessAt,
|
||||
string? LastError,
|
||||
long SyncCount,
|
||||
int ErrorCount,
|
||||
int FreshnessSlaSeconds,
|
||||
decimal WarningRatio,
|
||||
long FreshnessAgeSeconds,
|
||||
string FreshnessStatus,
|
||||
string SignatureStatus,
|
||||
long TotalPacks,
|
||||
long SignedPacks,
|
||||
long UnsignedPacks,
|
||||
long SignatureFailureCount);
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace StellaOps.Symbols.Marketplace.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Four-dimension trust score for a symbol source.
|
||||
/// Each dimension is 0.0 to 1.0; Overall is a weighted average.
|
||||
/// </summary>
|
||||
public sealed record SymbolSourceTrustScore(
|
||||
double Freshness,
|
||||
double Signature,
|
||||
double Coverage,
|
||||
double SlCompliance,
|
||||
double Overall);
|
||||
@@ -0,0 +1,34 @@
|
||||
using StellaOps.Symbols.Marketplace.Models;
|
||||
|
||||
namespace StellaOps.Symbols.Marketplace.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Repository for marketplace catalog operations (browse, install, uninstall).
|
||||
/// </summary>
|
||||
public interface IMarketplaceCatalogRepository
|
||||
{
|
||||
Task<IReadOnlyList<SymbolPackCatalogEntry>> ListCatalogAsync(
|
||||
Guid? sourceId,
|
||||
string? search,
|
||||
int limit,
|
||||
int offset,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<SymbolPackCatalogEntry?> GetCatalogEntryAsync(
|
||||
Guid entryId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task InstallPackAsync(
|
||||
Guid entryId,
|
||||
string tenantId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task UninstallPackAsync(
|
||||
Guid entryId,
|
||||
string tenantId,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<IReadOnlyList<SymbolPackCatalogEntry>> ListInstalledAsync(
|
||||
string tenantId,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using StellaOps.Symbols.Marketplace.Models;
|
||||
|
||||
namespace StellaOps.Symbols.Marketplace.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Read repository for symbol sources and their freshness projections.
|
||||
/// </summary>
|
||||
public interface ISymbolSourceReadRepository
|
||||
{
|
||||
Task<IReadOnlyList<SymbolSourceFreshnessRecord>> ListSourcesAsync(
|
||||
bool includeDisabled,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<SymbolSourceFreshnessRecord?> GetSourceByIdAsync(
|
||||
Guid sourceId,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
using StellaOps.Symbols.Marketplace.Models;
|
||||
|
||||
namespace StellaOps.Symbols.Marketplace.Scoring;
|
||||
|
||||
/// <summary>
|
||||
/// Default trust scorer using weighted dimensions:
|
||||
/// Freshness=0.3, Signature=0.3, Coverage=0.2, SLA=0.2.
|
||||
/// </summary>
|
||||
public sealed class DefaultSymbolSourceTrustScorer : ISymbolSourceTrustScorer
|
||||
{
|
||||
private const double WeightFreshness = 0.3;
|
||||
private const double WeightSignature = 0.3;
|
||||
private const double WeightCoverage = 0.2;
|
||||
private const double WeightSla = 0.2;
|
||||
|
||||
public SymbolSourceTrustScore CalculateTrust(SymbolSourceFreshnessRecord source)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
|
||||
var freshness = ComputeFreshnessDimension(source);
|
||||
var signature = ComputeSignatureDimension(source);
|
||||
var coverage = ComputeCoverageDimension(source);
|
||||
var slCompliance = ComputeSlaDimension(source);
|
||||
|
||||
var overall =
|
||||
(freshness * WeightFreshness) +
|
||||
(signature * WeightSignature) +
|
||||
(coverage * WeightCoverage) +
|
||||
(slCompliance * WeightSla);
|
||||
|
||||
return new SymbolSourceTrustScore(
|
||||
Freshness: Clamp(freshness),
|
||||
Signature: Clamp(signature),
|
||||
Coverage: Clamp(coverage),
|
||||
SlCompliance: Clamp(slCompliance),
|
||||
Overall: Clamp(overall));
|
||||
}
|
||||
|
||||
private static double ComputeFreshnessDimension(SymbolSourceFreshnessRecord source)
|
||||
{
|
||||
if (source.FreshnessSlaSeconds <= 0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return source.FreshnessStatus switch
|
||||
{
|
||||
"healthy" => 1.0,
|
||||
"warning" => 1.0 - ((double)source.FreshnessAgeSeconds / source.FreshnessSlaSeconds),
|
||||
"stale" => Math.Max(0.0, 0.3 - (0.1 * ((double)source.FreshnessAgeSeconds / source.FreshnessSlaSeconds - 1.0))),
|
||||
_ => 0.0 // unavailable
|
||||
};
|
||||
}
|
||||
|
||||
private static double ComputeSignatureDimension(SymbolSourceFreshnessRecord source)
|
||||
{
|
||||
if (source.TotalPacks <= 0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return (double)source.SignedPacks / source.TotalPacks;
|
||||
}
|
||||
|
||||
private static double ComputeCoverageDimension(SymbolSourceFreshnessRecord source)
|
||||
{
|
||||
// Coverage is derived from the presence of packs relative to sync activity.
|
||||
// A source with packs and no errors has full coverage potential.
|
||||
if (source.SyncCount <= 0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if (source.TotalPacks <= 0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
var errorRate = source.SyncCount > 0
|
||||
? (double)source.ErrorCount / source.SyncCount
|
||||
: 0.0;
|
||||
|
||||
return Math.Max(0.0, 1.0 - errorRate);
|
||||
}
|
||||
|
||||
private static double ComputeSlaDimension(SymbolSourceFreshnessRecord source)
|
||||
{
|
||||
if (source.FreshnessSlaSeconds <= 0)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// SLA compliance is based on whether the source stays within its freshness window.
|
||||
if (source.FreshnessAgeSeconds <= source.FreshnessSlaSeconds)
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
// Degrade linearly up to 2x the SLA window, then zero.
|
||||
var overage = (double)(source.FreshnessAgeSeconds - source.FreshnessSlaSeconds) / source.FreshnessSlaSeconds;
|
||||
return Math.Max(0.0, 1.0 - overage);
|
||||
}
|
||||
|
||||
private static double Clamp(double value) => Math.Clamp(value, 0.0, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using StellaOps.Symbols.Marketplace.Models;
|
||||
|
||||
namespace StellaOps.Symbols.Marketplace.Scoring;
|
||||
|
||||
/// <summary>
|
||||
/// Computes a trust score for a symbol source based on freshness, signature coverage,
|
||||
/// artifact coverage, and SLA compliance.
|
||||
/// </summary>
|
||||
public interface ISymbolSourceTrustScorer
|
||||
{
|
||||
SymbolSourceTrustScore CalculateTrust(SymbolSourceFreshnessRecord source);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user