blockers 2
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-11-23 14:54:17 +02:00
parent f47d2d1377
commit cce96f3596
100 changed files with 2758 additions and 1912 deletions

View File

@@ -0,0 +1,37 @@
using System.Collections.Generic;
namespace StellaOps.Concelier.WebService.Contracts;
public sealed record AdvisorySummaryResponse(
AdvisorySummaryMeta Meta,
IReadOnlyList<AdvisorySummaryItem> Items);
public sealed record AdvisorySummaryMeta(
string Tenant,
int Count,
string? Next,
string Sort);
public sealed record AdvisorySummaryItem(
string AdvisoryKey,
string Source,
string? LinksetId,
double? Confidence,
IReadOnlyList<AdvisorySummaryConflict>? Conflicts,
AdvisorySummaryCounts Counts,
AdvisorySummaryProvenance Provenance,
IReadOnlyList<string> Aliases,
string? ObservedAt);
public sealed record AdvisorySummaryConflict(
string Field,
string Reason,
IReadOnlyList<string>? SourceIds);
public sealed record AdvisorySummaryCounts(
int Observations,
int ConflictFields);
public sealed record AdvisorySummaryProvenance(
IReadOnlyList<string>? ObservationIds,
string? Schema);

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using StellaOps.Concelier.Core.Linksets;
using StellaOps.Concelier.WebService.Contracts;
namespace StellaOps.Concelier.WebService.Extensions;
internal static class AdvisorySummaryMapper
{
public static AdvisorySummaryItem ToSummary(AdvisoryLinkset linkset)
{
ArgumentNullException.ThrowIfNull(linkset);
var aliases = linkset.Normalized?.Purls ?? Array.Empty<string>();
var conflictFields = linkset.Conflicts?.Select(c => c.Field).Distinct(StringComparer.Ordinal).Count() ?? 0;
var conflicts = linkset.Conflicts?.Select(c => new AdvisorySummaryConflict(
c.Field,
c.Reason,
c.SourceIds?.ToArray()
)).ToArray();
return new AdvisorySummaryItem(
AdvisoryKey: linkset.AdvisoryId,
Source: linkset.Source,
LinksetId: linkset.BuiltByJobId,
Confidence: linkset.Confidence,
Conflicts: conflicts,
Counts: new AdvisorySummaryCounts(
Observations: linkset.ObservationIds.Length,
ConflictFields: conflictFields),
Provenance: new AdvisorySummaryProvenance(
ObservationIds: linkset.ObservationIds.ToArray(),
Schema: "lnm-1.0"),
Aliases: aliases.ToArray(),
ObservedAt: linkset.CreatedAt.UtcDateTime.ToString("O"));
}
public static AdvisorySummaryResponse ToResponse(
string tenant,
IReadOnlyList<AdvisorySummaryItem> items,
string? nextCursor,
string sort)
{
return new AdvisorySummaryResponse(
new AdvisorySummaryMeta(
Tenant: tenant,
Count: items.Count,
Next: nextCursor,
Sort: sort),
items);
}
}

View File

@@ -1,47 +1,49 @@
using System.Diagnostics.Metrics;
using System.Collections.Generic;
namespace StellaOps.Concelier.WebService.Telemetry;
internal sealed class LinksetCacheTelemetry
{
private static readonly Meter Meter = new("StellaOps.Concelier.Linksets");
private readonly Counter<long> _hitTotal;
private readonly Counter<long> _writeTotal;
private readonly Histogram<double> _rebuildMs;
public LinksetCacheTelemetry(IMeterFactory meterFactory)
public LinksetCacheTelemetry()
{
var meter = meterFactory.Create("StellaOps.Concelier.Linksets");
_hitTotal = meter.CreateCounter<long>("lnm.cache.hit_total", unit: "hit", description: "Cache hits for LNM linksets");
_writeTotal = meter.CreateCounter<long>("lnm.cache.write_total", unit: "write", description: "Cache writes for LNM linksets");
_rebuildMs = meter.CreateHistogram<double>("lnm.cache.rebuild_ms", unit: "ms", description: "Synchronous rebuild latency for LNM cache");
_hitTotal = Meter.CreateCounter<long>("lnm.cache.hit_total", unit: "hit", description: "Cache hits for LNM linksets");
_writeTotal = Meter.CreateCounter<long>("lnm.cache.write_total", unit: "write", description: "Cache writes for LNM linksets");
_rebuildMs = Meter.CreateHistogram<double>("lnm.cache.rebuild_ms", unit: "ms", description: "Synchronous rebuild latency for LNM cache");
}
public void RecordHit(string? tenant, string source)
{
var tags = new TagList
var tags = new KeyValuePair<string, object?>[]
{
{ "tenant", tenant ?? string.Empty },
{ "source", source }
new("tenant", tenant ?? string.Empty),
new("source", source)
};
_hitTotal.Add(1, tags);
}
public void RecordWrite(string? tenant, string source)
{
var tags = new TagList
var tags = new KeyValuePair<string, object?>[]
{
{ "tenant", tenant ?? string.Empty },
{ "source", source }
new("tenant", tenant ?? string.Empty),
new("source", source)
};
_writeTotal.Add(1, tags);
}
public void RecordRebuild(string? tenant, string source, double elapsedMs)
{
var tags = new TagList
var tags = new KeyValuePair<string, object?>[]
{
{ "tenant", tenant ?? string.Empty },
{ "source", source }
new("tenant", tenant ?? string.Empty),
new("source", source)
};
_rebuildMs.Record(elapsedMs, tags);
}